010: writeup
Ссылка на задачу http://f8tasks.ru/challenges#010-22
010
| Category | Stegano |
| Difficulty | Easy |
| Technique | Bitmap Reconstruction → QR |
| Flag | flag{are_you_robot?} |
Recon
Нам выдан файл file.txt. На первый взгляд — просто текст. Задача называется «Черное и белое». Найди флаг.
Артефакт: file.txt.
file file.txt
wc -c file.txt
file.txt: ASCII text, with no line terminators
108900 file.txt
Ключевые наблюдения:
- файл состоит исключительно из символов
0и1; - расширение
.txtне говорит о структуре данных — нужно смотреть внутрь; - размер файла —
108900байт.
Первая проверка — является ли размер точным квадратом:
from math import isqrt
print(isqrt(108900)) # → 330
print(330 * 330) # → 108900
Результат: 330. Вывод: 108 900 бит раскладываются в квадрат 330 × 330.
Bitmap Reconstruction
Bitmap (битовая карта) — изображение, в котором каждый пиксель задаётся одним битом: 1 = чёрный, 0 = белый (или наоборот).
Ключевой вопрос: если разбить поток из 108 900 символов на строки по 330 — что получится?
QR-код версии 2 устроен так: поле данных 25 × 25 модулей, тихая зона по 4 модуля с каждой стороны, каждый модуль масштабирован до 10 × 10 пикселей. Итоговый размер:
Размер 330 × 330 в точности совпадает с QR версии 2. Гипотеза: перед нами QR-код, сериализованный как плоский поток битов.
Проверяем, просто нарисовав картинку:
from pathlib import Path
from math import isqrt
from PIL import Image
data = Path("file.txt").read_text().strip()
side = isqrt(len(data))
img = Image.new("1", (side, side))
px = img.load()
for i, bit in enumerate(data):
px[i % side, i // side] = 0 if bit == "1" else 1 # 1 → чёрный пиксель
img.save("qr.png")
qr.png — три угловых finder-паттерна видны сразу
Три большие квадратные метки по углам — классический признак QR-кода. Телефон или любой QR-сканер читает его мгновенно.
Decryption
| Шаг | Действие | Формула / команда |
|---|---|---|
| 1 | Убедиться, что длина — точный квадрат | isqrt(108900) = 330; 330 × 330 = 108900 |
| 2 | Сложить биты в матрицу 330 × 330 | первые 330 символов → строка 0; следующие 330 → строка 1; … |
| 3 | Отрисовать как чёрно-белое изображение | Image.new("1", (330, 330)) |
| 4 | Распознать QR по трём угловым finder-паттернам | визуальная проверка |
| 5 | Считать QR телефоном или библиотекой | flag{are_you_robot?} |
Root cause: задача намеренно сохранила QR-код как поток символов в .txt. Стандартные инструменты (file, strings) ничего не сообщат — нужно сначала понять форму данных, а уже потом пытаться её декодировать.
Правильный подход при анализе неизвестного файла:
- не доверяй расширению — проверяй реальное содержимое;
- смотри, из каких символов состоит файл (только
0и1= явный сигнал); - проверяй размер на квадратность / кратность известным форматам;
- пробуй несколько интерпретаций: картинка, аудио, архив, кодировка.
Automation
from pathlib import Path
from math import isqrt
from PIL import Image
data = Path("file.txt").read_text().strip()
side = isqrt(len(data))
assert side * side == len(data), "данные не образуют квадрат"
img = Image.new("1", (side, side))
px = img.load()
for i, bit in enumerate(data):
# "1" в файле → чёрный пиксель (0 в PIL); "0" → белый (1 в PIL)
px[i % side, i // side] = 0 if bit == "1" else 1
img.save("qr.png")
print("Saved qr.png — scan with phone or qreader")
python3 solve.py
# Saved qr.png — scan with phone or qreader
После сканирования qr.png любым QR-сканером:
flag{are_you_robot?}
Key Takeaways
Расширение файла — не тип данных.
.txtможет содержать битовую карту, архив или любой другой бинарный формат. Всегда проверяй содержимое черезfileи анализ символов.Квадратный размер данных — сигнал. Если число символов равно $n^2$, данные скорее всего представляют квадратную матрицу. Одна строка
isqrt(len(data))**2 == len(data)подтверждает или опровергает гипотезу за секунду.Ловушка: название «Черное и белое». Это прямой намёк на монохромное изображение — но многие участники тратят время на поиск Base64, XOR-шифра или LSB-стего внутри текста, не проверив базовую структуру файла первой.
