PoC Archive PoC Archive
High CVE-2026-33555 unpatched

HAProxy HTTP/3 (QUIC) Standalone FIN Body Validation Bypass Leading to Request Smuggling — CVE-2026-33555

by r3verii · 2026-07-05

Severity
High
CVE
CVE-2026-33555
Category
network
Affected product
HAProxy with HTTP/3 (QUIC) support built (USE_QUIC=1); tested on HAProxy 3.0.18
Affected versions
HAProxy 3.0.18 (QUIC-enabled builds); requires http-reuse always backend configuration
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-04
Author / Researcherr3verii
CVE / AdvisoryCVE-2026-33555
Categorynetwork
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagshaproxy, http3, quic, request-smuggling, http-reuse, content-length, reverse-proxy, cross-user
RelatedN/A

Affected Target

FieldValue
Software / SystemHAProxy with HTTP/3 (QUIC) support built (USE_QUIC=1); tested on HAProxy 3.0.18
Versions AffectedHAProxy 3.0.18 (QUIC-enabled builds); requires http-reuse always backend configuration
Language / PlatformPoC client in Python (aioquic); target is HAProxy + nginx backend, run via Docker Compose
Authentication RequiredNo
Network Access RequiredYes

Summary

HAProxy’s HTTP/3 frontend does not validate that a request’s declared Content-Length matches the number of body bytes actually delivered over the QUIC stream before the stream is closed (a “standalone FIN”). When HAProxy translates such a malformed HTTP/3 request into HTTP/1.1 for the backend, it forwards the (incorrect) Content-Length header while transmitting zero actual body bytes. If the backend answers early (e.g. with a 301 redirect) and then drains the declared-but-missing body bytes from the pooled TCP connection — a pattern enabled by the non-default but common http-reuse always setting — it inadvertently consumes bytes belonging to the next, unrelated HTTP request queued on that same reused connection. Because HAProxy pools backend connections across different client requests, this effectively smuggles one user’s request into another user’s response, a classic cross-user HTTP request smuggling condition. The PoC ships a full Docker Compose lab (HAProxy H3/QUIC frontend, stock nginx backend, and a Python/aioquic attacker client) that continuously sends malformed “poison” requests and demonstrates that a legitimate victim on a separate QUIC connection receives corrupted (400) responses while the attack is running.


Vulnerability Details

Root Cause

HAProxy’s HTTP/3/QUIC request parser does not verify that the actual bytes received on a QUIC stream (terminated by a stream FIN) match the request’s declared Content-Length before proxying the request to an HTTP/1.1 backend over a pooled/reused connection.

Attack Vector

  1. Attacker establishes an HTTP/3 (QUIC) connection to the HAProxy frontend and sends a POST request with a Content-Length header larger than the actual body bytes sent, then closes the stream (FIN) early.
  2. HAProxy forwards the request to the backend over a reused HTTP/1.1 TCP connection, including the false Content-Length but without the missing body bytes.
  3. The backend (e.g. nginx) responds early (such as a 301 redirect) and then reads/drains the connection expecting the declared body length, consuming bytes that actually belong to the next request queued on that pooled connection.
  4. A victim’s legitimate request sent over a separate QUIC connection gets its bytes misattributed as the tail of the attacker’s request, causing the victim to receive a corrupted/incorrect response (e.g. HTTP 400) instead of their expected response.

Impact

An unauthenticated network attacker can desynchronize a shared HAProxy backend connection pool, causing cross-user response confusion/corruption for other clients traversing the same proxy — potentially leading to response splitting, cache poisoning, or information disclosure depending on backend behavior.


Environment / Lab Setup

Target:   HAProxy 3.0.18 (USE_QUIC=1) fronting stock nginx 1.27, http-reuse always
Attacker: Docker Compose (haproxy, nginx, client containers), Python 3 + aioquic in the client container

Proof of Concept

PoC Script

See client/poc.py, haproxy/haproxy.cfg, haproxy/Dockerfile, nginx/default.conf, and docker-compose.yml in this folder.

1
2
3
cd poc/
docker compose up -d --build
docker exec -it poc-client python3 poc.py --target haproxy --port 10002 --interval 3

The lab spins up an HAProxy H3/QUIC frontend with a pooled HTTP/1.1 backend to nginx, plus a Python/aioquic attacker container. Running poc.py in continuous mode repeatedly sends the malformed Content-Length “poison” request; a --once single-shot mode sends one poison request followed by a victim-style request on a separate QUIC connection and reports whether the victim received a corrupted (400) response instead of the expected 200, confirming request smuggling.


Detection & Indicators of Compromise

Signs of compromise:

  • Spikes in HTTP 400 (or otherwise anomalous) responses served to unrelated clients immediately following requests with suspicious/mismatched Content-Length values.
  • Backend (nginx) access logs showing early 301/redirect responses on connections later associated with unrelated victim requests.
  • QUIC/H3 connection logs showing streams closed (FIN) before the declared body length was fully transmitted.

Remediation

ActionDetail
Primary fixNo vendor patch confirmed as of 2026-07-05 — monitor for advisory; ensure HAProxy validates actual received body length against declared Content-Length before forwarding over reused connections.
Interim mitigationDisable http-reuse always (use safe or never) on H3/QUIC-fronted backends until a fix is available, or disable QUIC/H3 support if not required.

References


Notes

Mirrored from https://github.com/r3verii/CVE-2026-33555 on 2026-07-05.