Skip to main content

1337book: writeup

1337book

CategoryStegano
DifficultyEasy
TechniquePDF text extraction + Base64
Flagflag{U_REALLY_CAN_READ_TH1$}

Recon

Тебе вручили файл 1337book.pdf. Где-то внутри спрятан флаг.

Артефакт: 1337book.pdf.

file 1337book.pdf
wc -c 1337book.pdf
1337book.pdf: PDF document, version 1.3
157822  1337book.pdf

Ключевые наблюдения:

  • Название намекает на leet-число 1337 — это классика хакер-культуры, вероятно количество страниц.
  • Открыть в просмотрщике: 1337 страниц, практически все пустые.
  • Перелистать вручную нереально — нужна автоматизация.

Проверяем: не лежит ли флаг прямо в тексте файла (быстрая разведка):

strings 1337book.pdf | grep "flag{"
(нет вывода)

Результат: пусто. Вывод: flag{...} в открытом виде в файле нет — значит флаг закодирован.


PDF Text Scan

Парсинг PDF — программное извлечение текстового содержимого страниц, в обход ручного листания.

Образ: открыть 1337-страничную книгу и прочитать каждую страницу — занимает часы. Написать скрипт — занимает секунды: цикл просматривает страницу за страницей и сразу сообщает, на какой из них есть непустой текст.

Ключевой вопрос: на каких страницах вообще есть текстовый контент?

python3 parse_pypdf.py
Pages: 1337
Page 136: 'ZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ\n'
Extraction complete. Extracted length: 41

Одна страница из 1337 содержит непустой текст — страница 136 (нумерация с 0, то есть 137-я по счёту).

Строка ZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ — верхний регистр, цифры, без спецсимволов. Это классический облик Base64 — схемы перевода произвольных байт в печатные ASCII-символы.


Decryption

ШагДействиеКоманда / данные
1Запустить pypdf-скрипт, найти непустую страницуPage 136
2Получить Base64-строку с этой страницыZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ
3Декодировать Base64echo "ZmxhZ3..." | base64 -d
4Получить флагflag{U_REALLY_CAN_READ_TH1$}

Ручная проверка шага 3:

echo "ZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ" | base64 -d
flag{U_REALLY_CAN_READ_TH1$}

Root cause:

# Генератор таска: флаг кодируется в Base64 и вставляется текстом на страницу TARGET_PAGE
import base64
FLAG_B64 = base64.b64encode(b"flag{U_REALLY_CAN_READ_TH1$}").decode().rstrip("=")
# → ZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ

if page_num == TARGET_PAGE:          # страница 136 (1-based)
    pdf.cell(0, 10, txt=FLAG_B64, ln=1, align="C")
# остальные 1336 страниц — пустые

Паддинг == убран намеренно — строка распознаётся как Base64 без него, но grep flag по PDF не находит ничего.

Правильный подход (как усложнить сокрытие):

  • Спрятать текст белым шрифтом на белом фоне — визуально невидим, но pypdf извлекает.
  • Заполнить каждую страницу ложными Base64-строками — только одна декодируется в flag{...}.
  • Встроить флаг в метаданные PDF (XMP / DocInfo) вместо текста страницы.
  • Использовать нестандартный шрифт-замену: глифы подменены, OCR выдаёт флаг, копипаст — мусор.

Automation

import base64
from pypdf import PdfReader


def main() -> None:
    reader = PdfReader("1337book.pdf")
    print(f"Всего страниц: {len(reader.pages)}")

    for page_num, page in enumerate(reader.pages):
        text = page.extract_text()
        if not text or not text.strip():   # пропускаем пустые страницы
            continue

        candidate = text.strip()
        print(f"[+] Страница {page_num}: {candidate}")

        try:
            # базовый декодировщик: добавляем паддинг на случай, если он обрезан
            decoded = base64.b64decode(candidate + "==").decode()
            print(f"[+] Base64 → {decoded}")
        except Exception:
            print("[-] Не Base64, пропускаем")


if __name__ == "__main__":
    main()
python3 solve.py
# Всего страниц: 1337
# [+] Страница 136: ZmxhZ3tVX1JFQUxMWV9DQU5fUkVBRF9USDEkfQ
# [+] Base64 → flag{U_REALLY_CAN_READ_TH1$}

Key Takeaways

  1. PDF — не монолит, а набор объектов. Текст на каждой странице хранится в отдельном потоке и легко извлекается библиотеками pypdf / PyMuPDF. Просмотрщик скрывает структуру, парсер — раскрывает.
  2. Base64 узнаётся по алфавиту. Строка только из A–Z, a–z, 0–9, +, /, = (и длина, кратная 4 или без паддинга) — немедленный сигнал для base64 -d. Это не шифр, а транспортная кодировка.
  3. Ловушка задачи: strings | grep flag — привычная команда, которая здесь молчит. Участники, доверившись ей, решают, что флага нет, и бросают разведку. Флаг присутствует в файле в виде читаемых ASCII-символов — просто без слова flag{ в открытом виде.