← Back to Writeups
CUSTOMEasyCrypto

Закодированный дед

XESXOR9/10/20262 min read
#crypto#custom#easy#bmp_header_analysis#known_plaintext_attack#xor_key_recovery#linux

Закодированный дед

Platform: bug-makers | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-27 | Status: Solved Techniques: bmp_header_analysis, known_plaintext_attack, xor_key_recovery

Summary

Task: XOR-encrypted BMP image with encryption script provided but key lost; key is 10 bytes. Solution: known-plaintext attack using predictable BMP header bytes to recover the XOR key 'deadencode', then decrypt the entire file.

Закодированный дед — Bug Makers CTF

Description

У меня есть картинка в старом формате BMP и код, которым ее закодировали. Но ключ от шифра уже утерян. Помогите расшифровать картинку.

English summary: We are given an XOR-encrypted BMP image (encrypted.bmp, 3,072,138 bytes) and the Python encryption script (encrypt.py). The encryption key has been lost and must be recovered to decrypt the image.

Analysis

The encryption script performs simple repeating-key XOR on the entire BMP file starting from byte 0 (including the header). Key observations: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  1. Key length is 10 bytes — confirmed by the default placeholder 'xxxxxxxxxx' (10 x's) and the comment "ключ-длиной-10-байт" ("key of 10 bytes length").

  2. Entire file is XOR'd — start_index = 0, so the BMP header is encrypted too. This is the critical weakness: BMP headers have a well-known, predictable structure.

  3. BMP header provides known plaintext — the first 10 bytes of any BMP file are deterministic:

    • Bytes 0–1: Magic signature BM → 0x42 0x4D
    • Bytes 2–5: File size in little-endian → 3,072,138 = 0x002EE08A → 8A E0 2E 00
    • Bytes 6–9: Reserved fields, always zero → 00 00 00 00 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Since the key is exactly 10 bytes and we know 10 bytes of plaintext at offset 0, we can recover the entire key with a single XOR operation.

Solution

Step 1: Extract known plaintext from BMP header

The first 10 bytes of the original (unencrypted) BMP must be:

Known plaintext: 42 4D 8A E0 2E 00 00 00 00 00

Step 2: Read encrypted header bytes

bash
xxd -l 10 encrypted.bmp
# Output: 26 28 EB 84 4B 6E 63 6F 64 65

Step 3: Recover the XOR key

Encrypted:  26 28 EB 84 4B 6E 63 6F 64 65
Plaintext:  42 4D 8A E0 2E 00 00 00 00 00
XOR result: 64 65 61 64 65 6E 63 6F 64 65

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Converting to ASCII: deadencode — the key is a readable English word, a play on "dead" + "encode".

Step 4: Decrypt the file

python
#!/usr/bin/env python3
key = b'deadencode'
with open('encrypted.bmp', 'rb') as f:
    data = bytearray(f.read())
for i in range(len(data)):
    data[i] ^= key[i % len(key)]
with open('decrypted.bmp', 'wb') as f:
    f.write(data)
print("Decrypted successfully!")

Step 5: View the result

The decrypted BMP (1280×800, 24-bit color) shows a Zoolander meme with Russian text "У нас в семье нет программистов" / "Закодированный дед" and the flag at the bottom. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍