Server-side request forgery (SSRF) In github.com/axllent/mailpit

Description

Mailpit has a Server-Side Request Forgery (SSRF) via HTML Check API

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 132

      downloadToBytes() - line 193

      isURL() - 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...

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
    }
...

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:


# 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:

╔══════════════════════════════════════════════════════════════════════════════╗
║  Mailpit SSRF PoC - HTML Check CSS Download Vulnerability                   ║
║  Severity: MODERATE                                                              ║
║  File: internal/htmlcheck/css.go:193-207                                    ║
╚══════════════════════════════════════════════════════════════════════════════╝

[+] SSRF listener started on port 8888
[*] Testing SSRF with callback to local listener......

Manual Testing:

# 1. Send malicious email
cat << 'EOF' | python3 - <<SENDMAIL
import smtplib
from email.mime.text import MIMEText

html = '''
<!DOCTYPE html>
<html>...

Mitigation

Update Impact

Minimal update. May introduce new vulnerabilities or breaking changes.

Ecosystem
Package
Affected version
Patched versions