Разбор: 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.