Mailpit's Open Door: A Tale of SSRF in Dev Tools
Jan 7, 2026·6 min read
Executive Summary (TL;DR)
Mailpit, a popular email testing tool, included a `/proxy` endpoint to help render email assets. Unfortunately, it verified nothing. Attackers can abuse this to make the Mailpit server send HTTP GET requests to any destination, including `localhost` and internal network ranges. The fix forces the proxy to validate that the requested URL actually exists inside a specific email message.
A Server-Side Request Forgery (SSRF) vulnerability in Mailpit's `/proxy` endpoint allows unauthenticated attackers to use the Mailpit server as an arbitrary HTTP proxy. This facilitates internal network reconnaissance, access to local APIs, and potential cloud metadata exfiltration.
The Hook: The Helper That Helped Too Much
Mailpit is the spiritual successor to MailHog—a tool every developer loves because it captures SMTP traffic and displays it in a web UI, preventing you from accidentally emailing your entire customer database during testing. To make those captured emails look pretty, Mailpit renders HTML bodies. But there's a catch: modern browsers implement strict security policies (CORS, Mixed Content) that often block local web apps from loading images or fonts hosted on external servers.
To bypass this annoyance, Mailpit implemented a /proxy endpoint. The logic is simple: "The browser can't fetch this image, so I'll fetch it for you." The frontend asks the backend to grab a resource, and the backend relays it. Ideally, this should only fetch harmless cat JPEGs from public CDNs.
In reality, this feature was implemented with the trusting optimism of a golden retriever. It didn't care what you asked it to fetch, or where it lived. It just fetched it. This turned a helpful rendering utility into a wide-open gateway for attackers to pivot into the internal network.
The Flaw: A Proxy Without Boundaries
The vulnerability lies in how the server/handlers/proxy.go handler processed requests. It accepted a raw url query parameter and passed it directly to Go's HTTP client. There were no checks to ensure the destination was external, no checks to see if the URL was actually related to an email, and crucially, no validation of the response content type.
This is a classic Server-Side Request Forgery (SSRF). By supplying a URL like http://127.0.0.1:8025/api/v1/info, an attacker forces the Mailpit server to talk to itself. Since the request originates from the server (loopback), it bypasses network firewalls that would normally block external access to internal services.
The flaw wasn't just about where it could connect, but what it would return. Because it didn't filter the response, it would happily relay JSON, XML, or plain text configuration files back to the attacker, treating them as if they were just image data.
The Code: Trust But Don't Verify
Let's look at the "smoking gun" code. The pre-patch implementation was shockingly simple—too simple. It essentially acted as a pipe between the user and any HTTP endpoint.
Vulnerable Code (Simplified):
// The classic mistake: Taking user input and using it directly in a request
func Proxy(w http.ResponseWriter, r *http.Request) {
// 1. Get the URL from the query string
targetUrl := r.URL.Query().Get("url")
// 2. Create a request (No validation on IP/Host)
req, _ := http.NewRequest("GET", targetUrl, nil)
// 3. Fire away!
resp, _ := httpClient.Do(req)
// 4. Stream response back to user
io.Copy(w, resp.Body)
}The Fix (Commit 3b9b470):
The patch introduces a concept of "context-aware" proxying. You can no longer just ask for a URL. You must provide a specific messageID and the URL. The server then parses that specific email to confirm the URL is actually present in the email body.
// The mitigation: Verify the asset belongs to the email
func Proxy(w http.ResponseWriter, r *http.Request) {
// 1. Decode payload containing MessageID + URL
data := decode(r.URL.Query().Get("data"))
// 2. Load the actual email message
msg := storage.GetMessage(data.MessageID)
// 3. HTML Parse the email body to verify the URL exists inside it
if !containsAsset(msg.HTML, data.TargetURL) {
http.Error(w, "Forbidden", 403)
return
}
// 4. Validate Content-Type (Only allow images, fonts, css)
// Blocks JSON/XML responses used in cloud metadata attacks
if !isSafeContentType(resp.Header.Get("Content-Type")) {
return
}
}This completely neutralizes the attack. An attacker can't force the server to visit an arbitrary internal URL because that URL won't exist inside a stored email message.
The Exploit: Knocking on Internal Doors
Exploiting this is trivial. Since Mailpit is often deployed in development environments (sometimes exposed to the internet, sometimes just accessible via corporate VPNs), it becomes a perfect reconnaissance tool.
Scenario 1: Cloud Metadata Exfiltration If Mailpit is running on an AWS EC2 instance, an attacker can steal the IAM role credentials attached to the instance.
# Ask Mailpit to fetch the AWS metadata service
curl "http://mailpit.corp/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"Scenario 2: Internal API Recon
Mailpit itself has an API. We can use the SSRF to query Mailpit's own internal info endpoints, or scan for other services on localhost.
# Probing for local services
curl "http://mailpit.corp/proxy?url=http://127.0.0.1:8025/api/v1/info"The response would dump the JSON configuration of the server, potentially revealing database paths or other sensitive environment variables.
The Impact: Why This Matters
You might argue, "It's a dev tool, who cares?" But dev tools are often the soft underbelly of corporate security. They are frequently deployed with default settings, no authentication, and permissive firewall rules because "it's just for testing."
An SSRF here allows an attacker to:
- Bypass Network Segmentation: Use the Mailpit server as a jump box to reach internal databases or admin panels.
- Steal Cloud Credentials: As shown above, this is a direct path to full account compromise in cloud environments.
- Perform Port Scanning: Map out the internal network topology without sending a single packet from the attacker's machine directly to the targets.
The severity (5.8) might seem "Medium," but in the right environment, the impact is Critical.
The Mitigation: Stop the Bleeding
The fix provided by the maintainer is robust. It moves from an "allow-list of protocols" (which failed) to an "allow-list of content" and "verification of intent."
Immediate Steps:
- Update Mailpit: Ensure you are running a version built after January 6, 2026.
- Network Isolation: Even with the patch, your dev tools shouldn't have unrestricted outbound access. Use firewall rules to block the Mailpit container/server from reaching sensitive internal subnets (like
169.254.169.254or your production DB range). - Authentication: Mailpit supports basic auth. Turn it on. If an attacker can't reach the UI, they can't trigger the proxy endpoint.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Mailpit axllent | < 2026-01-06 (commit 3b9b470) | Post-2026-01-06 Release |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 (SSRF) |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 5.8 (Medium) |
| Exploit Status | PoC Available |
| Authentication | None Required (Default) |
| Impact | Confidentiality (High potential via cloud metadata) |
MITRE ATT&CK Mapping
Server-Side Request Forgery (SSRF) occurs when a web application fetches a remote resource without validating the user-supplied URL. It allows an attacker to coerce the application to send a crafted request to an unexpected destination, often bypassing firewalls.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.