In late 2017, three researchers signed a message with Facebook’s private key. They didn’t steal it. They didn’t need to. They used a bug from 1998 that everyone believed had been fixed two decades earlier, pointed it at facebook.com’s TLS server, and made the server perform a signing operation it should never have performed for a stranger. Then they did it to PayPal-class hosts too, and published a paper.
The bug had a name the first time around: Bleichenbacher’s attack. The second time it got a better one — ROBOT, Return Of Bleichenbacher’s Oracle Threat. The joke in the acronym is the whole story. This wasn’t a new flaw. It was the same flaw, wearing the same clothes, walking back in through a door everyone thought they’d locked.
What Bleichenbacher found in 1998
Daniel Bleichenbacher’s original attack targeted RSA as TLS used it for key exchange, specifically the PKCS#1 v1.5 padding scheme. When a client wanted to set up an old-style RSA session, it encrypted a secret with the server’s public key and sent it over. The server decrypted it and checked whether the padding looked right.
That check was the mistake. Not the math — the reaction. A server that behaved even slightly differently for correctly-padded versus incorrectly-padded ciphertexts was leaking one bit of information per guess: was the padding valid? An attacker who could see that difference could send thousands of carefully mangled versions of a captured ciphertext, watch which ones the server “accepted,” and use each yes-or-no to peel the encryption apart one step at a time. This is a padding oracle: the server, without meaning to, answers a question it thinks it’s refusing to answer.
The original needed on the order of a million such queries to recover a single session key, which is why it’s sometimes called the million-message attack. A million requests to a server is not exotic. It’s an afternoon.
The fix that wasn’t a fix
Here’s the part worth sitting with. The response to Bleichenbacher was not to stop using the broken padding. RSA key exchange with PKCS#1 v1.5 stayed in TLS for another two decades. Instead, the standard prescribed a countermeasure: make the server behave identically whether the padding is valid or not. When decryption fails, don’t send an error — generate a random secret and continue as if nothing were wrong, so the handshake fails later, generically, in a way that looks the same as a hundred other failures.
If you implement that perfectly, the oracle goes silent. The attacker’s thousands of probes all come back looking the same, and the one leaked bit disappears.
The word doing all the work there is perfectly. The countermeasure doesn’t remove the vulnerability; it hides it behind a promise that your server will be indistinguishable across two code paths. And “indistinguishable” is a brutally high bar. Any observable difference reopens the oracle: a different TLS alert number, a connection that resets instead of timing out, an error that arrives a few milliseconds sooner, a subtly different response to a malformed message. The attacker doesn’t care which signal leaks. Any one will do.
2017: it never actually left
Hanno Böck, Juraj Somorovsky, and Craig Young did something nobody had bothered to do at scale: they went and checked. They took the 1998 attack, updated it for modern implementations, and pointed it at the real internet.
It worked. Roughly a third of the top 100 domains were vulnerable, and the affected code came from a who’s-who of infrastructure vendors — F5, Citrix, Radware, Cisco, and others whose load balancers and TLS terminators sit in front of an enormous slice of the web. These weren’t obscure servers running abandoned software. These were the boxes protecting the biggest sites online.
Every one of them had, in principle, the countermeasure. Every one of them leaked a distinguishable signal somewhere the original authors of the fix hadn’t anticipated. Nineteen years of everyone believing the problem was solved, and the problem was sitting there the whole time, because the “solution” was a tightrope walk that too many implementations quietly fell off.
The lesson is about workarounds, not RSA
It would be easy to file this under “RSA is bad” and move on. That’s the wrong lesson. The right one is about the difference between fixing a flaw and papering over it.
A patch that says “keep doing the dangerous thing, but be careful” is not a fix. It’s a debt with interest. Every new implementation, every refactor, every performance tweak that changes an error path is a fresh chance to reintroduce the exact bug the workaround was hiding. The bug doesn’t need to be rediscovered by an attacker; it needs to be reconstructed by a well-meaning developer who moves an error return three lines up. And because everyone believes the issue was settled long ago, nobody’s looking.
We keep learning this the hard way. regreSSHion was a 2006 OpenSSH bug that a later change quietly resurrected. ROBOT was a 1998 attack that survived because its fix was a behavioral promise no one could keep across dozens of independent codebases. The pattern is identical: a dangerous primitive kept alive by a fragile mitigation, waiting for entropy to undo the careful part.
The thing that actually killed ROBOT wasn’t a better workaround. It was removal. TLS 1.3 dropped RSA key exchange entirely — there is no static-RSA handshake left to attack, only forward-secret ephemeral key agreement (ECDHE and its kin), which never asks the server to decrypt a client-chosen ciphertext and so has no oracle to leak. The researchers’ recommendation was blunt and correct: deprecate RSA encryption key exchange and stop carrying PKCS#1 v1.5 forward.
Sometimes the only way to fix a bug is to delete the thing it lives in. A workaround buys you time. It does not buy you safety, and the meter is always running.