Кубок CTF России

Разбор: compuuuter с отбора X Кубка CTF

Условие: web с меткой beginner, участникам дают исходники. Это похожий на Scratch граф-компьютер: граф нод загружают как JSON, вычисляют и читают результат. По таблице организаторов задание решили 480 раз.

Уязвимость

Скрытый опкод type 1337 выдаёт байты флага. compute сначала проверяет, что такой ноды нет, потом строит таблицу и считает. А patch меняет ноду на месте из другого запроса и такой проверки не делает. Это TOCTOU: разрыв между проверкой и использованием.

Как растянуть окно

У нормального графа окно — микросекунды. Индекс нод — обычный dict по id, а хеши целых в CPython не рандомизируются. На 64-битном CPython для неотрицательного n верно, что hash(n) равен n % (2**61 - 1). Значит, id вида BASE + k*(2**61-1) схлопываются в одну цепочку коллизий и вставка становится O(n²).

Ход решения

  • Загрузить тысячи нод-наполнителей и безобидную выходную ноду последней
  • Отправить compute в одном потоке
  • Примерно через 0,3 с отправить PATCH и поставить этой ноде type 1337
  • Перевести результат из little-endian в байты

Пример

Пример условный. Для проверки команда запускает сервис локально и подбирает размер наполнителя: если вычисление закончилось раньше патча, число нод увеличивают.

Важно. Ищите в коде пары «проверил — использовал» с общим изменяемым объектом. Так находят логические гонки и в обычных веб-приложениях.

Для разбора в команде

  • ☐ Найдена пара проверки и использования
  • ☐ Понятно, что меняется снаружи
  • ☐ Окно растянуто измеримо
  • ☐ Эксплойт записан

В шаблоне разбора есть раздел «Как защититься»: сюда ляжет и разбор гонки 👇

Основание: условие и решение авторов задачи, репозиторий acisoru/ctfcup2026-quals, web/compuuuter. Канал не принадлежит организаторам; официальный сайт — ctfcup.ru.

Первоисточник