flawopen.com/Teardowns/cve-2014-0160-heartbleed
CVE-2014-0160: vazamento Heartbleed no OpenSSL
A CVE-2014-0160 permitia que um par TLS ou DTLS lesse até 64 KiB além de um registro Heartbeat quando o OpenSSL confiava em um tamanho de payload que não estava presente no registro. A correção upstream valida o registro antes de analisar e ecoar o payload.[1], [2]
Imagine um funcionário recebendo um cartão com poucas palavras, mas confiando em uma etiqueta que pede uma cópia muito maior. Em vez de parar no cartão, ele continua copiando o texto que aparece depois na bandeja compartilhada. O Heartbleed fazia o OpenSSL copiar além do registro recebido; os bytes expostos dependiam do conteúdo da memória do processo.[1], [3]
Conceitos Centrais e Termos
Heartbeat TLS/DTLS (RFC 6520)- Mensagem de manutenção de conexão em TLS ou DTLS. A solicitação contém um payload que o par devolve, além do padding exigido pelo protocolo.[3]
payload_length- Campo de 16 bits que informa o tamanho do payload Heartbeat. Ele deve caber no registro recebido após o tipo da mensagem, o campo de tamanho e o padding mínimo.[1], [3]
memcpy()- Copia uma quantidade de bytes a partir de um ponteiro de origem. A função não conhece o tamanho válido do buffer; o chamador precisa validar esse limite.[1], [2]
Leitura além do buffer (CWE-126)- Leitura após o fim do buffer de origem esperado. No Heartbleed, a resposta podia expor até 64 KiB da memória do processo, dependendo do conteúdo presente.[5]
Mapeamentos de frameworks
- Referência CWE para a fraqueza descrita neste guia: CWE-126
- CISA KEV — adicionado em 2022-05-04 — CVE-2014-0160
Análise de Causa Raiz
tls1_process_heartbeat() e dtls1_process_heartbeat() liam o tamanho de payload de 16 bits de uma mensagem Heartbeat e, no caminho de resposta, o passavam a memcpy() sem comprovar que esses bytes existiam em s->s3->rrec.length. Um par podia declarar mais dados do que o registro TLS ou DTLS continha. O OpenSSL então ecoava bytes além do registro recebido, podendo expor até 64 KiB da memória do processo; o conteúdo dependia da memória, portanto não havia garantia de que uma chave, senha ou cookie específico aparecesse. O commit upstream 96db902 primeiro rejeita registros curtos demais para o tipo, o campo de tamanho e o padding mínimo; depois compara 1 + 2 + payload + 16 com o tamanho do registro recebido. Também limita a resposta com SSL3_RT_MAX_PLAIN_LENGTH e usa o write_length calculado. O mesmo padrão de validação foi aplicado aos handlers TLS e DTLS.[1], [2], [3], [4]
Fluxo de Ataque Passo a Paso
Código-Fonte: Vulnerável vs. Seguro
Estes trechos vêm da alteração upstream do OpenSSL e não formam um programa independente. O código vulnerável analisa os campos antes de verificar se o cabeçalho fixo e o padding mínimo estão presentes e, depois, copia o tamanho de payload declarado pelo par. A correção verifica o tamanho mínimo do registro antes da análise, valida o payload declarado contra o registro recebido e limita o tamanho da resposta. O OpenSSL aplicou mudanças correspondentes a tls1_process_heartbeat() e dtls1_process_heartbeat().[2], [1]
Trecho de origem: ssl/t1_lib.c
/* Trecho de tls1_process_heartbeat() antes da correção. */
hbtype = *p++;
n2s(p, payload);
pl = p;
/* No caminho de resposta TLS1_HB_REQUEST: */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT,
buffer, 3 + payload + padding);
/* Rejeita um registro curto demais para o tipo, o tamanho e o preenchimento 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;
/* No caminho de resposta 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);
Linha do Tempo do Incidente
Lista de Verificação de Segurança para Engenharia
- ✓Atualize para uma versão do OpenSSL ainda suportada pelo fornecedor que contenha a correção; o upstream corrigiu 1.0.1g e 1.0.2-beta2. Verifique backports da distribuição, não apenas o texto da versão.[1], [2]
- ✓Em endpoints que possam ter ficado expostos, substitua chaves privadas e certificados TLS afetados e revogue os certificados antigos quando houver possibilidade de exposição; inclua clientes que processaram heartbeats não confiáveis.[6]
- ✓Depois de corrigir o serviço vulnerável, troque as credenciais que poderiam plausivelmente estar na memória do processo. Altere senhas de usuários somente após o responsável pelo serviço confirmar que a correção foi aplicada.[6], [8]
- ✓Teste os handlers TLS e DTLS nos limites do registro: cabeçalhos fixos curtos, tamanhos de payload maiores que os bytes recebidos e solicitações válidas com padding mínimo.[2], [3]
- ✓Se o Heartbeat não for necessário e a compatibilidade permitir, recompile com
-DOPENSSL_NO_HEARTBEATS, a mitigação alternativa do aviso do OpenSSL.[1]
Fontes
- 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