
Self-contained Heartbleed (CVE-2014-0160) lab: builds vulnerable OpenSSL 1.0.1f in Docker and includes a Python memory-leak PoC for authorised testing.
A small, self-contained lab for CVE-2014-0160 (Heartbleed) — the TLS heartbeat over-read in OpenSSL 1.0.1–1.0.1f. It builds the genuinely vulnerable OpenSSL from pinned upstream source in a throwaway container, serves it, and includes a proof-of-concept client that leaks live process memory from it.
Authorised lab use only. Everything here targets
localhost/ a container you run yourself. Do not point the PoC at any host you don't own and have explicit permission to test — unauthorised use of Heartbleed against live systems is a crime in most jurisdictions. Heartbleed was fixed in OpenSSL 1.0.1g (April 2014); this lab exists to understand the bug, not to attack anyone.
| Path | What it is |
|---|---|
Dockerfile | Builds OpenSSL 1.0.1f from checksum-pinned upstream source with heartbeats enabled, generates a throwaway cert, and runs openssl s_server — the real vulnerable server. |
exploit/heartbleed.py | Python 3 PoC. Sends a malformed heartbeat and hexdumps the memory the server leaks back. Exits 0 if the target is vulnerable, 1 if patched. |
demo/server.py | A naive simulation in pure Python — no OpenSSL involved. It blindly echoes 64 KB and leaks nothing real; kept only to show the shape of an over-read reply. |
demo/gen-cert.sh | Regenerates the throwaway localhost cert for the demo. |
No private keys are committed — certs are generated locally (see .gitignore).
# 1. Build and start the vulnerable server (needs Docker)
docker build -t heartbleed-lab .
docker run --rm -p 8443:8443 heartbleed-lab
# 2. In another terminal, bleed it
python3 exploit/heartbleed.py 127.0.0.1 -p 8443
A vulnerable server prints a hexdump of leaked memory. Rerun the PoC a few times — each request returns a different slice of the heap, which is exactly why Heartbleed was so dangerous: session cookies, form data, and private key material all live there.
cd demo
./gen-cert.sh
python3 server.py
This is not the CVE — it's a teaching stub that always replies with 64 KB of As.
The TLS heartbeat request carries a payload plus a length field. Vulnerable OpenSSL
trusts the attacker-supplied length and memcpys that many bytes from the request
buffer into the response — but the request never contained that much data, so the
copy reads past it into whatever adjacent process memory holds. The fix in 1.0.1g is
a bounds check: if (1 + 2 + payload + 16 > s->s3->rrec.length) return 0; — silently
drop any heartbeat that claims more than was actually sent.
-DOPENSSL_NO_HEARTBEATS.