CVE-2026-34441: Verified Repro With Script Download
CVE-2026-34441: cpp-httplib HTTP Request Smuggling via Unconsumed GET Request Body
CVE-2026-34441 is verified against yhirose/cpp-httplib · github. Affected versions: <=0.38.0 per GHSA metadata. Fixed in 0.40.0. Vulnerability class: Request Smuggling. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00127.
What Is CVE-2026-34441?
CVE-2026-34441 is a high-severity HTTP Request Smuggling vulnerability in cpp-httplib, a header-only C++ HTTP library, versions <= 0.38.0. The server's static file handler serves GET responses without consuming the request body, letting body bytes be reinterpreted as a new request on keep-alive connections. Pruva reproduced it (reproduction REPRO-2026-00127).
CVE-2026-34441 Severity & CVSS Score
CVE-2026-34441 is rated medium severity, with a CVSS base score of 4.8 out of 10.
Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.
Affected yhirose/cpp-httplib Versions
yhirose/cpp-httplib · github versions <=0.38.0 per GHSA metadata are affected.
How to Reproduce CVE-2026-34441
pruva-verify REPRO-2026-00127 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00127/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-34441
Reproduced by Pruva's autonomous agents — 300 tool calls over 1h 1m. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-34441
Summary
cpp-httplib versions ≤0.38.0 are vulnerable to HTTP Request Smuggling. The vulnerability occurs because the server's static file handler serves GET responses WITHOUT consuming the request body. When a GET request with a Content-Length header matches a static file mount point, the handle_file_request() function returns true and causes routing() to return early, bypassing the expect_content() check that would normally read the body. This leaves request body bytes on the TCP stream, which are then interpreted as a new HTTP request on keep-alive connections.
Impact
Package: cpp-httplib (header-only C++ HTTP library) Affected Versions: ≤0.38.0 Fixed Version: 0.40.0
Risk Level: HIGH
Consequences:
- HTTP Request Smuggling: Arbitrary HTTP requests on keep-alive connections
- Access Control Bypass: Smuggled requests bypass proxy-level authentication
- Cache Poisoning: Smuggled responses cached for wrong URLs
- Request Hijacking: Behind reverse proxy, smuggled request paired with other user's request
Root Cause
Technical Explanation
The vulnerability exists in the Server::routing() function in httplib.h:
// File handler
if ((req.method == "GET" || req.method == "HEAD") &&
handle_file_request(req, res)) {
return true; // Returns early for static files
}
if (detail::expect_content(req)) { // This is never reached for GET static files
// Content reader handler - would read the body
...
}
The Bug Flow:
- Client sends GET request with Content-Length header and a body containing a smuggled HTTP request
- Server matches request to static file mount point via
handle_file_request() routing()returns true immediately (early return)- The
expect_content()check is never executed for static file requests - Request body bytes remain unread in the TCP stream
- Server sends response for first request and keeps connection alive
- Next iteration of keep-alive loop reads body bytes as a new HTTP request
- Server processes the smuggled request
Code Location: Server::routing() at approximately line 11542-11544 in v0.38.0
Reproduction Evidence
Our differential testing confirms the vulnerability:
v0.38.0 (Vulnerable):
- 2 HTTP responses received from single payload
- SECRET content leaked from smuggled request
- Server processed 2 requests: /index.html and /secret.html
- Total bytes received: 360 (two full HTTP responses)
v0.40.0 (Fixed):
- 1 HTTP response received
- No SECRET content leaked
- Server processed 1 request: /index.html only
- Total bytes received: 179 (single HTTP response)
Exploit Payload:
GET /index.html HTTP/1.1\r\n
Host: localhost\r\n
Content-Length: 38\r\n
Connection: keep-alive\r\n
\r\n
GET /secret.html HTTP/1.1\r\n
Host: x\r\n
\r\n
The first request (GET /index.html) has a Content-Length: 38 header pointing to the second request in the body. The vulnerable server serves /index.html without consuming the body, then the body bytes are read as a separate HTTP request on the keep-alive connection.
Fix Analysis
Version 0.40.0 properly handles the request body in one of these ways:
- The body is consumed before the connection is returned to the keep-alive pool
- The connection is closed after requests with unconsumed bodies
- The expect_content() check is moved earlier in the routing flow
The fix ensures that body bytes cannot be left in the stream to be misinterpreted as a new request.
Attack Scenario
- Attacker sends crafted request to a reverse proxy with a smuggled request in the body
- Proxy forwards to cpp-httplib backend (v0.38.0)
- Backend serves the legitimate request but doesn't consume the body
- Backend keeps connection alive to proxy
- Backend reads body as second request and processes it
- Second response is associated with the proxy's original request
- Attacker receives response for smuggled request, potentially accessing protected resources
Recommendations
- Upgrade to cpp-httplib v0.40.0 or later
- Validate Content-Length headers on GET requests at the proxy/load balancer level
- Disable keep-alive for requests with bodies if using affected versions
- Monitor for anomalous request patterns in server logs
References
- CVE-2026-34441
- cpp-httplib repository: https://github.com/yhirose/cpp-httplib
- Affected version: v0.38.0
- Fixed version: v0.40.0
CVE-2026-34441 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Unknown error
cd /data/pruva/runs/413afe30-008f-4058-ac2a-f136b90af54a && git clone --depth 1 --branch v0.38.0 https://github.com/yhirose/cpp-httplib.git 2>&1Cloning into 'cpp-httplib'... Note: switching to '6f2717e6230713484c4ee819fdd431723d29ea08'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may
tail -100 /data/pruva/runs/413afe30-008f-4058-ac2a-f136b90af54a/cpp-httplib/example/simplesvr.cc{"cwd":"/data/pruva/runs/413afe30-008f-4058-ac2a-f136b90af54a","duration_ms":3,"exit_code":0,"stderr":"","stdout":" for (const auto &x : form.files) {\n const auto &name = x.first;\n const auto &file = x.second;\n\n snprintf(buf, sizeof(buf), \"name: %s\\n\", name.c_str());\n s += buf;\n\n snprintf(buf, sizeof(buf), \"filename: %s\\n\", file.filename.c_str());\n s += buf;\n\n snprintf(buf, sizeof(buf), \"content type: %s\\n\", file.content_type.c_str());\n s += buf;\n\n snprintf(buf, sizeof(buf), \"text length: %zu\\n\", file.content.size());\n s += buf;\n\n s += \"----------------\\n\";\n }\n\n return s;\n}\n\nstring log(const Request &req, const Response &res) {\n string s;\n char buf[BUFSIZ];\n\n s += \"================================\\n\";\n\n snprintf(buf, sizeof(buf), \"%s %s %s\", req.method.c_str(),\n req.version.c_str(), req.path.c_str());\n s += buf;\n\n string query;\n for (auto it = req.params.begin(); it != req.params.end(); ++it) {\n const auto &x = *it;\n snprintf(buf, sizeof(buf), \"%c%s=%s\",\n (it == req.params.begin()) ? '?' : '&', x.first.c_str(),\n x.second.c_str());\n query … [truncated]Artifacts and Evidence for CVE-2026-34441
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-34441
Upgrade yhirose/cpp-httplib · github to 0.40.0 or later.
FAQ: CVE-2026-34441
How does the CVE-2026-34441 smuggling attack work?
Which versions of cpp-httplib are affected by CVE-2026-34441, and where is it fixed?
How severe is CVE-2026-34441?
How can I reproduce CVE-2026-34441?
References for CVE-2026-34441
Authoritative sources for CVE-2026-34441 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.