libuhttpd: Stack Overflow en date2unix

Un header If-Modified-Since de 220 bytes desborda el buffer de stack buf[128] en date2unix. El resultado es un control del RIP con un decoder XOR montado en el propio stack. Análisis del sink, PoC y validación E2E completa.

HIGH
Stack overflow · pre-auth · CWE-121 · Control parcial del RIP · Sin CVE · Sin fix · Validador E2E · Requiere ≥1 archivo en el docroot
+

00. Resumen Ejecutivo

libuhttpd es un pequeño servidor HTTP embebido extendido en aplicaciones de red (aplicaciones web de red, dashboards, gateways). Al servir un archivo estático comprueba la cabecera If-Modified-Since. El parser de fechas date2unix copia el valor de la cabecera a un buffer de 128 bytes sin comprobar la longitud de origen, con lo que un valor largo desborda el stack.

Como el valor de la cabecera es completamente controlable por el atacante y el desbordamiento escribe en el stack, se puede tomar control parcial del flujo. La ejecución de código depende de la toolchain (canarios, NX, ASLR), que en el despliegue embebido de referencia suele estar ausente o ser eludible.

01. 0x00 — El Sink: file.c date2unix

// file.c:136 — date2unix static int date2unix(char *date, time_t *out) { char buf[128]; // buffer fijo en el stack ... strncpy(buf, date, strlen(date)); // SIN bound de destino: desborda buf // 'date' proviene de la cabecera If-Modified-Since del request ... }

El bug es que el tamaño copiado viene dado por el origen (strlen(date)), no por el destino (sizeof(buf)). Un If-Modified-Since de más de 128 bytes empieza a sobrescribir el marco de stack de la función.

02. 0x01 — La Explotación (decoder XOR in-stack)

Para convertir el desbordamiento en control del RIP en la toolchain de referencia (sin canario), el shellcode se auto-decoda en el propio stack. La técnica: escribir el bytecode XOR in-stack para evitar bytes malos del parsing HTTP (espacios, línea nueva, etc.), y redirigir el RIP hacia allí.

# Layout del payload sobre el stack (orden de escritura del strncpy): [padding ][ buf[128] ][ ROMPER_EBP ][ EIP_dirige_a_stack ][ shellcode_XOR ] # El decoder XOR (8 bytes) se ejecuta primero y desofusca el shellcode # que aparece después en el stack, evadiendo bytes prohibidos del HTTP. # Ejecución: fecha=<padding><EIP><decoder><shellcode_xor_encoded>

La observación clave de la validación: bajo gdb (que usa ptrace) la dirección del stack era +0xC0 bytes distinta de la del run nativo. Solo reconstruyendo el layout en el run real se logró aterrizar el EIP en el shellcode. Este tipo de disparidad nativo/gdb es exactamente lo que el estándar E2E exige demostrar.

03. 0x02 — El PoC

El PoC es un GET con un If-Modified-Since de ~220 bytes. Una prueba limpia es el control de flujo con un valor guía:

# GET a un archivo del docroot con la cabecera larga GET /index.html HTTP/1.1 Host: 127.0.0.1:8080 If-Modified-Since: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... # ~220 bytes # Con ASAN se observa el stack-buffer-overflow en date2unix # Y con el EIP guiado, el RIP aterriza en 0x41414141 (control parcial)

04. 0x03 — Validación E2E (ASAN + gdb)

La validación reproducida:

# Compilar libuhttpd con ASAN para confirmar el overflow exacto gcc -fsanitize=address -g -O0 ... libuhttpd.c # 1. Reporte ASAN del desbordamiento en el stack $ ./app ERROR: AddressSanitizer: stack-buffer-overflow on address ... WRITE of size 4 at ... # date2unix, buf[128] sobrescrito # 2. Control del EIP bajo gdb (gdb) r Program received signal SIGSEGV RIP = 0x41414141 # control parcial del flujo # 3. Run nativo: reconstruir el layout +0xC0 para aterrizar el shellcode $ ./app # reverse shell low-priv hacia el atacante
Fase 1: el sink date2unix identificado
Fase 1 — Identificación del sink date2unix en file.c, el strncpy sin bound de destino.
Fase 3: servidor levantado y payload preparado
Fase 3 — Servidor en marcha y payload del header If-Modified-Since preparado.
Fase 4: el desbordamiento disparado
Fase 4 — El desbordamiento se dispara con la fecha de 220 bytes.
Fase 5: shell obtenida
Fase 5 — Shell de baja autoridad obtenida vía el decoder XOR in-stack.

05. 0x04 — Limitaciones de la Toolchain

MitigaciónImpacto en el PoC
Canario (-fstack-protector)Aborta antes del ret si está activo; no lo estaba en la toolchain de referencia
NX stackNecesita el decoder+RIP ejecutable; en MCU embebido típicamente NX desactivado
ASLREl layout in-stack es determinista (dirección fija), elude ASLR del heap/libs
Divulgación responsable

El fix en el mantenedor es copiar con un bound basado en el destino (strncpy(buf, date, sizeof(buf)-1) y terminar), o validar la longitud de la cabecera antes del parseo. Publicado con fines de hardening y comunicado al mantenedor como parte del proceso responsable.

06. 0x05 — Referencias

ReferenciaDescripción
file.c:136Sink date2unix: strncpy sin bound de destino
If-Modified-SinceCabecera HTTP fuente del desbordamiento
CWE-121Stack-based Buffer Overflow
LECCION E2EDisparidad de stack nativo vs gdb (+0xC0) — requiere run real

← Volver al índice de la serie