Статья Patch2Vuln: анализ бинарных патчей для автоматического восстановления уязвимостей Linux

Криминалистический стол с двумя бинарными файлами рядом, ноутбук с зелёным кодом на чёрном экране. Лупа на распечатке дизассемблера, синяя перчатка с логическим пробником.


LLM-агент, ограниченный локальными бинарными артефактами - без CVE-описания, без исходного патча, без changelog - локализовал security-relevant функцию в 10 из 20 реальных Ubuntu .deb-пакетов и корректно определил root-cause класс уязвимости в 11 из 20 случаев. Это результаты исследования Patch2Vuln с arxiv. За два года интеграции LLM в рабочий процесс patch diffing ядра Linux я убедился: основное узкое место - не модель, а бинарный диффер. Patch2Vuln подтверждает это цифрами: 6 из 20 неудач случились до того, как LLM вообще увидел данные - на этапе извлечения и ранжирования функций.

Бинарный патч как единственный артефакт для n-day research​

Когда дистрибутив Linux выпускает security-обновление, появляется окно, в котором уязвимый и исправленный бинарники доступны одновременно. Для n-day vulnerability research эта пара ELF-файлов - основной источник информации. Не advisory на NVD (которое зачастую содержит одно предложение), не коммит в upstream - а два бинарника, старый и новый. Подробнее - в нашем подробном разборе бинарный анализ уязвимостей.

В upstream ядра Linux действует культура «bugs are bugs»: security-фиксы часто попадают в публичные коммиты без явной маркировки. Коммит может содержать формулировку «fix bounds check» или «avoid in-place operation» без указания на CVE-идентификатор. Дистрибутивы - Ubuntu, Debian, RHEL - собирают эти фиксы в бинарные пакеты, и diff между двумя версиями .deb или .rpm становится первичным артефактом для patch-based vulnerability discovery.

Задача формулируется прямо: получив пару бинарных пакетов (vulnerable и patched), определить, какая функция изменена по причинам безопасности, какой класс уязвимости она закрывала, через какой input surface достижим уязвимый код, и что в поведении программы отличается между версиями. Вручную это занимает от часов до недель - зависит от размера diff и количества функций-кандидатов. Patch2Vuln проверяет, насколько глубоко LLM-агент может автоматизировать восстановление уязвимостей из патчей при полном отсутствии вспомогательной информации.

Работа намеренно исключает из входных данных агента: CVE-страницу, security advisory дистрибутива (USN), исходный патч, package changelog, публичный PoC и веб-доступ. Единственный источник - бинарные артефакты. Это моделирует реальный сценарий: пакет обновлён, но advisory ещё не опубликован или намеренно лаконичен.

Архитектура Patch2Vuln: pipeline и agent loop​

Patch2Vuln - не новый бинарный диффер и не очередная LLM-обёртка. Система проверяет, может ли агент оркестрировать существующие инструменты binary analysis и трансформировать сырой вывод diff в структурированное объяснение уровня уязвимости.

Pipeline работает в два этапа: локальный бинарный diff и агентский цикл рассуждений.
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Agent Loop: от сырого diff до структурированного аудита​

Агент получает досье на каждую функцию-кандидата и проходит три стадии. Каждая порождает отдельный артефакт.

Preliminary Audit. На основе статического анализа - декомпилированного diff, символов, строк, call context - агент формирует первичную гипотезу: какой класс уязвимости закрыт патчем, через какой input surface достижим уязвимый код, какова степень уверенности. Выход - структурированный JSON плюс Markdown, пригодные для машинной и человеческой обработки.

Bounded Validation Plan. Агент формулирует конкретный план проверки гипотезы: входные данные для старого и нового бинарей, ожидаемые различия в поведении - crash, sanitizer-сигнал, отличие в выводе. Валидация ограничена локальным выполнением в Docker. Никакого сетевого взаимодействия, shellcode или exploit chains - система явно не генерирует эксплуатационный код.

Final Audit. Интегрирует результаты валидации с предварительным аудитом. Если валидация подтвердила гипотезу (старый бинарь падает на подготовленном вводе, новый - нет), уверенность повышается. Если нет - агент снижает confidence или пересматривает root-cause класс.

Концептуальная структура финального аудита для случая tcpdump:
JSON:
{
  "patch_family": "filter_file_bounds_check",
  "root_cause_class": "out-of-bounds-read",
  "input_surface": "crafted BPF filter file via -F flag",
  "confidence": "medium-high",
  "static_evidence": "bounds check added in read_filter()",
  "validation_result": "old crashes on oversized filter; new rejects"
}
Три стадии - не формальность. Разделение на предварительный аудит и финальный позволяет точно диагностировать, где произошла ошибка: модель неверно интерпретировала diff (reasoning failure) или валидация не сработала (validation failure). Для итеративного улучшения системы эта диагностика критична.

Результаты бенчмарка: 25 пар Ubuntu .deb​

Patch2Vuln оценён на 25 Ubuntu .deb package pairs: 20 реальных security-обновлений и 5 negative controls (пакеты без security-фиксов). Каждая пара вручную верифицирована против ground truth - исходного патча и бинарного маппинга функций. Не synthetic benchmark - реальные обновления из Ubuntu security pipeline.

Агрегированные метрики:
  • Локализация патча: security-relevant функция идентифицирована в 10 из 20 security-пар (50%)
  • Root-cause класс: корректно определён в 11 из 20 (55%) - в одном случае root cause определён верно без точной локализации функции, через косвенные признаки в call graph
  • Negative controls: все 5 корректно классифицированы как «unknown», ни одного false positive, ни одного сгенерированного validation differential
  • Behavioral validation: 2 из 20 пар (обе - tcpdump) получили минимизированный old/new behavioral differential - реальное, воспроизводимое различие в обработке конкретного ввода
Разбор причин неудач - самая ценная часть работы. Oracle-диагностика (оценщик с полным доступом к ground truth) показала:
  • 6 пар: отказ до начала рассуждений модели. Бинарный диффер или ранкер не включил security-relevant функцию в набор кандидатов. LLM в принципе не мог её увидеть - нечего анализировать
  • 1 пара: ошибка экспорта контекста. Функция попала в кандидаты, но декомпилированный контекст был урезан или некорректно передан агенту
  • 3 пары: ошибки рассуждения модели. Функция видна, контекст передан, но агент неверно интерпретировал diff
Соотношение 6:1:3 (диффер : экспорт : модель) - ключевой результат. Основной bottleneck в автоматическом анализе бинарных патчей - не LLM-компонент, а бинарный diff и ранжирование. Ghidriff, как BinDiff или Diaphora, не гарантирует, что security-relevant изменение окажется в топе. Функция может быть инлайнена компилятором при -O2, распределена по нескольким call sites после LTO, или замаскирована массовыми изменениями - например, добавлением stack canaries ко всем функциям.

tcpdump (bionic) - агент успешно идентифицировал уязвимость в обработке filter-файлов. Декомпилированный diff показал добавление bounds check в функции чтения BPF-фильтра. Root-cause определён как out-of-bounds read, а bounded validation подтвердила: старый бинарь читал за границами буфера на специально подготовленном вводе, новый корректно отвергал.

Expat - сильная статическая реконструкция. Агент верно определил root-cause класс в XML-парсере, но не сумел построить работающий trigger. Для парсеров это типичная история: уязвимый путь требует специфической комбинации вложенных XML-конструкций, которую сложно сгенерировать автоматически. Статический аудит корректен, behavioral validation - нет.

libarchive - локализация успешна, но финальный root-cause класс консервативен: «possible memory safety issue» вместо конкретного CWE-типа. Для n-day research осторожность оправдана. Conservative final class лучше, чем false positive с high confidence. Я бы тоже так написал в отчёте.

LLM-powered patch diffing в индустрии: подход Bishop Fox​

Patch2Vuln - не единственная попытка натравить LLM на бинарный анализ уязвимостей. Параллельное исследование от Bishop Fox использует другой стек и другую методологию, что позволяет выделить инвариантные паттерны и специфические ограничения каждого подхода.

Bishop Fox берёт Binary Ninja для декомпиляции и BinDiff для сравнения вместо связки Ghidra/Ghidriff. LLM получает два промпта: первый - декомпилированные функции с запросом на именование и описание, второй - текст vendor advisory плюс результаты первого промпта с запросом на итеративное ранжирование функций по релевантности.

Принципиальное отличие: Bishop Fox даёт агенту vendor advisory. Patch2Vuln намеренно работает без него. В реальном n-day research оба сценария встречаются: USN/RHSA/DSA доступны чаще, чем кажется, но зачастую содержат минимум технических деталей.

Bishop Fox протестировали workflow на четырёх CVE с CVSS 9.4+ и тремя моделями (Claude Haiku 3.5, Claude Sonnet 3.7, Claude Sonnet 4):
  • Information disclosure (27 изменённых функций): все три модели разместили уязвимую функцию в Top 5 в каждом прогоне - эталонный результат
  • Format string injection (134 функции): Sonnet 3.7 и 4 - Top 5 в 100% случаев, Haiku провалился полностью
  • Authorization bypass (1400+ функций): Sonnet 3.7 показал приемлемый результат, но средняя стоимость - $35 за тест
  • Stack buffer overflow (708 функций): все три модели провалились. Причина - крайне лаконичный advisory и шум от массового добавления stack canaries в diff. Даже после фильтрации canary-изменений промптом только Haiku 3.5 вышел на Top 25 в 7 из 9 прогонов
Агрегированный результат Bishop Fox: 66% тестов завершились с vulnerable функцией в Top 25. Провал на stack buffer overflow показателен: когда advisory минимален, а diff зашумлён однотипными изменениями, модель теряет опору для ранжирования. У Patch2Vuln advisory нет вообще, но зато нет и ложных сигналов от скупого advisory.

КритерийPatch2VulnBishop FoxРучной reverse engineering
Инструменты diffGhidra + GhidriffBinary Ninja + BinDiffIDA/Ghidra + BinDiff/Diaphora
Входные данные LLMТолько бинарные артефактыБинарные артефакты + vendor advisoryВесь доступный контекст
СтоимостьCompute для Ghidra + LLM inferenceДо $35/тест (1400+ функций)Часы-недели рабочего времени
Когда применимоAdvisory недоступен, массовый триажAdvisory доступен, targeted analysisMulti-function патчи, kernel modules
Когда не работаетSecurity-функция не попала в diff rankingСкупой advisory + шум в diffДефицит времени и экспертизы
Behavioral validationВстроено (bounded local)Вне scopeПолная свобода (QEMU, pwndbg)

Ускорение disclosure window: почему автоматизация patch diffing критична​

Весной этого года два кейса в ядре Linux показали, как сжимается промежуток между публикацией коммита и пониманием его security-значения.

Описание NVD указывает на реверт коммита в algif_aead, устраняющий in-place операцию при обработке AEAD. CVE добавлен в CISA KEV как actively exploited. Суть: взаимодействие интерфейса AF_ALG и системного вызова splice() при in-place операции в AEAD на данных из разных mappings создавало потенциал для нежелательной модификации данных. Page cache - общий системный кэш; in-place операция на данных из разных mappings создавала потенциал для порчи кэшированных страниц. Детали возможной эксплуатации в публичных источниках ограничены.

Серия связанных фиксов splice/SKBFL_SHARED_FRAG в сетевой подсистеме: CVE-2026-43284 (xfrm/ESP-in-UDP, CVSS 8.8 HIGH, CWE-123 Write-what-where Condition, вектор CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) и CVE-2026-43500 (rxrpc, CVSS 7.8 HIGH, CWE-787 Out-of-bounds Write). Обе связаны с тем, что splice() через __ip_append_data прикрепляет страницы из pipe к skb, а обработчики xfrm/ESP-in-UDP и rxrpc не проверяют флаг SKBFL_SHARED_FRAG и модифицируют shared-страницы. Публичных PoC для этих CVE на момент написания не обнаружено.

Фиксы в upstream выглядят как «avoid in-place decrypt on shared skb frags» или «revert to operating out-of-place». Чтобы разобрать такой коммит вручную, нужны часы работы аналитика, который знает крипто-подсистему ядра и механику splice/page cache. LLM-agent меняет экономику этого процесса в трёх направлениях.

Массовый мониторинг коммитов дешевеет. Pipeline может собирать diffs по подсистемам, классифицировать security-relevance и маршрутизировать подозрительные изменения на человека. Модель не обязана быть точной - достаточно уменьшить стог сена.

Variant analysis (поиск аналогичных паттернов в соседних подсистемах) становится доступнее. Нашли «splice + in-place AEAD = page cache corruption» в algif_aead? LLM неплохо справляется с поиском того же structural pattern в xfrm, rxrpc и остальных потребителях splice.

Параллельное независимое обнаружение одной уязвимости разными командами - известный паттерн в kernel security - становится ещё вероятнее.

Ограничения: где автоматический анализ бинарных патчей не работает​

Patch2Vuln честно фиксирует ограничения, и для практикующего vulnerability researcher они принципиальны.

Binary-diff coverage - главный bottleneck. Если компилятор агрессивно инлайнит функцию, меняет layout кода из-за LTO (Link-Time Optimization), или security-fix распределён по нескольким translation units - диффер может не выделить ключевое изменение. 6 из 20 неудач Patch2Vuln - здесь. Для пакетов, собранных с -O2 -flto, покрытие diff систематически хуже, чем для -O0 или -Og.

Behavioral validation остаётся нерешённой. Из 20 security-пар только 2 получили behavioral differential. Генерация PoC-ввода - фундаментально сложная задача: deep state bugs в парсерах, race conditions, kernel-специфичные пути - всё это за пределами текущих возможностей agent-подхода. Crash - самый простой сигнал, но далеко не все уязвимости приводят к crash. CWE-125 out-of-bounds read, как в CVE-2014-0160 Heartbleed, проявляется утечкой данных, а не падением процесса.

Контекстное окно конечно. Декомпилированный код сложных функций ядра может занимать тысячи строк. Pipeline вынужден обрезать контекст. Одна из 20 неудач - ошибка экспорта контекста: функция попала в кандидаты, но урезанный декомпилированный фрагмент лишил агента ключевой информации. Классическая ситуация: самое интересное оказалось за границей окна.

False positive rate не оценён при масштабе. Все 5 negative controls отвергнуты - отлично, но 5 образцов статистически ничего не доказывают. Реальный поток обновлений содержит десятки non-security пакетов на каждый security-фикс.

Kernel modules - за пределами бенчмарка. Patch2Vuln тестировался на userspace-бинарниках (tcpdump, libexpat, libarchive). Модули ядра с RCU-секциями, memory barriers, per-CPU структурами - совсем другой зверь. Use-after-free в io_uring или race condition в netfilter требует понимания контекста, который декомпилятор передаёт плохо: lifetime аллокаций, порядок блокировок, атомарность операций.

Стоимость масштабирования. Bishop Fox показали: authorization bypass с 1400+ функций стоит $35 за прогон на Claude Sonnet 3.7. Для массового триажа сотен .deb-пар бюджет набегает серьёзный. Локальные модели - потенциальная альтернатива, но их качество на security reasoning пока не бенчмаркировано в аналогичных условиях.

Patch2Vuln задаёт правильную рамку: это не «автоматическая генерация эксплойтов», а исследование глубины, на которую LLM-агент проникает в семантику security-патча при наличии только бинарных артефактов. 10 из 20 - половина, но с прозрачной диагностикой провалов, позволяющей целенаправленно улучшать bottleneck-компоненты.

Два года работы с LLM в контексте ELF binary analysis убедили меня в одном: модели хорошо справляются со structured reasoning по готовому контексту, но плохо - с extraction этого контекста из сырых бинарей. Patch2Vuln количественно подтверждает эту интуицию. Следующий рывок будет не в промптинге и не в более мощных моделях, а в дифферах и ранкерах. Если Ghidriff или его наследники начнут надёжно выделять security-relevant изменения в оптимизированных бинарниках с учётом compiler-induced noise (stack canaries, CFI instrumentation, register allocation changes), то 10/20 превращается в 16/20 без замены LLM-компонента. Формула на бумаге понятна, но реальное ощущение bottleneck появляется только когда сам прогоняешь pipeline на дюжине .deb-пар и видишь, как ранкер раз за разом кладёт security-функцию на 50-е место из 200 кандидатов. Готовый стенд для отработки подобных навыков - от бинарного артефакта до восстановленной логики уязвимости - есть на HackerLab.pro в категории reverse.

Для defensive teams вывод практический: patch intelligence - мониторинг upstream-коммитов с автоматической классификацией security-relevance - перестаёт быть nice-to-have и становится baseline control. Для offensive research вывод жёстче: n-day window сокращается для всех, и конкурентное преимущество теперь не в «знаю LLM» (его знают все), а в качестве binary-diff pipeline, speed validation loop и, как обычно, в глубине понимания target subsystem. Если хочешь работать с binary patch analysis на реальных ELF-артефактах - на HackerLab лежит сценарий, где бинарный primitive нужно собрать в полную цепочку.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab