cybersecurity · advanced · ~15 min · safe pentest lab
Recognise the ECB-mode ciphertext fingerprint (repeated plaintext -> repeated ciphertext blocks) and write a bounds-safe detector that validates attacker-controlled length and block-size arguments before indexing, denying malformed input by default.
AES in ECB mode encrypts every plaintext block independently, so identical plaintext blocks always produce identical ciphertext blocks. That determinism is a leak: an attacker who can see the ciphertext learns which plaintext regions repeat (the classic "ECB penguin"). During a defensive assessment you want a detector that flags this fingerprint in captured ciphertext so you can tell a developer their crypto is broken.
Asset: a fixed buffer of captured ciphertext baked into the test harness. Threat: confidentiality loss from block-level plaintext structure leaking through ECB. Your detector only reads that in-memory array — never a live target, socket, or file.
Implement has_ecb_repeats(ct, n, block). Return 1 if any two whole block-sized chunks of ct are byte-for-byte identical, otherwise 0.
The dangerous, insecure assumption to avoid is trusting the caller's sizes. You must deny by default: reject block == 0 and a NULL pointer, and only ever read bytes inside [0, (n / block) * block). A trailing partial block (fewer than block bytes) is ignored, never compared.
block == 0 → return 0 (never divide or index by it).ct == NULL → return 0.n / block < 2) → return 0.n.has_ecb_repeats(repeat, 16, 4) -> 1 /* blocks 0 and 2 are identical */
has_ecb_repeats(distinct, 16, 4) -> 0 /* every block differs */
A pointer ct to n bytes of ciphertext, and a chunk size block. All three are supplied by the (untrusted) caller; sizes may be zero or larger than the buffer's usable region.
Return int 1 if at least two whole block-sized chunks of ct are identical, else 0. Invalid inputs (block==0, NULL) return 0.
C11, no dynamic allocation. Compare only whole blocks: read strictly within [0, (n/block)*block). block may be 0 and ct may be NULL — handle both without undefined behaviour. Do not modify ct.
int has_ecb_repeats(const unsigned char *ct, size_t n, size_t block)
{
/* TODO: detect two identical `block`-sized chunks in ct.
This insecure stub trusts the input and reports nothing. */
(void)ct; (void)n; (void)block;
return -1;
}Computing the block count as (n + block - 1) / block or otherwise rounding up, which reads a partial block past valid data; looping while i*block < n so the final compare overruns the buffer; forgetting to check block == 0 and hitting a division/modulo by zero; dereferencing ct without a NULL guard; using == on pointers or comparing only the first byte instead of memcmp over the whole block; returning a truthy non-1 value like -1 that the grader treats as wrong.
block == 0 (must not divide/index by zero); ct == NULL; n == 0 (empty buffer); n smaller than one block; exactly one whole block (no pair to compare); n not a multiple of block (trailing partial block must be ignored, not read); the minimal positive case of two equal 1-byte blocks.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.