Во многих задачах на стыке ML и CTF участникам показывают нейросеть как "чёрный ящик" и предлагают догадаться, что она вычисляет. Но в этой истории формат намеренно перевернули: вместо доступа только к входам и выходам авторы выдали полное устройство модели - архитектуру и параметры - чтобы решение опиралось не на угадывание, а на механистическую интерпретируемость и аккуратный анализ весов нейросети. Именно такой подход сегодня становится практикой: когда модель ведёт себя странно, иногда проще "прочитать" её изнутри, чем пытаться подобрать входы.
Идея головоломки родилась в начале прошлого года, когда команда задумала собственный набор ctf задания машинное обучение. Хотелось уйти от типичной развилки "подбери вход - получи флаг" и заставить участников действительно понять вычисление, спрятанное в сети. Модель опубликовали в феврале: файл `model.pt` (обычный PyTorch-пакет в формате pickle) содержал сеть глубиной около 2500 слоёв. На первый взгляд она выглядела "мертвой": почти на любых данных возвращала 0. Естественное предположение - где-то существует редкий ввод, на котором выход станет 1 или хотя бы ненулевым. Но конструкцию специально собрали так, чтобы к ответу нельзя было подступиться привычным перебором и градиентными трюками: обратное распространение от ненулевого выхода к входу не давало простого пути. Нужно было разбираться в логике вычисления, шаг за шагом восстанавливая "программу", закодированную в слоях.
Реакция оказалась неожиданно мощной: сложность попала ровно в цель - не слишком легко, но и не безнадёжно. Более того, организаторы честно признались: если человек способен пройти такое испытание, он с высокой вероятностью будет комфортно чувствовать себя в команде Jane Street. Разбор одного из решений (со всеми ошибками, тупиками и внезапными поворотами) хорошо показывает, как выглядит настоящее reverse engineering нейросети, когда вместо ассемблера у вас матрицы, ReLU и тысячи однотипных блоков. Подробный разбор этой задачи удобно читать как перевод статей Хабр о восстановлении вычислений по весам нейросети: там аккуратно раскладывают, почему "просто оптимизировать вход" не сработает и как участники распутывали конструкцию.
Сюжет подан как находка "тензоров под неолитическим курганом": неизвестный артефакт, из которого "нейросетевой сантехник" собрал модель. В качестве примера входа фигурирует строка вроде `vegetable dog`, а на выходе - одиночное число. Но главное начинается, когда один из участников, студент выпускного курса Алекс, скачивает модель и открывает последние слои.
Почти сразу выясняется, что перед ним не обученная сеть в привычном смысле. Веса - целые числа, без следов стохастики и плавных распределений, характерных для тренировки. Это больше похоже на вручную спроектированную схему, где нейроны играют роль логических элементов и регистров. Алекс начинает с конца: последний линейный слой - матрица 48×1 - визуально распадается на три одинаковых сегмента. И это не случайность: если посмотреть на активации предыдущего слоя, один и тот же паттерн повторяется трижды.
Ещё интереснее предпоследний слой: он, по сути, содержит три копии одинаковых весов, а в смещениях повторяется один и тот же набор из 16 байтов, но с сдвигом на единицу - как будто закодированы три эталона: `v`, затем `v+1`, затем `v+2`. После короткого размышления Алекс делает ключевую догадку: предпоследний ReLU-слой, вероятнее всего, реализует проверку равенства для двух 16-байтовых целых чисел, причём каждому байту соответствует свой нейрон. Слой берёт входной 16-байтовый вектор, трижды его "размножает" и сравнивает с константой `x`, заданной bias'ом. На выходе получается три набора признаков, которые затем сворачиваются последним слоем в один бит с весами вида 1, -2 и т. п. - типичный приём, когда из нескольких проверок собирают строгий логический результат.
Дальше начинается то, ради чего и затевалась головоломка: восстановление более крупного вычисления, спрятанного глубже, где повторяющиеся блоки и целочисленные матрицы складываются в цепочку операций. Участникам приходится буквально "декомпилировать" сеть: искать в слоях следы сложения по модулю, перестановок, побитовых операций и других строительных блоков, пока не проявится знакомая структура. В итоге выясняется, что в этих 2500 слоях спрятана реализация MD5, а параметры сети содержат достаточно информации, чтобы по весам реконструировать логику хеширования.
Этот кейс полезен не только как интеллектуальная игра. Он хорошо подсвечивает практическую сторону темы нейросети безопасность обучение: если модель можно прочитать как программу, то публикация весов иногда раскрывает больше, чем кажется. С одной стороны, открытость ускоряет исследования и аудит. С другой - когда внутри скрыты "секретные" правила, watermark'и или проверочные условия, опытный исследователь способен извлечь их, даже не имея доступа к данным обучения.
Есть и методический вывод для тех, кто решает ctf задания машинное обучение или строит свои. "Градиентный" путь - не единственная дорога. Если модель детерминирована, целочисленна и выглядит как набор повторяющихся примитивов, иногда проще (и надёжнее) анализировать её как вычислительную схему: сегментацию слоёв, поиск повторов, чтение bias'ов как констант, проверку гипотез через локальные тесты. Именно так и рождается механистическая интерпретируемость на практике - не в виде красивых диаграмм, а в виде кропотливого восстановления алгоритма из матриц.
Наконец, подобные головоломки дают редкую возможность потренировать "инженерное зрение" к моделям. Большинство реальных сетей обучены и потому выглядят шумно, но принципы те же: замечать симметрии, инварианты, повторяющиеся подструктуры, искать места, где сеть вынуждена "считать" (а не просто распознавать). А если хочется увидеть, как это выглядит в разборе от начала до почти детективного финала, то разбор восстановления MD5 по весам нейросети отлично передаёт, почему здесь победил не перебор, а понимание устройства слоёв.