Статья
Мой первый CTF
Возможно, кто-то скажет, что этот CTF был не совсем привычного формата, но так как он стал для меня первым, я позволю себе считать, что другие просто не такие, какими должны быть. В этом задании я понял важность владения базовыми инструментами и выполнения стандартных действий — иногда решение лежит на поверхности, и не нужно изобретать велосипед.
Сразу признаюсь: за отведённое время я бы не справился, так как за компьютер смог сесть только после официального окончания соревнований. К счастью, CTF продлили, и у меня появился шанс довести дело до конца. Возможно, в моём решении был элемент везения или «вайб-кодинга», но так уж сложилось. Главное — я начал нарабатывать скилл и насмотренность, а это бесценно.
Этап 1: Первое знакомство с «битым» изображением
Мы получили файл osint_kraken.png, который не открывался стандартными просмотрщиками. Первое, что приходит в голову — проверить, что это вообще за файл и не повреждён ли он намеренно.
Базовые проверки:
file osint_kraken.png # Проверка типа файла exiftool osint_kraken.png # Проверка метаданных pngcheck osint_kraken.png # Проверка структуры PNG
Результат pngcheck был показательным: CRC error in chunk IHDR. Файл оказался намеренно «испорченным» — классический приём в стеганографии.
Hex-анализ:
xxd osint_kraken.png | head -40 # Просмотр заголовка
Сигнатура PNG (89 50 4E 47) была на месте, что означало: файл действительно является PNG-изображением, но с изменёнными данными внутри.
Этап 2: Понимание метода сокрытия
Из статьи-подсказки я узнал, что изменяя размеры в блоке IHDR и не обновляя контрольную сумму (CRC), можно скрыть часть изображения, не удаляя её окончательно. Структура критически важного блока IHDR:
-
Смещение 16-23 байт: ширина и высота изображения (по 4 байта).
-
Смещение 29-32 байт: CRC блока
IHDR.
Этап 3: Восстановление изображения
Первым делом нужно было исправить CRC, чтобы файл стал валидным PNG. Для этого:
cp osint_kraken.png fixed.png # pngcheck покажет что-то вроде: "computed XXXXXXXX, expected YYYYYYYY" # Меняем CRC на вычисленное значение (XXXXXXX): printf '\xXX\xXX\xXX\xXX' | dd of=fixed.png bs=1 seek=29 conv=notrunc
Файл fixed.png наконец-то открылся, но изображение оказалось обрезанным — в нижней части был виден лишь фрагмент QR-кода.
Этап 4: Восстановление полного QR-кода
Гипотеза была очевидной: QR-код скрыт «за границами» видимой области, то есть исходная высота изображения была больше. Нужно было подобрать правильное значение высоты.
Я загуглил простой bash-скрипт для автоматического перебора:
-
Перебирал дельты от 1 с шагом 10 и до 1000 пикселей.
-
Для каждой дельты менял высоту в блоке
IHDRи пересчитывал CRC. -
Сохранял каждый валидный PNG в отдельный файл.
#!/bin/bash for delta in $(seq 1 10 1000); do echo "Пробуем delta = $delta" cp original.png test_delta_${delta}.png # Вычисляем новую высоту new_height=$((2912 + delta)) # Конвертируем в hex (big-endian) h1=$(( (new_height >> 24) & 0xFF )) h2=$(( (new_height >> 16) & 0xFF )) h3=$(( (new_height >> 8) & 0xFF )) h4=$(( new_height & 0xFF )) # Заменяем высоту printf "\\x$(printf '%02x' $h1)\\x$(printf '%02x' $h2)\\x$(printf '%02x' $h3)\\x$(printf '%02x' $h4)" | \ dd of=test_delta_${delta}.png bs=1 seek=20 conv=notrunc 2>/dev/null # Получаем новый CRC crc_output=$(pngcheck test_delta_${delta}.png 2>&1 | grep "computed") if echo "$crc_output" | grep -q "computed"; then computed_crc=$(echo "$crc_output" | sed 's/.*computed \([0-9a-f]*\),.*/\1/') # Заменяем CRC c1=$(echo $computed_crc | cut -c1-2) c2=$(echo $computed_crc | cut -c3-4) c3=$(echo $computed_crc | cut -c5-6) c4=$(echo $computed_crc | cut -c7-8) printf "\\x$c1\\x$c2\\x$c3\\x$c4" | \ dd of=test_delta_${delta}.png bs=1 seek=29 conv=notrunc 2>/dev/null # Проверяем валидность if pngcheck test_delta_${delta}.png >/dev/null 2>&1; then echo " ✓ Валидный PNG с высотой $new_height" # Пробуем сканировать QR-код zbarimg test_delta_${delta}.png 2>/dev/null && echo " ✓ QR-код найден!" && exit 0 fi fi done
Сохраните как find_qr.sh, запустите:
chmod +x find_qr.sh
./find_qr.sh
Затем вручную открыл полученные изображения и нашёл тот, где QR-код отображался полностью.

Этап 5: Сканирование QR-кода и небольшая неудача
QR-код я отсканировал с помощью приложения на телефоне. Он вёл на ссылку Яндекс.Диска, где лежало второе изображение — 20231111_155824.jpg.
Тут я совершил ошибку: вместо того чтобы сразу проанализировать новую картинку, я решил поискать её в интернете. Нашёл мемориальную табличку и почему-то подумал, что флаг — это имя «А.А. Мачура». Естественно, это оказалось неверным. Мне пришлось просить подсказку, после которой я вернулся на правильный путь и повторил для второго изображения Этап 1.
Этап 6: Анализ второй картинки
Проверка метаданных дала результат:
exiftool 20231111_155824.jpg
В EX-данных обнаружились GPS-координаты:
-
Широта: 45°1'7.38" N
-
Долгота: 38°58'7.51" E
Этап 7: Интерпретация GPS-координат
Я преобразовал координаты в десятичный формат (спасибо ИИ за помощь):
-
45°1'7.38" N = 45.0187167
-
38°58'7.51" E = 38.9687528
Поиск по координатам в Google Maps (оказалось, что в Яндекс.Картах они отображаются немного иначе) привёл к конкретному дому в Краснодаре. Переключившись на Яндекс.Карты для удобства, я нашёл то, что искал.
Флагом оказалось имя человека, связанное с этим адресом.
Заключение
wqvQotGLINC00YPQvNCw0LXRiNGMLCDRjdGC0L4g0LrQvtC90LXRhj/CuyDigJQgwqvQndC10YIuINCt0YLQviDRgtC+0LvRjNC60L4g0L3QsNGH0LDQu9C+wrsu
Мой первый CTF по стеганографии научил меня нескольким важным вещам:
-
Не игнорируйте основы. Команды
file,exiftoolиpngcheckдолжны стать рефлексом. -
Доводите анализ до конца. Если нашли новое изображение — применяйте к нему все те же методы.
-
Контекст имеет значение. GPS-координаты — это не просто числа, а указание на место, которое может хранить ответ.
-
Не сдавайтесь после первой ошибки. Ошибочная догадка — часть процесса, главное — вовремя вернуться на правильный путь.
Этот опыт стал отличным стартом в мире CTF. Спасибо команде KRAKEN-ACADEMY