flawopen.com/Teardowns/cve-2014-0160-heartbleed
CVE-2014-0160: fuga de memoria Heartbleed
CVE-2014-0160 permitía que un par TLS o DTLS leyera hasta 64 KiB más allá de un registro Heartbeat cuando OpenSSL confiaba en una longitud de payload que el registro no contenía. La corrección upstream valida el registro antes de analizar y devolver el payload.[1], [2]
Imagina que un empleado recibe una tarjeta con pocas palabras, pero confía en una etiqueta que pide una copia mucho más larga. En vez de detenerse al final de la tarjeta, sigue copiando el texto que hay después en la bandeja compartida. Heartbleed hacía que OpenSSL copiara más allá del registro recibido; los bytes expuestos dependían del contenido de la memoria del proceso.[1], [3]
Conceptos Clave y Términos
Heartbeat TLS/DTLS (RFC 6520)- Mensaje de mantenimiento de conexión en TLS o DTLS. La solicitud incluye un payload que el par devuelve, además del padding exigido por el protocolo.[3]
payload_length- Campo de 16 bits que indica el tamaño del payload Heartbeat. Debe caber en el registro recibido después del tipo de mensaje, el campo de longitud y el padding mínimo.[1], [3]
memcpy()- Copia una cantidad indicada de bytes desde un puntero de origen. No conoce la longitud válida del búfer de origen; el llamador debe validarla.[1], [2]
Lectura fuera del búfer (CWE-126)- Lectura más allá del final del búfer de origen previsto. En Heartbleed, la respuesta podía revelar hasta 64 KiB de memoria del proceso, según su contenido.[5]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-126
- CISA KEV — añadido el 2022-05-04 — CVE-2014-0160
Análisis de Causa Raíz
tls1_process_heartbeat() y dtls1_process_heartbeat() leían la longitud de payload de 16 bits de un mensaje Heartbeat y, en la ruta de respuesta, la pasaban a memcpy() sin demostrar que esos bytes existieran en s->s3->rrec.length. Un par podía declarar más datos de los que contenía el registro TLS o DTLS. OpenSSL entonces devolvía bytes posteriores al registro recibido y podía revelar hasta 64 KiB de memoria del proceso; el contenido dependía de esa memoria, por lo que no se garantizaba que apareciera una clave, contraseña o cookie concreta. El commit upstream 96db902 primero rechaza registros demasiado cortos para el tipo, el campo de longitud y el padding mínimo; después compara 1 + 2 + payload + 16 con la longitud del registro recibido. También limita la respuesta mediante SSL3_RT_MAX_PLAIN_LENGTH y usa el write_length calculado. Se aplicó el mismo patrón a los handlers TLS y DTLS.[1], [2], [3], [4]
Flujo de Ataque Paso a Paso
Código Fuente: Vulnerable vs. Seguro
Estos fragmentos proceden del cambio upstream de OpenSSL y no son un programa independiente. El código vulnerable analiza los campos antes de comprobar que estén presentes la cabecera fija y el padding mínimo, y después copia la longitud de payload declarada por el par. La corrección comprueba el tamaño mínimo del registro antes de analizarlo, valida el payload declarado frente al registro recibido y limita la respuesta. OpenSSL aplicó cambios equivalentes a tls1_process_heartbeat() y dtls1_process_heartbeat().[2], [1]
Fragmento de origen: ssl/t1_lib.c
/* Fragmento de tls1_process_heartbeat() antes de la corrección. */
hbtype = *p++;
n2s(p, payload);
pl = p;
/* En la ruta de respuesta TLS1_HB_REQUEST: */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT,
buffer, 3 + payload + padding);
/* Rechaza un registro demasiado corto para el tipo, la longitud y el relleno mínimo. */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0;
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0;
pl = p;
/* En la ruta de respuesta TLS1_HB_REQUEST: */
unsigned int write_length = 1 + 2 + payload + padding;
if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
return 0;
buffer = OPENSSL_malloc(write_length);
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
Cronología del Incidente
Lista de Verificación de Seguridad para Ingeniería
- ✓Actualice a una versión de OpenSSL con soporte del proveedor que incluya la corrección; upstream corrigió 1.0.1g y 1.0.2-beta2. Compruebe los backports de la distribución, no solo el texto de la versión.[1], [2]
- ✓En endpoints que pudieron estar expuestos, sustituya las claves privadas y los certificados TLS afectados y revoque los certificados antiguos si la exposición es plausible; incluya clientes que procesaron heartbeats no confiables.[6]
- ✓Después de corregir el servicio vulnerable, rote las credenciales que podrían haber estado en su memoria de proceso. Cambie las contraseñas de usuario solo cuando el responsable del servicio confirme que ya está corregido.[6], [8]
- ✓Pruebe los handlers TLS y DTLS en los límites del registro: cabeceras fijas demasiado cortas, longitudes de payload mayores que los bytes recibidos y solicitudes válidas con padding mínimo.[2], [3]
- ✓Si Heartbeat no es necesario y la compatibilidad lo permite, recompile con
-DOPENSSL_NO_HEARTBEATS, la mitigación alternativa del aviso de OpenSSL.[1]
Fuentes
- OpenSSL Security Advisory — TLS heartbeat read overrun (CVE-2014-0160)
- OpenSSL upstream fix — commit 96db902
- RFC 6520 — TLS and DTLS Heartbeat Extension
- NVD — CVE-2014-0160
- MITRE CWE-126 — Buffer Over-read
- Codenomicon / Heartbleed.com — The Heartbleed Bug
- CVE Program — CVE-2014-0160
- CISA — “Heartbleed” OpenSSL Vulnerability