flawopen.com/Teardowns/cve-2014-0160-heartbleed
CVE-2014-0160: OpenSSL Heartbleed memory leak
CVE-2014-0160 let a TLS or DTLS peer read up to 64 KiB beyond a Heartbeat record when OpenSSL trusted a declared payload length that the record did not contain. The upstream fix validates the record before parsing and echoing the payload.[1], [2]
Imagine a clerk receives a card with a few words but trusts a label asking for a much longer copy. Instead of stopping at the card, the clerk keeps copying whatever text follows in the shared tray. Heartbleed made OpenSSL copy past the received record; the bytes exposed depended on what was present in process memory.[1], [3]
Core Concepts & Subsystem Terms
TLS/DTLS Heartbeat (RFC 6520)- A keep-alive message in TLS or DTLS. A request carries a payload that the peer echoes, with padding required by the protocol.[3]
payload_length- The 16-bit field that states the Heartbeat payload size. It must fit inside the received record after the message type, length field, and minimum padding.[1], [3]
memcpy()- Copies a requested byte count from a source pointer. It cannot determine the source buffer’s valid length; the caller must validate that length first.[1], [2]
Buffer over-read (CWE-126)- Reading past the end of the intended source buffer. In Heartbleed, the echoed response could disclose up to 64 KiB of process memory, depending on memory contents.[5]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-126
- CISA KEV — added 2022-05-04 — CVE-2014-0160
Root Cause Analysis
tls1_process_heartbeat() and dtls1_process_heartbeat() read a Heartbeat message’s 16-bit payload length and, in the request path, passed it to memcpy() without proving that many payload bytes existed in s->s3->rrec.length. A peer could declare more data than the TLS or DTLS record carried. OpenSSL then echoed bytes past the received record, potentially disclosing up to 64 KiB of process memory; the contents were memory-dependent, so a particular key, password, or cookie was not guaranteed to appear. Upstream commit 96db902 first rejects records too short for the type, length, and minimum padding, then checks 1 + 2 + payload + 16 against the received record length. It also bounds the reply with SSL3_RT_MAX_PLAIN_LENGTH and uses the computed write_length. The same guard pattern was applied to both TLS and DTLS handlers.[1], [2], [3], [4]
Step-by-Step Attack Flow
Source Code: Flaw vs. Secure Implementation
These are excerpts from OpenSSL’s upstream change, not a standalone program. The vulnerable code parses fields before checking that even the fixed header and minimum padding are present, then copies the peer-declared payload length. The patch checks the minimum record size before parsing, validates the declared payload against the received record, and bounds the encoded reply. OpenSSL made corresponding changes in tls1_process_heartbeat() and dtls1_process_heartbeat().[2], [1]
Source excerpt: ssl/t1_lib.c
/* Excerpt from tls1_process_heartbeat() before the fix. */
hbtype = *p++;
n2s(p, payload);
pl = p;
/* In the TLS1_HB_REQUEST response path: */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT,
buffer, 3 + payload + padding);
/* Reject a record too short for type, length, and minimum padding. */
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;
/* In the TLS1_HB_REQUEST response path: */
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);
Incident Timeline
Engineering & System Hardening Checklist
- ✓Upgrade to a vendor-supported OpenSSL release containing the fix; upstream fixed 1.0.1g and 1.0.2-beta2. Verify distribution backports instead of relying only on the displayed version string.[1], [2]
- ✓For endpoints that could have been exposed, replace affected TLS private keys and certificates and revoke old certificates where exposure is plausible; include client endpoints that processed untrusted heartbeats.[6]
- ✓After the vulnerable service is patched, rotate credentials that could plausibly have been present in its process memory. Change user passwords only after the service owner confirms the service has been fixed.[6], [8]
- ✓Test both TLS and DTLS handlers at record boundaries: too-short fixed headers, payload lengths beyond received bytes, and valid requests with minimum padding.[2], [3]
- ✓If Heartbeat is unnecessary and compatibility permits, rebuild with
-DOPENSSL_NO_HEARTBEATS, the alternative mitigation in the OpenSSL advisory.[1]
Sources
- 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