Polished CTF Writeup

HTB Wonky AES

An Insane crypto challenge requiring AES Differential Fault Analysis to recover a live service key and decrypt the encrypted flag.

Category
Crypto
Difficulty
Insane
Technique
AES Differential Fault Analysis
Endpoint
154.57.164.83:31929
Recovered Flag
HTB{fcdd3d2cacd358595dc55f8eff000f0c}

Overview

Wonky AES provided source code for an AES encryptor and hinted that something about the implementation was “wonky.” The vulnerability was not in AES itself, but in the deliberate exposure of correct and faulted encryptions under the same random key.

The solve required recovering the AES-128 key through Differential Fault Analysis, then decrypting the final encrypted flag from the remote service.

Source Review

The archive contained:

crypto_wonky_aes/
  enc_fault
  src/aes.c
  src/aes.h
  src/enc_fault.c
  src/Makefile

The service offered repeated encryption queries. For each query, it revealed:

Correct encryption: AES_K(P)
Faulty encryption:  AES_K_faulted(P)

At the end, it printed an encrypted flag under the same AES key.

Fault Model

The vulnerable code injected a single-byte fault during AES encryption:

if (is_fault && round == Nr - 1) {
  (*state)[pos % 4][pos / 4] ^= fault;
}

This means:

  • The same unknown AES-128 key is reused.
  • The attacker receives correct and faulty ciphertexts for random plaintexts.
  • The fault is injected before the final full round structure completes.
  • A single byte fault propagates through MixColumns into a four-byte ciphertext difference group.
Cryptanalytic opening: correct/faulty ciphertext pairs leak enough information to recover the final round key column by column.

DFA Attack

The attack works backward through the final AES round. For each correct/faulty ciphertext pair:

  1. Identify which four ciphertext bytes differ.
  2. Map those positions through the final ShiftRows layout.
  3. Guess four bytes of the last round key for the affected group.
  4. Apply inverse S-box relationships to test whether the observed difference is consistent with a one-byte fault before MixColumns.
  5. Intersect candidates across many samples until a unique round-10 key remains.

Once the full AES round-10 key is recovered, AES-128 key schedule inversion gives the original encryption key.

The main implementation pitfalls were:

  • tiny-AES stores state as state[column][row].
  • Fault positions and ciphertext byte indexes must be grouped in AES state order, not simple ascending output order.
  • Recovering the last round key is only half the job; the key schedule must be inverted to recover round zero.

Solver Implementation

A reusable solver was written at:

binary-intelligence-workbench/samples/htb-wonky-aes/solve_wonky_aes.py

The solver supports both local and remote modes:

python3 solve_wonky_aes.py --local crypto_wonky_aes/enc_fault --samples 200
python3 solve_wonky_aes.py --remote HOST PORT --samples 200

The local mode was validated using a dummy flag.txt, proving the solver could recover the generated AES key and decrypt the dummy encrypted flag before attacking the live endpoint.

Remote Exploitation

The challenge endpoint was:

154.57.164.83:31929

The verified solver was run as:

python3 solve_wonky_aes.py --remote 154.57.164.83 31929 --samples 200

Recovered live values:

encrypted flag:
44aaa94e9989d75744c578895b2da1929d46be680fbd67df936df1325a6bb7ffd13ae97e286e07de9bbd837dd0fdd4f8

AES key:
e791d80555fdc500377da7ac22463a06

round10 key:
398cc3a202e610dc249b7818de260ba2

Verification

Decrypting the encrypted flag with the recovered AES key produced:

b'HTB{fcdd3d2cacd358595dc55f8eff000f0c}'

After stripping null padding:

HTB{fcdd3d2cacd358595dc55f8eff000f0c}

Lessons Learned

  • Source availability did not make the challenge easy; the solver still required specialist AES DFA knowledge.
  • State layout mistakes can completely invalidate a correct cryptanalytic idea.
  • The correct/faulty pair oracle is devastating when the fault location is late and the key is reused.
  • A live remote flag can be recovered without brute forcing the key or breaking AES itself.