ZeroNights 2026 остался позади, но HackQuest ещё долго будет сниться участникам. В этом году организаторы подготовили три совершенно разных задания: одно — про автомобильную электронику, второе — про криптографические доказательства в Monero-стиле, третье — про тактику «взломай бота через нестандартную архитектуру». Разбираем, что произошло и как решались задачи.
Первая задача отправила нас в мир японских магнитол и CD-чейнджеров. В машине друга нашёлся древний, но вполне рабочий чейнджер, который, правда, упорно выдавал странную ошибку. Организаторы дали дамп логического анализатора в формате sigrok: шесть аналоговых каналов, около двух секунд записи, две дифференциальные шины. Одна шина — CAN со скоростью 62.5 кбит/с, вторая — Toyota AVC-LAN, по которой общается головное устройство с чейнджером.
Самое интересное началось при разборе пакетов. CAN-шина декодировалась в 491 кадр, все контрольные суммы сходились, и на ней передавался какой-то текст. Склеив куски, можно было получить явное указание, что это «странная ошибка» — но на самом деле фальшивка, обманка. Настоящий флаг лежал на второй шине, и там была засада: полярность битов оказалась инвертирована относительно типовых описаний AVC-LAN. Обычно high-импульс 33 мкс считается единицей, а 20 мкс — нулём, но в задаче всё наоборот. Только после того как переворачиваешь полярность, адреса становятся осмысленными: голова и чейнджер, а длина кадра сходится с ожидаемой. Дальше механика простая: головное устройство перебирает диски и треки вперемешку, чейнджер отвечает байтом, который и является символом флага. Остаётся отсортировать ответы по паре «диск-трек» и получить строку. Мораль: первый найденный флаг — это не обязательно флаг, а странная ошибка на дисплее может быть просто подсказкой копать глубже.
Вторая задача была про свободные Monero и неаккуратные доказательства. Операторы пула ShadowPool выложили в Tor сервис, а в нём — документацию, REST API и GraphQL. Схема доказательства выглядела классически: пользователь предоставляет лист, соль, путь и корень, а верификатор пересчитывает Merkle root и сравнивает с тем, что у него «доверенного». Но внимательное изучение GraphQL-схемы показало: account ID и action сидят рядом с proof, но не внутри самого доказательства. В формуле вызова challenge их просто нет. Значит, proof никак не привязан ни к аккаунту, ни к операции.
Первая интуиция подсказывала, что нужен настоящий лист. На сервере выгружалось около 600 заданий, в них было 147 уникальных аккаунтов. Какое-то время участник собирал все jobs, выделял account ID, сравнивал их по эпохам и действиям, пытался понять, как восстановить оставшиеся UUID. Всё это в итоге не понадобилось. Нашлась история persisted queries — там были многообещающие названия операций, но почти все поля уже отсутствовали в текущей схеме. В приложении также был публичный beta token, который пробовали подставлять куда угодно — как secret, serial, blind, часть proof, — но он тоже не пригодился.
Разгадка оказалась до смешного простой: сервер доверяет не только листу, но и корню, который присылает пользователь. Формула расчёта Merkle root это явно допускала: если путь пустой, цикл не выполняется и результатом становится сам leaf. Можно было взять произвольную строку, назвать её листом, указать пустой путь и тот же самый лист в качестве корня — верификатор с радостью примет всё за валидное доказательство. Никакой криптографии не было, просто сервер пересчитывает хеши, но сравнивает результат с ещё одним значением, которое контролирует атакующий.
Самодельный proof начал проходить, но GraphQL не отдавал ни токен, ни флаг. Пришлось перебирать действия. Открытые jobs не помогали, операции из старых запросов тоже. В итоге сработал вариант, связанный с credit service. Главное, что proof не содержит идентификатора аккаунта, поэтому сервер криптографически не способен проверить, для кого он выпущен. Это позволило применить самодельное доказательство для привилегированной операции. Не пришлось знать ни одного настоящего листа — только догадаться, что пустой путь даёт корень, равный листу. И немного отдохнуть в баре, чтобы посмотреть на задачу свежим взглядом.
Третья задача напомнила, что иногда уязвимость лежит в архитектуре. Для получения приглашения на ZeroNights нужно было обыграть бота в Hedgewars — игру, очень похожую на Worms, со счётом до семи побед. Бот телеграм-бота выдавал инстанс и настройки. С первого раза стало ясно, что бот ходит первым и кидает снаряд, а затем вызывает авиаудар: обычная стратегия проигрывает, даже если бросаешь идеально.
Архитектура игры оказалась экзотической: физика и логика — движок на Паскале, интерфейс — Qt на C++, мультиплеерный сервер — на Хаскеле. Сервер по сути не реализует игровую логику, а работает файерволом, который решает, какие сообщения разослать подключённым движкам. И вот тут вскрылась главная дыра: сервер проверяет только команды в матче, legalMsgs и checkNetCmd, причём реальная проверка отправителя есть только для опкода h. Остальные опкоды слепо передаются во все движки, находящиеся в игре.
В движке нашёлся опкод «запятая», который входит в вайтлист и позволяет выставлять флаг, обнуляющий время хода активного игрока и автоматически завершающий его ход. Поскольку этот опкод можно отправить не в свой ход, противник теряет ход. Но реализовать это стабильно оказалось непросто: примерно в одном случае из десяти трюк срабатывал, а бот пропускал ход. Проблема в том, что обычные команды хода (выстрел, движение, скип) передаются с таймстампом, привязанным к тикам движка. Движок кладёт команду в FIFO и исполняет только при совпадении timestamp с GameTicks. Угадывать тики слишком сложно. Однако изучение кода очереди и диспатча ходов дало ещё один интересный опкод — N, который тоже был в вайтлисте и отключал tmpflag. Это позволяло обойти стабильную синхронизацию и добиться нужного поведения. В итоге бот был побеждён, несмотря на то, что в одиночку агенты не могли его обойти.
Все три задания объединяет одна мысль: уязвимости не всегда лежат в криптографии или сложной математике. Иногда достаточно проверить, кому сервер доверяет, что именно он считает валидным и не забывает ли он проверить отправителя. А ещё — не останавливаться после первого найденного флага, потому что за ним может прятаться настоящий.
Что вы думаете: какая из трёх задач показалась самой сложной — автомобильные шины, подделка Merkle-доказательств или взлом игрового сервера через нестандартную архитектуру? Поделитесь в комментариях!
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | 30 сентября мы провели конференцию по информационной безопасности ZeroNights 2026. ... | 1 | 9.58 | 06-10-2026 |
| 2 | На соревновании Pwn2Own продемонстрированы взломы смартфонов, принтеров, устройств умного дома и AI-инструментов | 0 | 16.54 | 08-10-2026 |
| 3 | Daily Hacker News for 2026-09-25 | 0 | 8.38 | 26-09-2026 |
| 4 | Daily Hacker News for 2026-09-28 | 0 | 9.42 | 29-09-2026 |
| 5 | ч2: Разбор. Как мы закрывали утечку через tun0 в AmneziaVPN | 0 | 7.78 | 01-10-2026 |
| 6 | This Week in Security: FBI Gets Hacked, Muse Vulnerable to ClickFix, Popular Rust Developers at Risk, and New Attacks Against RSA | 0 | 30.1 | 25-09-2026 |
| 7 | ч2: Разбор. Как мы закрывали утечку через tun0 в AmneziaVPN | 0 | 7.78 | 01-10-2026 |
| 8 | 900 гипотез об уязвимостях и 12 миллиардов токенов | 0 | 8.99 | 01-10-2026 |
| 9 | 📊💯 ШОУ «СТО К ОДНОМУ» НА МАКСИМАЛКАХ! Итоги невероятно азартной ... | 0 | 11.09 | 28-09-2026 |
| 10 | Message to community | 0 | 11.01 | 28-09-2026 |