OpenSSL trusted the user-supplied length field in Heartbeat packets without verifying the actual payload size. This allowed attackers to 'over-read' the heap, leaking sensitive data like SSL private keys and user passwords. It affected nearly two-thirds of the internet upon disclosure.
A catastrophic missing bounds check in the OpenSSL Heartbeat extension allowed remote attackers to read up to 64KB of process memory, exposing private keys, session tokens, and user credentials.
April 2014 was a simpler time. We worried about SQL injection and the occasional XSS. Then, on a fateful Monday, the internet collectively gasped. A vulnerability was disclosed in OpenSSL, the cryptographic library that essentially held the web together. It wasn't a complex race condition. It wasn't a cryptographic break. It was a missing if statement.
The vulnerability, dubbed "Heartbleed," lived in the implementation of the TLS Heartbeat Extension (RFC 6520). The concept of a Heartbeat is simple: it's a keep-alive mechanism. A client sends a message saying, "I'm still here, here is a word, please repeat it back to me." The server, being polite, echoes the word back. This avoids the overhead of renegotiating secure connections.
But here is where the cynicism of security research sets in: developers often assume inputs are honest. The OpenSSL developers assumed that if a client said, "I am sending you a 5-byte word," they were actually sending 5 bytes. But what happens when a client lies? What happens when a client says, "I am sending you a 65,535-byte word," but only sends one byte? The server, blindly obedient, reads the one byte provided, and then keeps reading 65,534 more bytes from its own memory to fulfill the request. Welcome to the most embarrassing data exfiltration method in history.
To understand Heartbleed, you have to understand the memcpy instruction and the concept of a buffer over-read. This is the distinct, slightly less destructive cousin of the buffer overflow. In an overflow, we write garbage where we shouldn't. In an over-read, we see things we shouldn't.
Here is the logic flaw in plain English:
0xFFFF (65,535).0xFFFF because that's what the attacker asked for.memcpy.memcpy relies on the length field provided. It copies the 'A', reaches the end of the input packet, and just keeps going.It continues reading from the heap memory adjacent to the input buffer until it has filled the 64KB quota. This memory wasn't empty. It was occupied by whatever OpenSSL was doing previously. This is the digital equivalent of asking a bank teller for change for a $100 bill, handing them a penny, and watching them empty the register to make up the difference because they trust your math implicitly.
Let's look at the crime scene. The vulnerability resided in d1_both.c and t1_lib.c. The code handled the Heartbeat request by reading the type and the payload length from the wire.
The Vulnerable Code:
/* p points to the incoming record data */
hbtype = *p++;
n2s(p, payload); // Reads 16-bit length into 'payload' variable
pl = p; // pl points to the start of the supplied data
/* ... later in the code ... */
/* bp is the output buffer */
/* pl is the input buffer */
/* payload is the attacker-controlled length */
memcpy(bp, pl, payload);Do you see the bounds check? Neither do I. The variable payload is completely controlled by the attacker. The variable pl points to the incoming data. If the actual data at pl is shorter than payload, memcpy happily crosses the boundary into process memory.
The Fix (OpenSSL 1.0.1g): The patch is painfully simple. It calculates the actual length of the received record and ensures the requested payload size fits inside it.
/* Read type and payload length */
hbtype = *p++;
n2s(p, payload);
/* THE FIX: Check if the record is actually long enough */
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* Silently discard malformed packet */
pl = p;The check 1 + 2 + payload + 16 accounts for the heartbeat type (1 byte), the length field (2 bytes), the payload itself, and the padding (16 bytes). If the math doesn't check out, the server ignores the request. It took two lines of code to stop the internet from bleeding out.
Exploiting Heartbleed is trivial. You don't need shellcode. You don't need to bypass ASLR or DEP. You just need to send a malformed packet. The beauty (and horror) of this exploit is that it leaves very few traces in standard logs, as the connection negotiation often completes successfully or looks like a standard timeout.
The Attack Vector:
0x18: Content Type (Heartbeat)0x03 0x01: Version (TLS 1.1)0x00 0x03: Length of the Heartbeat message (3 bytes)0x01: Heartbeat Type (Request)0xFF 0xFF: Payload Length (65535 bytes - The Lie)0x18 packet containing 0xFFFF bytes of data.What's in the dump? Because OpenSSL uses a custom allocator (to reduce fragmentation and syscalls), related objects tend to sit next to each other in memory. When you over-read the heap, you aren't getting random garbage; you are getting fresh data.
[!NOTE] Researchers found that sending multiple requests (Heap Spraying/Grooming) allowed them to map out the memory layout and statistically guarantee the extraction of the server's private key (
RSA PRIVATE KEY) within a few hundred attempts.
Beyond keys, we saw session cookies (PHPSESSID, JSESSIONID), HTTP POST data containing passwords, and even medical records. It was a loot box for cybercriminals.
The impact of Heartbleed cannot be overstated. It wasn't just that data leaked; it was what data leaked. The primary goal of TLS is to protect the confidentiality and integrity of data. Heartbleed completely bypassed the confidentiality.
The Crown Jewels:
Because the attack occurs before any application-level logging, there was often no way for administrators to know if they had been compromised. The only safe assumption was that everything was compromised. This led to a massive revocation event, forcing Certificate Authorities to work overtime reissuing certificates for millions of domains.
If you are reading this and still running OpenSSL 1.0.1f, stop reading and go update. The remediation path is well-trodden but requires thoroughness.
Step 1: Patch
Upgrade to OpenSSL 1.0.1g or later. Most modern OS distributions have long since backported this fix. If you are on an embedded device, check with the vendor. If you compile from source and cannot upgrade, compile with -DOPENSSL_NO_HEARTBEATS to compile out the vulnerable code entirely.
Step 2: Revoke and Reissue This is the painful part. Patching the binary stops the leak, but it doesn't fix the fact that your keys might be in a hacker's database. You must:
Step 3: Cycle Credentials Assume all active session tokens and user passwords processed by the vulnerable server have been leaked. Force password resets and invalidate all session cookies.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenSSL OpenSSL Software Foundation | 1.0.1 - 1.0.1f | 1.0.1g |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-126 (Buffer Over-read) |
| CVSS Score | 7.5 (High) |
| Attack Vector | Network |
| EPSS Score | 94.47% |
| Exploit Status | Active / Weaponized |
| Impact | Information Disclosure (Critical) |
The software reads from a buffer using length parameters that attacker can control, allowing access to memory outside the intended buffer.
Get the latest CVE analysis reports delivered to your inbox.