The Challenge
A web endpoint accepts a POST with a flag guess. The server takes noticeably longer when more leading characters of the guess match the real flag — a classic timing oracle. There is no output other than a correct/incorrect response.
Approach
For each position i in the flag:
- Try every printable ASCII character
cappended to the known prefix. - Pad the guess to a fixed total length (say 5 characters beyond the known prefix) to control the response time baseline.
- Measure the round-trip elapsed time.
- The candidate whose elapsed time exceeds
1 + len(known_prefix)seconds (or is the maximum by a clear margin) is the correct character.
Once a character is confirmed, append it to the known prefix and repeat.
Solution
|
|
The threshold 1 + len(flag) corresponds to the server sleeping 1 second per correct prefix character (the challenge’s timing mechanism). When a character’s elapsed time crosses this threshold it is immediately accepted. If no threshold is crossed, the character with the maximum elapsed time is chosen.
What I Learned
Timing side-channels are real even over the internet — network jitter averages out over repeated measurements. The mitigation is constant-time comparison: hmac.compare_digest() in Python compares the full string regardless of where the first difference occurs, producing a fixed-length execution path with no timing signal.