What Was Heartbleed? The Bug That Asked for One Byte and Got Sixty-Four Kilobytes

The request was legal. That is the part worth sitting with.

A TLS heartbeat is a keep-alive. One side sends a small payload and states how many bytes it is; the other side sends the same payload back. It exists so a connection can be held open without renegotiating it. It is specified in RFC 6520, and it is about as boring as a protocol feature gets.

OpenSSL took the sender's word for the length.

So you could send one byte, tell the server it was 65,535 bytes, and the server would hand back your one byte followed by 65,534 bytes of whatever happened to be sitting next to it in memory. Session cookies. Form contents. Credentials in the clear. Sometimes the server's own private key.

No exploit code. No malformed packet. No crash, no error, nothing written to any log. You asked a well-formed question and the server answered it honestly.

What was Heartbleed?

Heartbleed — CVE-2014-0160 — was a missing bounds check in OpenSSL's implementation of the TLS and DTLS Heartbeat Extension. It was disclosed publicly on 7 April 2014 and affected OpenSSL 1.0.1 through 1.0.1f. Version 1.0.1g, released the same day, fixed it.

It is filed almost everywhere as a flaw in SSL, or in TLS, or in encryption itself. It was none of those. TLS was fine. The Heartbeat Extension was fine. One library's implementation of one optional extension was not — and that library was sitting underneath most of the encrypted web.

It was also the first vulnerability to arrive with a brand. Codenomicon, one of the two parties who found it, gave it the name, a dedicated website, and a logo of a bleeding heart. Every named-and-logoed bug since — Shellshock, POODLE, Log4Shell — is downstream of that decision.

The bug, in one line

A heartbeat request carries a type byte, a two-byte payload length, the payload itself, and some padding. OpenSSL read the claimed length out of the packet, allocated a response buffer of that size, and then did this:

memcpy(bp, pl, payload);

payload is the length the client claimed. pl is a pointer to the data the client actually sent. Nothing in that line, or anywhere near it, compared the two.

Because the length field is sixteen bits, the ceiling is 65,535 bytes — roughly 64KB per request. And the request could be repeated indefinitely, from anyone, before or without authentication, each time returning a different slice of process memory as the server did other work.

The fix was a length check that silently discards any heartbeat whose claimed payload does not fit inside the record that carried it. Three lines.

Two years and twenty-four days

The heartbeat code was written by Robin Seggelmann and submitted on 31 December 2011. It was reviewed by an OpenSSL core developer and merged. It shipped to the world in OpenSSL 1.0.1 on 14 March 2012.

It was disclosed on 7 April 2014. Two years and twenty-four days in production, in the default build, on a large fraction of the internet's servers.

It was found twice, independently and within days of each other: by Neel Mehta of Google Security, and by a team at the Finnish firm Codenomicon who hit it while testing their own fuzzing product against their own servers.

The commit was reviewed before it shipped. Two people looked at that line and neither saw it, and then nobody else saw it for two years, in the most security-scrutinised open source library in the world.

Could you actually get the private key?

This was genuinely disputed in the first days. The initial reading was that private keys were unlikely to be recoverable in practice — that the key material would not usually be sitting in the 64KB windows an attacker could reach.

On 11 April 2014 Cloudflare settled it by putting up a deliberately vulnerable server and inviting the internet to try. Two people — Fedor Indutny and Ilkka Mattila — pulled the private key out of it, the first within about nine hours of the challenge going up. Indutny reportedly sent on the order of two and a half million requests to do it.

That result is what turned Heartbleed from a patching problem into a trust problem. Patching the library closed the hole. It did nothing about any key that had already walked out, and because exploitation left no trace, nobody could prove what had or had not been taken. Every affected certificate had to be treated as compromised: reissued, and the old one revoked. The revocation wave that followed was large enough to be visible in the size of certificate revocation lists across the web.

How much of the web?

The number quoted everywhere is two-thirds. It is worth knowing what it actually refers to.

Netcraft's April 2014 survey put roughly 66% of active sites on Apache or nginx, both of which typically link against OpenSSL. That is the origin of "two-thirds of the web." It is a statement about which web server software was popular, not a count of vulnerable machines.

The narrower figure is the useful one. Netcraft found that about 17.5% of SSL sites — on the order of half a million trusted certificates — were running a vulnerable OpenSSL version with the heartbeat extension actually enabled. Plenty of servers ran an affected version but had never negotiated a heartbeat, and were never exposed.

Half a million certificates is a smaller number than two-thirds of the internet, and it is still one of the largest simultaneous key-rotation events that has ever happened.

It was scored 5.0 out of 10

The National Vulnerability Database's original CVSS v2 base score for CVE-2014-0160 was 5.0. Medium.

Under CVSS v2 the scoring was defensible on its own terms: no integrity impact, no availability impact, and confidentiality impact rated "partial" because the attacker does not choose what they read. The framework had no way to express partial, but repeatable, unauthenticated, untraceable, and occasionally the private key.

Rescored under CVSS v3.1 it comes out at 7.5. The bug did not change. The scale did.

If you have ever had an argument with someone about whether a CVSS score should drive your patching priority, this is the case to reach for.

The library under two-thirds of the web had one full-time developer

This is the part that actually mattered afterwards.

At the time of the disclosure, the OpenSSL Software Foundation was taking in on the order of two thousand dollars a year in outright donations. The project had a very small core team and, effectively, one full-time person. Enormous parts of the global financial, medical and governmental internet were resting on a volunteer codebase funded like a hobby project.

Nobody had decided this. It accumulated. Every organisation using OpenSSL assumed somebody else was funding it, and collectively they were all correct that somebody should have been.

Three things came out of that realisation:

  • The Linux Foundation announced the Core Infrastructure Initiative on 24 April 2014 — Amazon, Cisco, Dell, Facebook, Fujitsu, Google, IBM, Intel, Microsoft, NetApp, Qualcomm, Rackspace and VMware each committing funding to underwrite critical open source projects.
  • The OpenBSD project forked OpenSSL into LibreSSL on 11 April 2014, four days after disclosure, and began deleting code aggressively.
  • Google started its own fork, BoringSSL, later that year.

The bug was three lines. The finding was that the world's cryptographic plumbing had no owner.

What it actually cost people

Most Heartbleed exploitation is unmeasurable by definition — that was the whole problem. Two cases are on the record.

The Canada Revenue Agency confirmed that roughly 900 social insurance numbers were taken through Heartbleed during the 2014 tax filing season. The agency took its filing systems offline and extended the deadline. An arrest followed within weeks, which makes this one of the very few Heartbleed incidents with a named defendant.

The breach at Community Health Systems in August 2014 — around 4.5 million patient records — was attributed by the incident responders to Heartbleed against a vulnerable network appliance, rather than a web server. It is a useful reminder that OpenSSL was embedded in a great deal of hardware that nobody thought of as "a server" and that many organisations had no process for patching at all.

What is still true

The library is patched everywhere that anyone is looking. Internet-wide scans still turn up vulnerable hosts more than a decade on, and they are almost entirely embedded devices and forgotten appliances — the same category as the Community Health Systems box.

The durable lesson is not "patch faster." It is that a memory-disclosure bug with no logging is unfalsifiable after the fact: you cannot investigate it, you can only assume the worst and rotate everything. That property, more than the byte count, is why April 2014 was as expensive as it was.

Further reading

  • Codenomicon — heartbleed.com. The original disclosure site, written the week it broke.
  • OpenSSL Security Advisory, 7 April 2014, and the 1.0.1g release notes.
  • Cloudflare — The Results of the CloudFlare Challenge (April 2014), on private key extraction.
  • Netcraft — Half a million widely trusted websites vulnerable to Heartbleed bug (8 April 2014), for the exposure numbers.
  • Durumeric et al. — The Matter of Heartbleed (IMC 2014). The measurement paper: who was vulnerable, who patched, and how fast.

EXHIBIT 0006 — HEARTBLEED

We put it on a shirt. An anatomical heart, opened as a technical cutaway, with its chambers drawn as a memory buffer — and the contents spilling out of the bottom right, past the end of the buffer, into whatever was next to it.

The callouts are the bug: PAYLOAD_LENGTH = 65535 against ACTUAL PAYLOAD = 1 BYTE. Most people will read it as an anatomical drawing. A smaller number will follow the hex past B7 and notice it keeps going.

That's the whole idea.

View the Heartbleed shirt — EXHIBIT 0006

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.