BMP с монологом Гамлета: ключ в байтах выравнивания
Маленький BMP 22 × 34 с палитрой. Выглядит как разноцветный шум.
- Платформа
- CTF (флаги вида DUCKERZ{…})
- Носитель
- BMP, 8 бит, палитра 256 цветов
- Приём
- ключ-алфавит в байтах выравнивания строк, текст — индексами в пикселях
- Инструменты
xxd, Python- Статус
- решено
Проверка заголовка: подмен нет
| Проверка | Расчёт | |
|---|---|---|
| строка пикселей | 22 байта → выравнивание до 24 (кратно 4) | 2 лишних байта на строку |
| размер данных | 24 × 34 = 816 | ✓ как в заголовке |
| смещение пикселей | 54 + 256 × 4 = 1078 | ✓ |
| размер файла | 1078 + 816 = 1894 | ✓, хвоста нет |
Что подозрительно
- Случайная палитра (
ca3d59,e9d841,047f67…). В нормальной 8-битной картинке палитра упорядочена. Случайная означает, что автору было всё равно, как картинка выглядит: смысл в номерах цветов, а не в цветах. - Картинка крошечная: 748 пикселей — ровно размер короткого текста.
- 2 байта выравнивания × 34 строки = 68 «ничейных» байт, которые просмотрщик никогда не показывает. В ASCII-колонке
xxdкаждые 24 байта видны парыab,cd,ef…
Скрипт: отделить пиксели от выравнивания
import sys
d = open(sys.argv[1], 'rb').read()
off, w, h = 1078, 22, 34
row = (w + 3) // 4 * 4 # 24 байта на строку с выравниванием
rows = [d[off + i*row : off + (i+1)*row] for i in range(h)]
alphabet = b''.join(r[w:] for r in rows).decode() # байты выравнивания
pixels = b''.join(r[:w] for r in rows[::-1]) # BMP хранится снизу вверх
print('алфавит:', alphabet)
print(''.join(' ' if p == 0 else alphabet[p - 1] for p in pixels))
Из выравнивания собирается алфавит abcdefghijklmnopqrstuvwxyz_{}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789. Пиксель n — это n-я буква, 0 — пробел. Строки разворачиваем, потому что BMP хранит их снизу вверх. В порядке файла текст шёл кусками задом наперёд.
Получается наставление Гамлета актёрам («Speak the speech, I pray you…», акт 3, сцена 2), и посреди монолога вставлен флаг. Проверка гипотезы по формату флага: байты 21 32 20 28 22 2F 37 1C = номера 33, 50, 32, 40, 34, 47, 55, 28 → D U C K E R Z {.
показать флаг (спойлер)
DUCKERZ{bmp_sh4k3sp34r_l33t_sp33ch}Что вынести
- В BMP есть «ничейные» байты: выравнивание строк, зарезервированные поля заголовка, промежуток между палитрой и пикселями.
- Нашлась строка-алфавит — ищи числа, которые могут быть номерами в ней.
- Формат флага (
DUCKERZ{) — лучший способ проверить гипотезу на первых символах.