# Сплетники - WriteUp Задача находится в категории Blackbox (реверс без бинарника) и предполагает наблюдение за работой неизвестного протокола. Уже из вывода эндпоинтов `feed`, `status` и `settings` можно получить базовую картину: - Есть одна сущность, которая задаёт вопросы, и две сущности, которые отвечают. - Вопросы имеют жёсткую форму: вопросительное слово, существительное, прилагательное и обстоятельство (ровно 4 слова + `?`). - Ответы всегда длиной 12 "слов" (фрагментов между пробелами) и выглядят как непрерывная последовательность из "Трёх мушкетёров". - Сообщения валидируются системным участником `SYSTEM`. - Общение идёт в рамках "эпох". - Перед концом эпохи запускается выбор нового лидера: все участники задают вопросы и отвечают, а победитель выбирается по числу правильных ответов и средней задержке. #### По обращению в эндпоинт `status` можно понять следующее: ```json { "epoch": 12841, - номер эпохи "mode": "CHAT/ELECTION/PAUSE", - режим фида "leader_kid": "d997ba98d4de71fb", - id задающего в эпохе "election_duration_sec": 7.0, - длительность элекции "post_election_pause_sec": 2.0, - длительность паузы "pause_remaining_sec": 0.0, "answer_algo": "rsa_vdf_v1", - понимаем, что ответ детерминирован по RSA VDF "question_algo": "xs64_v1", - понимаем, что вопросы генерируются xorshift64 "vdf_N_hex": "a0ede10c4195e4334e0622f66d94977f3d95ed014847af5c908adfd0d016eac45f8feca1edacd09393e8182039ec9fc0a1521a2d29cc7822084cb8bbe1e2e319e8db7243984c632cf87832d0147291f825a0be20c969bbcbfc0dd8c34a2230382584ffcb066aafa48c98ed5596b6f663dcd29a4fd39a5f7ce31177077e19112093595a68af689da8db66dcc68ff85e1b686621a1777ff2d964a6500550ebdf5de1526ec6135e7147f2afae2ddb975899c891e63cd7a534e461b7b7499a62bd55014d6212584a6feac79b51d438165f4ca890982975e7cc4325932a44fed1d0a305c19a80889be8f9e22cfa074dd325e2eda8bd344a280c36c75f3594c41b498f", - параметр N функции получения ответа "vdf_T": 10000000, - параметр T функции получения ответа } ``` #### А если зайдём в `settings`, то увидим: ```json { "vdf_keygen": "det_rsa_xs64_v1", - понимаем, что модуль RSA детерминированно генерируется из xorshift64 "vdf_prime_bits": 1024, "miller_rabin_rounds": 32, "kid_algo": "sha256:8bytes:hex_lower", "answer_space": 131072, - пространство возможных ответов (dict_size для hint.py) "question_vocab": "16x16x16x16", - размерность словаря вопросов "book_encoding": "koi8_r", "book_sha256_src": "1babc9e9994556119ea941d0de6d867ce15640d55a2ee3801f577cf86633ea00", - помощь для нахождения книги "question_xs64_v1": { "u64_per_question": 4, - 4 генерации на построение вопроса "index_mask_bits": 4 - утекают 4 бита }, "master_seed64_sha256_le64_prefix": "...", - жирный намёк, что есть общая часть сида (64-бит) "epoch_seed64_sha256_le64_prefix": "...", - и тут понимаем, что сид зависит от эпохи } ``` ### На этом этапе делаем выводы - Книгу можно найти из открытых источников по хешу или по характерным фрагментам ответов. - Ответ детерминирован и зависит от вопроса, эпохи и конкретного отвечающего. - Для расчёта ответа используется RSA VDF. - Свои сообщения можно отправлять в фид, если соблюдён формат и подпись. - Вопросы в `CHAT` генерируются от `epoch_seed64`. - Генерация RSA-ключа сидится от `master_seed64` (иначе `N` менялся бы по эпохам). ### Эксперименты с сообщениями Подбор канона подписи показывает, что `sig` считается по всем полям, кроме самого `sig`. Попытка отправить `QUESTION` в обычной фазе не даёт результата, если мы не лидер, а попытка отправить `ELECTION_QUESTION` в `CHAT` сразу возвращает `400`. При ответах на вопросы получаем либо "самозванец" (неверный ответ), либо просто не успеваем в тайм-окно. Отсюда основной вывод: алгоритм вычисления ответа нужно радикально ускорять. Тут и начинается ключевая сложность задачи. ### Путь решения (обязательный) Нужен бот для работы в реальном времени. Он должен уметь генерировать `kid` (как в `hint.py`), корректно подписывать сообщения и отслеживать смену эпох и режимов. Минимальный функционал бота: - опрашивать `/status` и понимать текущие `mode` (`CHAT/ELECTION/PAUSE`), `epoch`, `leader_kid`, `vdf_N_hex`, `vdf_T`; - читать `/feed`, парсить события и хранить локальный лог по эпохам; - отправлять `POST /feed` только в допустимом режиме (в `PAUSE` будет `409`); - корректно выставлять `epoch` и подбирать `qid`, чтобы не конфликтовать с уже существующими сообщениями. После этого восстанавливаем словарь вопросов, а затем `epoch_seed64` по вопросам лидера. В `CHAT` используется `xorshift64`, на вопрос тратится 4 значения `u64`, а в текст попадают только младшие 4 бита каждого значения. Это сводится к линейной системе GF(2), которая решается обычным гауссом. Зная `epoch_seed64`, получаем `master_seed64 = epoch_seed64 ^ epoch` и проверяем себя по `master_seed64_sha256_le64_prefix` из `settings`. Далее берём `master_seed32 = master_seed64 & 0xFFFFFFFF`. ### Дальнейший путь Есть два рабочих направления. #### 1) "В лоб" Можно писать максимально быструю реализацию VDF (параллель, низкоуровневый язык) и делать предвычисления для заранее выбранной эпохи. Ключевой шаг - реверс формулы election-сида (из наблюдений видно, что `election`-вопросы зависят от `epoch` и `kid`): ``` seed = master_seed32 ^ epoch ^ kid16 ``` Этот сид прогоняется через **xorshift32** (не `xorshift64`) для генерации `election`-вопросов. После предсказания трёх `ELECTION_QUESTION` на нужную эпоху можно выигрывать выборы и получать флаг как лидер. #### 2) "Каноническое" Из `settings` видно `vdf_keygen: det_rsa_xs64_v1`, то есть модуль `N` детерминированно генерируется из `xorshift64` на `master_seed64`. Поскольку `vdf_N_hex` стабилен между эпохами, это и есть нужный корневой сид. Далее воспроизводим генерацию RSA-ключей, получаем `p` и `q`, проверяем `p*q == vdf_N_hex`, а затем считаем VDF-ответы на `ELECTION_QUESTION` через CRT и сокращение итераций до одной модэкспоненты. Это позволяет обогнать боссов, выиграть выборы и получить флаг. ### Хвосты Эти факты неочевидны, но сильно упрощают решение: - Ответ с высокой вероятностью можно однозначно преобразовать в вопрос (последовательности из 12 слов в книге почти всегда уникальны). - Полезно логировать и `feed`, и `status`: между перезапусками задания RSA-ключи не меняются, так что накопленный датасет пригодится позже. - В фазе элекции игрок даёт не 2 ответа, а 3, поэтому даже относительно медленная реализация может победить в рамках окна.