Flash CTF – Electronic Christmas Book

Clues

The title is a dead giveaway: Electronic Christmas Book -> ECB.

We were given nice_list_screenshot.bmp.enc. I checked the file size first: 6,220,816 bytes. We know that the image was a screenshot, so if we enumerate common screen sizes, we’ll find that 1080p is a highly likely resolution for the image. If we assume a standard 24-bit RGB bitmap: 1920×1080×3 bytes=6,220,800 bytes1920×1080×3 bytes=6,220,800 bytes.

That’s almost a perfect match. The extra bytes are the bmp header and padding bytes.

The Problem

The challenge encrypted the entire file, including the original BMP header. Because the header is now ciphertext, image viewers won’t recognize the file.

AES works in 16-byte blocks. The original BMP header is 54 bytes. 54÷16=3.37554÷16=3.375. This means the header occupies the first 4 blocks (16×4=6416×4=64 bytes). To get to the clean pixel data, we have to throw away the first 64 bytes of the encrypted file.

3. The Exploit: Pattern Leakage

Why does this work without the key? In Electronic Codebook (ECB) mode, every identical 16-byte block of plaintext produces the exact same 16-byte block of ciphertext.

Since the “Nice List” UI has big chunks of solid colors (solid background, solid header), those identical pixel blocks turn into repeating patterns of “static.” The text, being a different color, creates a different repeating pattern. Even though we can’t see the original colors, the human eye can easily distinguish the shapes of the letters.

Solve Script

import struct

# Standard 1080p dimensions
w, h = 1920, 1080
pixel_size = w * h * 3

# Build a basic 54-byte BMP header
# Header (14 bytes) + DIB Header (40 bytes)
fmt = '<2sIHHI IiiHHIIIIII'
header = struct.pack(fmt, b'BM', 54 + pixel_size, 0, 0, 54, 40, w, h, 1, 24, 0, pixel_size, 0, 0, 0, 0)

with open('nice_list_screenshot.bmp.enc', 'rb') as f:
    data = f.read()

# Skip the first 64 bytes of ciphertext to align the blocks
pixels = data[64:]

with open('solve.bmp', 'wb') as f:
    f.write(header + pixels[:pixel_size])

Final Result

Opening solve.bmp gives a clearly corrupted image, but the “NICE-LIST-V3.0” header and the table rows were clear as day. Looking at the last entry in the list reveals the flag.