Section 8: Attacks & Vulnerabilities

Here is the uncomfortable truth of applied cryptography: attackers almost never break the math. AES has withstood a quarter century of open cryptanalysis; RSA with proper key sizes remains unfactorable. Yet cryptographic systems are broken constantly — because attackers target the implementation (timing leaks, error messages, memory bugs), the protocol (replayable messages, unauthenticated key exchange), or the operator (reused nonces, homemade padding checks). As Bruce Schneier put it: attacks never get worse — they only ever get better. A weakness that looks merely theoretical today is a point-and-click exploit in five years.

This section explains how the classic breaks work — conceptually, so you can defend against them. There is no exploit code here; every subsection ends with the defense that neutralizes the attack.

Threat models: what the attacker sees

Cryptanalysis is classified by attacker access, from weakest to strongest:

ModelAttacker hasExample
Ciphertext-onlyIntercepted ciphertext onlyFrequency analysis on classical ciphers
Known-plaintextSome plaintext/ciphertext pairsBreaking Enigma via predictable weather reports
Chosen-plaintextCan encrypt messages of choiceAny public-key system grants this for free
Chosen-ciphertextCan submit ciphertexts, observe decryption behaviorPadding oracle attacks

Modern ciphers are built to survive even a chosen-ciphertext attacker. Broken systems usually hand the attacker a stronger position than the designers assumed.

Padding oracle attacks

Recall from Section 4: CBC mode pads plaintext to the block size with PKCS#7 (pad with n bytes each of value n). On decryption, the receiver checks the padding is well-formed.

The trap: if a server behaves differently when padding is invalid versus valid-but-otherwise-wrong — a different error string, HTTP status, or even response time — that single leaked bit ("was the padding valid?") turns it into a decryption oracle.

The mechanism, at a high level: in CBC, P[i] = Decrypt(C[i]) XOR C[i-1]. The attacker controls the previous ciphertext block C[i-1], so altering one of its bytes changes the same byte of decrypted P[i] predictably. By cycling the last byte of C[i-1] through its 256 values and watching for the "valid padding" response (which means the byte became 0x01), XOR algebra reveals the true plaintext byte. Move to the next byte, targeting padding 0x02 0x02, and so on — around 128 queries per byte, decrypting the message without ever knowing the key.

C[i-1]  (attacker tweaks bytes here)
  |  XOR
  v
Decrypt(C[i]) --> P[i]   server checks padding of P[i]
                          "valid" / "invalid"  <-- the leaked bit
One leaked bit per query is enough to peel the plaintext apart.

Real incidents: Lucky 13 (2013, timing variant), POODLE (2014, forced SSL 3.0 downgrade, hastened SSL 3 retirement), and the ASP.NET padding oracle (2010, MS10-070, allowed decryption of view-state and forms auth tickets).

Defense
Use authenticated encryption (AES-GCM, ChaCha20-Poly1305) so tampered ciphertext is rejected before any padding logic runs. If you must use CBC, apply encrypt-then-MAC and verify the MAC in constant time before decrypting. Return uniform errors and uniform timing regardless of failure cause.

Timing attacks

Cryptographic secrets can leak through how long an operation takes. The classic case is a byte-by-byte comparison that returns early on the first mismatch: a guess sharing more leading bytes with the secret takes measurably longer to reject, so an attacker can recover a MAC or token one byte at a time by timing responses. Paul Kocher formalized this in 1996 against RSA and Diffie-Hellman; later work showed it is viable even over a network.

Timing leak simulator

A secret token lives in this page. The naive compare below stops at the first wrong character, so its simulated time grows with each correct leading character — exactly the leak that lets an attacker home in. This is a teaching simulation (times are modeled, not measured).

time will appear here
Defense
Compare secrets with a constant-time function that always examines every byte (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). A common trick: HMAC both values under a random key and compare the HMACs, so absolute values never leak through timing.

Replay attacks

A replay attack captures a valid message and resends it later. The cryptography is untouched — the message was genuinely signed or encrypted — but replaying it triggers an unintended repeat: re-opening a car with a captured key-fob code, re-submitting a captured payment request, or reusing a captured authentication token.

Alice --[ valid signed request ]--> Server   (legit)
Eve   captures the bytes
Eve   --[ same bytes, later ]-----> Server   (replayed!)
Nothing is decrypted or forged — the same valid message is simply reused.
Defense
Bind each message to a one-time context: a fresh nonce the server records and rejects on reuse, a timestamp with a tight acceptance window, a monotonic sequence number, or a server-issued challenge (challenge-response). TLS 1.3 0-RTT data is replayable by design — treat it as such.

Man-in-the-middle (MITM)

If a key exchange is not authenticated, an active attacker sitting between the parties can run two separate exchanges — one with each side — and relay (and read) everything. Plain Diffie-Hellman is vulnerable exactly here.

Alice <==DH==> Mallory <==DH==> Bob
        shared        shared
        key A         key B
Mallory decrypts with A, re-encrypts with B — invisible to both.
Unauthenticated key exchange lets the attacker be a transparent relay.

Common vantage points: rogue Wi-Fi access points, ARP spoofing on a LAN, or BGP hijacking at the network level. The fix is authentication — certificates and the PKI from Section 6 prove the far end really is who it claims. Superfish (2015) is a cautionary tale: Lenovo shipped laptops with a MITM root CA whose private key was extractable, letting anyone forge trusted certificates for those machines. Certificate pinning adds a second line of defense for high-value apps.

Nonce reuse

A nonce is a "number used once" — and the contract is strict. Break it and strong crypto collapses:

Why it is catastrophic
Nonce reuse does not merely weaken security — it can hand over the plaintext or the private key outright. There is often no graceful degradation.
Defense
Generate GCM nonces as random 96-bit values or a strict monotonic counter you never repeat under a given key; rotate keys before the counter could wrap. For signatures, use deterministic ECDSA (RFC 6979) or Ed25519, which derive the nonce from the key and message so it cannot accidentally repeat. Large-nonce constructions like XChaCha20 make random collisions negligible.

Side-channel attacks

Side channels leak secrets through physical or micro-architectural behavior rather than the algorithm itself:

ChannelWhat leaks
Power analysis (SPA/DPA)Power draw correlates with key bits — devastating against smartcards
ElectromagneticEM emanations mirror internal computation
Cache timingData-dependent memory access (e.g. AES T-tables) leaks via cache hits/misses; Spectre/Meltdown are cousins
Acoustic / opticalSound or LED flicker during computation
Defense
Use constant-time, data-independent implementations (or AES-NI hardware instructions that avoid table lookups), apply blinding to public-key operations, and rely on hardware countermeasures for physical-access threats. This is a strong reason to use vetted libraries rather than hand-rolled crypto.

The "common mistakes" hall of shame

Every entry below is a real breach caused not by weak math but by misuse:

The defensive checklist
Prefer AEAD (GCM / ChaCha20-Poly1305). Never reuse a nonce under a key. Use vetted libraries, never your own primitives. Compare secrets in constant time. Return uniform errors. Hash passwords with a slow KDF (Argon2id / bcrypt / scrypt) plus a salt. Keep TLS current (1.2+/1.3) and disable legacy suites. Seed RNGs from the OS CSPRNG.

Best learning resources

videoComputerphile — Heartbleed
Clear walkthrough of how a memory-safety bug leaked private keys.
courseCryptopals — Sets 3 & 4
Hands-on: implement (defensively) a padding oracle and CTR/CBC attacks to truly understand them.
articleOWASP A02:2021 — Cryptographic Failures
The canonical checklist of real-world crypto mistakes and how to avoid them.
articleROBOT Attack (Bleichenbacher, revisited)
How a 1998 RSA padding flaw resurfaced in modern TLS stacks.
bookSerious Cryptography — JP Aumasson
Excellent chapters on real attacks and how to build systems that resist them.
articleSHAttered
The first practical SHA-1 collision — a concrete lesson in retiring broken primitives.