Нейрофаззинг: как нейросеть находит неочевидные уязвимости

“Нейрофаззинг, это не про замену инструмента, а про смену вектора атаки. Вместо того, чтобы тыкать вслепую в миллиард мест, модель учится делать один осмысленный, но неправильный ход. Этого часто достаточно, чтобы система, отточенная годами, споткнулась.”

Слова «нейрофаззинг» нет в глоссарии ФСТЭК. В российском регулировании это — разновидность динамического анализа. Но на практике между стандартными методами и подходом, завязанным на нейросети, лежит пропасть. Один подход проверяет соответствие, другой — ищет несоответствия, которые ещё никто не описал.

В чём подвох с классическим фаззингом

Классический фаззинг, особенно для поиска уязвимостей в ПО, подразумевает генерацию полуслучайного, заведомо некорректного ввода. Его сила — в автоматизации и покрытии. Но это и его ахиллесова пята. Типичный фаззер работает по грубым правилам: инвертировать байт, добавить нулевой символ, переполнить буфер предсказуемой последовательностью. Системы защиты и разработчики десятилетиями учились отбивать именно такие атаки. Анализаторы кода искали strcpy, санитайзеры ловили классические переполнения буфера.

В результате появилась устойчивая иллюзия: если проект проходит длительный прогон через AFL или libFuzzer без падений, он достаточно «профазжен». На самом деле, он устойчив к известному спектру аномалий. А нейросеть может сгенерировать аномалию, которая не вписывается в этот спектр — не потому, что она сложнее, а потому, что она семантически иная.

Простейшая аналогия: замок с десятью тысячами пинов. Обычный фаззер будет пробовать десять миллионов случайных отмычек. Нейрофаззинг попытается понять форму ключа и предложить отмычку, которая выглядит как правильный ключ, но один её зубчик чуть смещён. Часовой механизм, ожидающий идеальной формы, ломается.

Как нейросеть меняет игру

Вместо набора примитивных мутаций нейросеть, обученная на корпусе корректных данных (исходный код, сетевые пакеты, файлы определённого формата), строит вероятностную модель «нормальности». Её задача — не сломать формальные правила, а нарушить неявные предположения.

Например, парсер конфигурационного файла ожидает строк в формате «ключ=значение». Классический фаззер начнёт вставлять мусор, очень длинные строки, спецсимволы. Модель же может сгенерировать валидную на первый взгляд строку, но с ключом, который никогда не встречался в обучающих данных, или со значением, которое представляет собой корректный JSON, встроенный в строковое поле. Логика обработки, не готовая к такой комбинации, даёт сбой.

Практический кейс: протокол управления

Целью был закрытый бинарный протокол для управления сетевым оборудованием. Протокол имел документацию, но реализации на С++ была лет десять, со множеством патчей. Классический фаззинг на основе шаблонов пакетов дал пару падений, связанных с тривиальным переполнением целочисленных типов — их быстро пофиксили.

Подход с нейросетью был иным. На основе документации и примеров корректных траффика была дообучена небольшая модель (на базе архитектуры, подобной GPT-Neo) для генерации бинарных последовательностей, похожих на валидные сообщения. Критерий «похожести» определялся не формальной грамматикой, а статистическими закономерностями в обучающей выборке.

Вот упрощённый пример логики, которую использовала модель вместо прямого кода:

  • Корректное сообщение: заголовок (4 байта, тип команды), длина (2 байта), тело (N байт), контрольная сумма (2 байта).
  • Модель «поняла», что поле «длина» обычно коррелирует с реальным размером тела.
  • Она сгенерировала сообщение, где в поле «тип команды» стояло значение для «чтения конфигурации», а в поле «длина» — ноль. При этом тело сообщения было пустым, что формально корректно.
  • Обработчик, получив команду на чтение, пытался интерпретировать «тело» длиной 0 как указатель на структуру параметров. Возникло обращение к неинициализированной памяти.

Это не было переполнением буфера. Это была логическая ошибка в предположении разработчика: «если команда X, то в теле обязательно будут параметры». Модель нашла способ нарушить это предположение, оставаясь в рамках синтаксиса протокола.

Настройка окружения для эксперимента

Работа велась не на промышленных мощностях, а на локальной рабочей станции с GPU. Ключевые компоненты:

  • Целевое ПО: бинарник сервиса управления, запущенный в изолированном окружении (Docker) с подключёнными санитайзерами (ASAN, UBSAN).
  • Инструмент фаззинга: модифицированная связка. За основу был взят фреймворк для генерации сетевого трафика, в который интегрирован скрипт генерации через локально развёрнутую языковую модель.
  • Модель: Небольшая дообученная трансформерная модель (7B параметров). Дообучение проводилось на датасете из захваченных валидных сессий и формальном описании протокола в текстовом виде. Цель — научить модель «продолжать» бинарную последовательность в стиле протокола.

Стоит помнить: модель не генерировала код на C и не проводила статический анализ. Она работала как продвинутый генератор тестовых данных, «чувствующий» контекст протокола.

Что было найдено за ночь

За одну сессию длиной около 8 часов было обнаружено 12 уникальных сбоев. Их можно классифицировать так:

Тип сбоя Кол-во Пример триггера от модели Потенциальная классификация по 152-ФЗ
Обращение к нулевому/неинициализированному указателю 5 Команда с флагом «требовать ответ», но нулевым полем идентификатора запроса. ННД (Недостатки, ведущие к отказу в обслуживании)
Целочисленное переполнение в расчёте размера буфера 3 Пакет, где два поля длины вложенных структур при валидных по отдельности значениях в сумме давали переполнение 32-битного целого. ННД (Потенциально — выполнение произвольного кода)
Выход за границы при чтении (OOB Read) 2 Структура данных, где количество элементов было указано корректно, но один из индексов элементов ссылался на несуществующее поле. Утечка информации
Логическая ошибка (бесконечный цикл, deadlock) 2 Последовательность двух команд, где вторая отменяла действие первой, но при определённом значении таймаута приводила к ожиданию несуществующего события. ННД (Отказ в обслуживании)

Обратите внимание: только 3 из 12 были классическими «переполнениями буфера». Остальные, это логические и семантические уязвимости, которые сложно выловить формальными методами или случайным перебором.

Как это соотносится с регуляторикой и тестированием на соответствие

Методические документы ФСТЭК и требования 152-ФЗ фокусируются на выявлении известных классов уязвимостей. Статический анализ искает шаблоны уязвимого кода (РД «Безопасность ИС»), динамический анализ (фаззинг) рекомендован для проверки обработки внешних данных. Нейрофаззинг формально подпадает под динамический анализ, но его результаты ставят перед экспертами по аттестации непривычные вопросы.

Если классический фаззинг не нашёл ничего, а нейросеть нашла дюжину сбоев, означает ли это, что система не прошла проверку? С точки зрения формальных критериев — нет, если эти классы сбоев не были указаны в методике проверки. С точки зрения реальной безопасности, это красный флаг, указывающий на недостаточную глубину стандартных тестов.

В перспективе это может привести к эволюции требований. Не к предписанию использовать ИИ, а к ужесточению требований к полноте тестового покрытия и к необходимости проверки на устойчивость к «некорректным, но правдоподобным» данным, а не только к заведомо мусорным.

Ограничения и где метод не сработает

Нейрофаззинг — не серебряная пуля. Он требует:

  • Качественных данных для обучения. Без репрезентативной выборки валидных данных модель не научится генерировать правдоподобный ввод. Для полностью закрытых протоколов с малым количеством примеров метод бесполезен.
  • Вычислительных ресурсов. Генерация через модель на порядки медленнее, чем мутация байтов. Это не для массового скрининга тысяч бинарников.
  • Интерпретации результатов. Модель генерирует входные данные, которые приводят к сбою, но не объясняет причинно-следственную связь. Анализ корневой причины и оценка критичности — всё ещё за исследователем.
  • Слабой защищённости от собственных атак. Если в обучающую выборку попали данные, сгенерированные самим нейрофаззером, есть риск «зацикливания» и деградации модели. Нужна строгая фильтрация.

Главный риск — соблазн считать, что если нейросеть ничего не нашла, то уязвимостей нет. Это опасное заблуждение. Метод лишь расширяет арсенал, но не гарантирует полноты проверки.

Итог: следующий шаг в эволюции тестирования

Использование нейросетей во фаззинге не делает традиционные инструменты ненужными. Оно смещает фокус с поиска известных багов на исследование неявных допущений в коде. Это особенно актуально для legacy-систем и сложных протоколов, где формальная спецификация расходится с реальной реализацией.

Для специалиста по безопасности и аттестации это означает, что паспорт безопасности, составленный только по результатам стандартного сканирования и фаззинга, может иметь слепые зоны. Новые методы требуют новых компетенций — не только в машинном обучении, но и в интерпретации его «творческих» находок в контексте регуляторных требований. Результат той ночи — не просто 12 отчётов об ошибках. Это демонстрация того, что поверхностный слой защищённости давно пройден, и реальные проблемы лежат глубже, в слое логики, который раньше проверяли только вручную. Теперь это можно автоматизировать, пусть и частично.

Оставьте комментарий