惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
V
V2EX
博客园_首页
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recent Announcements
Recent Announcements
博客园 - 司徒正美
Microsoft Security Blog
Microsoft Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Latest news
Latest news
Vercel News
Vercel News
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
N
Netflix TechBlog - Medium
WordPress大学
WordPress大学
小众软件
小众软件
L
Lohrmann on Cybersecurity
GbyAI
GbyAI
P
Privacy & Cybersecurity Law Blog
T
Tor Project blog
AWS News Blog
AWS News Blog
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
K
Kaspersky official blog
B
Blog RSS Feed
G
Google Developers Blog
量子位
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
Intezer
雷峰网
雷峰网
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
IT之家
IT之家
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
The Hacker News
The Hacker News
U
Unit 42
S
SegmentFault 最新的问题
I
InfoQ
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
罗磊的独立博客
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Иголка в стоге сена: как LLM помогают искать уязвимости
Сергей Зыбнев · 2026-05-15 · via Все публикации подряд на Хабре

Примечание: изначально я хотел написать одну большую статью, которая закрыла бы всю методологию и технические детали приемов, которыми я пользуюсь: дифференциальный фаззинг на базе ИИ, grammar-based fuzzing, автоматическую генерацию harness-ов и связанные с этим рабочие процессы. Но я понял, что если упаковать все это в один текст, он станет непомерно плотным и тяжелым для чтения.

Поэтому перед вами первая часть. Здесь я даю верхнеуровневый обзор методологии и ключевых идей, на которых строится мой подход к использованию LLM. В следующих публикациях я подробнее разберу технические детали и реализацию каждого компонента.

Все, что описано ниже, предназначено строго для обучения и исследований. Любое неправильное использование или вредоносная активность на его основе остаются на совести того, кто этим занимается. 

Содержание

  1. Вступление

  2. Почему запрос «найди все уязвимости» не работает

  3. Минимальный каркас, который действительно помогает

  4. Кейс: Claude Opus 4.6 и Firefox

    • Что на самом деле сделала Anthropic

  5. Моя методология

    • Подход

      • Parse Server

      • HonoJS

      • ElysiaJS

      • harden-runner

      • BullFrog

      • Better-Hub

    • Найденные уязвимости

    • Почему это сработало

  6. Золотая середина

  7. Prompt Injection

  8. Источники

Вступление

За последние несколько недель я отправил довольно много репортов об уязвимостях. Небольшая их часть уже исправлена и раскрыта через бюллетени безопасности. Все они найдены исключительно с помощью LLM, без какого-либо ручного ревью исходного кода. Проекты, в которых я нашел эти проблемы, хорошо известны и широко используются. Среди них есть известные вроде Parse Server, HonoJS, ElysiaJS, Harden Runner и еще около десятка заметных проектов.

На мой взгляд, это доказывает: агентные CLI/TUI-инструменты вроде OpenAI Codex без всяких сомнений могут помогать находить серьезные уязвимости. Но как именно использовать их так, чтобы выявлять неочевидные проблемы? По итогам экспериментов и тысяч и тысяч промптов, отправленных в попытках найти уязвимости, я пришел к нескольким выводам. Возможно, они небезупречны с теоретической точки зрения, но это самые практичные выводы, к которым я смог прийти. 

Я обнаружил, что проще всего пропустить важную уязвимость так:

  1. переусложнить аудит безопасности избыточным каркасом и цепочками промптов;

  2. раздуть файлы AGENT.md и SKILLS.md;

  3. засыпать модель контекстом — документами или подробным планированием каждого шага процесса;

  4. пытаться оркестрировать слишком многое одновременно.

Звучит контринтуитивно, верно? 

Здравомыслящий человек скажет: чем больше направляющих инструкций, тем лучше результат. Но у систем с длинным контекстом есть вполне реальная и хорошо изученная проблема. Чем больше токенов вы набиваете в окно контекста, тем хуже модель отбирает нужные детали. Недавние работы называют это context rot: качество становится все менее стабильным по мере роста длины контекста, даже когда добавленный контент формально релевантен. Аудит безопасности — едва ли не худшая среда для такой проблемы. «Иголка» здесь обычно представляет собой одно тонкое нарушение ожидаемого свойства системы, зарытое среди тысяч корректных строк. По моим наблюдениям, во многих случаях модели ведут себя в соответствии с принципом первичности и недавности: они лучше справляются, когда «иголка» находится в начале или конце контекста, и хуже — когда она спрятана в середине. Это и есть проблема иголки в стоге сена в ее чистом виде. 

Так что же делать? Выбросить AGENTS.md и пустить LLM свободно бродить по коду без всякого каркаса? Это ведет к еще более серьезным проблемам, но это тема для другой статьи. Пока что, опираясь на тесты и опыт поиска более чем десятка CVE в популярных опенсорс-проектах, я для себя зафиксировал такой подход: минимальный постоянный каркас, максимально точечное исследование и проверка, а также рабочий процесс, который удерживает внимание модели на главном. 

Почему запрос «найди все уязвимости» не работает

Допустим, у вас есть большая папка с монолитным исходным кодом или вы только что клонировали репозиторий с GitHub и хотите найти в нем уязвимости. Первое, что вы делаете: запускаете Codex и пишете промпт «найди все уязвимости в этой кодовой базе». Этот промпт не работает по двум конкретным причинам.

Когда вы написали такой промпт, вы не указали модель угроз. В результате у LLM нет понимания импакта от своих находок. Она может вывести какую-то модель угроз сама, но, как показывают мои эксперименты, обычно либо не делает этого вовсе, либо делает плохо. Без нормальной модели угроз, границ доверия, возможностей атакующего и предварительных условий вы получите длинный список общих CWE-подобных гипотез без приоритизации. У вас не будет способа отличить интересные находки от шума.

Вторая причина в том, что такой промпт подталкивает модель к breadth-first hallucination. Мы знаем, что широкие промпты провоцируют широкие ответы. Широкие промпты порождают широкие ответы. Модель начинает сопоставлять код с распространенными классами багов даже там, где они в вашем конкретном контексте невозможны. В итоге вы будете ревьюить теоретические уязвимости во фрагментах кода, до которых атакующий никогда не доберется.

Минимальный каркас, который действительно помогает

Раз мы поняли, к чему ведет расплывчатый промпт, давайте сделаем все правильно. Прежде чем просить LLM провести аудит кода на уязвимости, попробуйте сделать то, что делают ИБ-команды: определите модель угроз. Кстати, ее тоже можно сгенерировать с помощью LLM. Обычно я ищу ранее раскрытые CVE в проекте и на основе их описаний прошу LLM построить модель угроз для правдоподобных классов багов.

Например, если ранее раскрытые CVE были связаны с heap overflow, stack overflow, integer overflow и memory corruption, LLM попытается построить модель угроз вокруг таких уязвимостей, потому что их уже признали реальными проблемами.

Дальше берете только что созданный документ с моделью угроз, скармливаете его Codex и просите LLM поискать связанные правила безопасности. Или, например, найти коммиты, которые исправляли эти уязвимости, а затем поискать обходы этих исправлений. С большой вероятностью это принесет больше уязвимостей, чем расплывчатый промпт. 

В этом случае весь каркас сводится к созданию модели угроз. Никаких skills.md или agents.md вы не пишите, не пытаетесь ничего оркестрировать. Просто создали модель угроз, отдали ее LLM, и теперь модель углубленно исследует кодовую базу в поиске уязвимостей, которые попадают в эту модель. 

Когда вы закончите искать уязвимости из тех же категорий, что и ранее раскрытые CVE, попробуйте определить точки входа: HTTP-маршруты, RPC-обработчики, потребители сообщений, CLI-точки входа и запланированные задачи. Обозначьте границы доверия: браузер ↔ сервер, сервис ↔ сервис, плагин ↔ хост, песочница ↔ привилегированная часть. Выделите операции повышенного риска: десериализацию, шаблонизацию, нативные биндинги, проверки авторизации и парсинг недоверенных данных. И явно зафиксируйте модель «атакующий — жертва»: например, вас интересуют уязвимости, которые может вызвать удаленный неаутентифицированный пользователь, удаленный аутентифицированный пользователь с низкими правами или кросс-тенантный пользователь. 

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

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

Кейс: Claude Opus 4.6 и Firefox

В публикации Anthropic от 6 марта 2026 года описано сотрудничество с Mozilla, в ходе которого Claude Opus 4.6 нашел 22 уязвимости примерно за две недели, причем Mozilla оценила 14 из них как high severity. Собственный пост Mozilla подтверждает результат и подчеркивает, почему это сработало на практике. Отчеты приходили с минимальными тестовыми примерами, что ускоряло воспроизведение и исправление багов, а команда расширила технику за пределы JS-движка на другие части браузера.

Что на самом деле сделала Anthropic

Описание исследования Anthropic не сводится к тому, что «мы написали мега-промпт». Они начали с узкого фрагмента кодовой базы — движка JavaScript, поскольку он имеет критическое значение и поддается анализу в изолированном виде. Дальше команда работала короткими итерациями. 

Claude обнаружил уязвимость use-after-free примерно через 20 минут исследования, люди подтвердили находку, и команда отправила отчет в Bugzilla с патчем-кандидатом. Как только рабочий процесс доказал свою эффективность, его масштабировали. В итоге просканировали около 6000 файлов C++ и отправили 112 уникальных отчетов. Большинство исправлений вошло в Firefox 148.0 (выпущен 24 февраля 2026 года).

Правильный ли это подход? Может, да, может, и нет. Дело в том, что многие из обнаруженных Anthropic уязвимостей наверняка были невалидными. 

От команды требовалось сообщать об эксплуатируемых уязвимостях, а чтобы добраться до эксплуатируемых векторов, нужно пройти путь от потенциальной уязвимости к ее валидации и далее — к подтверждению эксплуатируемости. Эта цепочка обходится очень дорого. Anthropic потратила на нее примерно 4000 долларов в API-кредитах. Можем ли мы позволить себе такие траты на аудит кода? Скорее всего, нет. Но работаем ли мы с такими же масштабами кода, как в Firefox? Тоже нет. 

Anthropic сделала это почти без каркаса. Но я выступаю за другой подход: использовать минимальный каркас в виде модели угроз и сначала описывать границы доверия. 

Сейчас я говорю только про поиск уязвимостей. Я не углубляюсь в их оценку и отсев ложных срабатываний. Есть способы двигаться в эту сторону, но это, наверное, тема для другого поста. Пока что можно сделать так: если после создания модели угроз LLM находит какие-то уязвимости, можно поручить Codex собрать исходники, запустить локальный экземпляр или написать тесты, которые доказывают существование уязвимостей. В большинстве случаев это работает.

Моя методология

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

Подход

Каждый аудит начинался одинаково. Выбрать тонкий срез и понять его модель доверия до того, как просить LLM искать уязвимости в кодовой базе.

Parse Server

Подробный разбор: Four Vulnerabilities in Parse Server

Parse Server — опенсорсный backend-фреймворк, который предоставляет REST API, запросы в реальном времени, push-уведомления и облачные функции. Он поддерживает несколько механизмов аутентификации, включая readOnlyMasterKey. Документация обещает, что этот ключ дает чтение на уровне master, но запрещает любые записи. 

Прежде чем просить LLM что-либо искать, я собрал ранее раскрытые CVE для Parse Server. Прошлые бюллетени демонстрировали повторяющийся паттерн ошибок применения политик авторизации: проверки привилегий существовали, но были неполными или непоследовательно применялись в разных обработчиках маршрутов. Я скормил описания этих CVE LLM и попросил сгенерировать модель угроз для правдоподобных классов багов на основе этой информации. Модель определила контроль границ авторизации как доминирующую категорию риска, что логично для архитектуры Parse Server с несколькими типами ключей и разными уровнями привилегий. 

Эта модель угроз подсветила readOnlyMasterKey как интересную границу доверия. Утверждение здесь простое: один тип ключа должен иметь строго меньше возможностей, чем другой. Я направил LLM на эту границу и попросил изучить, как разные типы ключей взаимодействуют со слоем авторизации, какие предположения код делает о разделении привилегий и где эти предположения могут сломаться.

LLM вернула карту поверхности атаки, которая подсветила паттерн: несколько обработчиков маршрутов ограничивали доступ по isMaster, но никогда не обращались к isReadOnly. Это был сигнал. Я сузил промпт: попросил перечислить все обработчики с таким паттерном и проследить, может ли read-only credential через любой из них дойти до операций записи или изменения состояния.

Три из четырех уязвимостей (CVE-2026-29182, CVE-2026-30228, CVE-2026-30229) имели одни и те же предпосылки. Когда они подтвердились, я открыл отдельный срез по адаптерам социальной аутентификации с таким же направленным подходом. Я нацелил LLM на слой адаптеров аутентификации и попросил изучить поток проверки токена: какие claims проверяются, что происходит при частичной или отсутствующей конфигурации, и где валидация может незаметно деградировать.

LLM указала на путь валидации JWT audience как на слабое место, а уточняющий промпт подтвердил четвертую находку — CVE-2026-30863, независимый обход JWT audience validation, где адаптер молча пропускал проверку aud, если конфигурация была неполной. Другой срез, но тот же подход.

HonoJS

Подробный разбор: HonoJS JWT/JWKS Algorithm Confusion

HonoJS — легковесный высокопроизводительный web-фреймворк для JavaScript и TypeScript, работающий в нескольких средах выполнения, включая Cloudflare Workers, Deno, Bun и Node.js. У него есть встроенная middleware для JWT- и JWKS-аутентификации.

Я начал с обзора бюллетеней безопасности Hono и ранее раскрытых проблем в middleware аутентификации. Публичных CVE нашлось немного, но если добавить к ним общий паттерн ошибок реализации JWT в соседних проектах, картина складывается. Когда я попросил LLM построить модель угроз, она ожидаемо отнесла работу с алгоритмами к зоне высокого риска и выделила два правдоподобных класса багов: algorithm confusion и небезопасный fallback. Так у меня появился четкий фокус — пути верификации JWT и JWKS. 

С этой моделью угроз я направил LLM изучать логику выбора алгоритма в JWT middleware. Вместо того чтобы спрашивать о конкретных уязвимостях, я попросил пройтись по мисконфигам: какие дефолтные небезопасные настройки забывают поменять, какие fallback-пути существуют и как middleware решает, какому алгоритму доверять. Цель была в том, чтобы LLM построила дерево решений выбора алгоритма и отметила ветки, где middleware может делать небезопасные допущения.

В анализе LLM подсветила два потенциальных уязвимых паттерна. Во-первых, она обнаружила fallback на HS256, когда алгоритм не закреплен явно. Во-вторых, отметила поведение JWKS middleware, которое доверяло значению header.alg из токена, если в объекте JWK key отсутствовало поле alg. По каждому случаю я продолжил точечными промптами: попросил LLM проследить точные условия, при которых атакующий может проэксплуатировать эти fallback’и.

В итоге нашлись две проблемы класса algorithm confusion. CVE-2026-22817 заключалась в том, что JWT middleware по умолчанию переходила на HS256, если алгоритм не был закреплен, что позволяло атакующему подписывать токены публичным ключом как HMAC secret. CVE-2026-22818 заключалась в том, что JWKS middleware fallback-илась на недоверенное значение header.alg, когда в JWK не было поля alg, позволяя атакующему диктовать, какой алгоритм сервер использует для проверки.

ElysiaJS

Подробный разбор: ElysiaJS Cookie Signature Validation Bypass

ElysiaJS — TypeScript web-фреймворк для Bun, ориентированный на type safety и удобство разработчика. У него есть встроенная обработка cookies с проверкой целостности через подпись и поддержкой ротации секретов. 

Генерация модели угроз шла по тому же сценарию. Я посмотрел документацию ElysiaJS. Когда я передал этот контекст LLM и попросил определить правдоподобные классы багов, модель отметила логику проверки подписи как зону высокого риска, особенно путь ротации секретов, где одновременно может быть валидно несколько signing-ключей, а логика проверки должна корректно отклонять cookie, которые не совпали ни с одним из них.

Это дало мне узкий срез. Я направил LLM на слой подписания и верификации cookie и попросил рассуждать об управлении состоянием во время проверки: как код отслеживает, что подпись успешно проверена, что происходит при переборе нескольких ротируемых секретов, когда ни один не совпадает, нет ли при инициализации допущений, из-за которых логика может молча принять невалидную подпись. 

LLM указала на status-переменную decoded как подозрительную и обратила внимание на ее инициализацию. Следуя этому сигналу, я попросил проследить control flow, когда ни один secret не дает совпадающей подписи. Это подтвердило баг: одна ошибка инициализации булевой переменной let decoded = true вместо let decoded = false означала, что при использовании ротации секретов проверка подписи не могла завершиться неуспешно. CVE пока еще ожидает публикации.

Harden-runner

Подробный разбор: Bypassing Outbound Connections Detection in harden-runner

harden-runner — инструмент безопасности от StepSecurity для GitHub Actions. Он мониторит исходящие сетевые соединения CI/CD runner-ов через instrumentation syscalls и обнаруживает неразрешенный egress. Работает в двух режимах: audit-режим логирует соединения, block-режим активно их предотвращает.

Я изучил прошлые бюллетени harden-runner и его документированную модель безопасности. Вся ценность инструмента держится на полной видимости исходящей сетевой активности, поэтому модель угроз, которую я попросил LLM сгенерировать, сводилась к одному вопросу: «Может ли атакующий с возможностью исполнения кода на GitHub Actions-раннере вывести данные в обход egress-контролей?». LLM указала пробелы в покрытии syscalls как наиболее вероятный класс обхода: инструмент опирается на хукинг конкретных системных вызовов, и любой вызов вне этого набора останется невидимым.

С этой моделью угроз я направил LLM на слой мониторинга syscalls и попросил исследовать поверхность покрытия. Какие семейства системных вызовов hook-аются, какими способами процесс в Linux может отправлять данные по сети, и есть ли разрыв между одним и другим? Идея была в том, чтобы LLM перечислила полный набор сетевых syscalls и затем сравнила его с тем, что harden-runner реально инструментирует. 

LLM вернулась с gap-анализом, где UDP send-family syscalls были отмечены как потенциально не охваченные мониторингом. Я и попросил конкретно проверить, покрываются ли sendto, sendmsg и sendmmsg. Обход оказался ровно таким, как предсказывала модель угроз: эти системные вызовы выпадали из области мониторинга в audit-режиме (CVE-2026-25598).

BullFrog

Подробные разборы: Bypassing egress filtering in BullFrog GitHub Action, sudo restriction bypass in BullFrog GitHub Action, Bypassing egress filtering in BullFrog using shared IP

BullFrog — еще один инструмент безопасности для GitHub Actions, который применяет egress filtering на уровне межсетевого экрана с DNS-aware правилами. В отличие от подхода harden-runner с instrumentation syscalls, BullFrog работает на сетевом уровне: резолвит доменные имена в IP-адреса и применяет firewall rules на основе этих резолвов.

Я снова пошел тем же путем. Изучил документацию BullFrog и модель безопасности, затем попросил LLM сгенерировать модель угроз на основе архитектурного подхода. Общий вопрос был тем же, что и для harden-runner: «Может ли атакующий с исполнением кода на GitHub Actions runner вывести данные в обход egress controls?». Но LLM выделила другой набор правдоподобных классов обхода, потому что enforcement-механизм BullFrog принципиально другой. Модель угроз отметила пограничные случаи DNS-парсинга, логику привязки IP к домену и повышение привилегий как три наиболее вероятные поверхности атаки.

Я разделил аудит на три отдельных среза, у каждого — свое направленное исследование. Для DNS-среза я направил LLM на слой DNS-парсинга и попросил изучить, как агент обрабатывает DNS-трафик на уровне протокола, какие предположения делает о границах сообщений и что происходит с edge cases вроде multiplexed или pipelined messages. Для IP-среза я попросил LLM изучить, как строятся firewall-правила после DNS resolution, отслеживается ли связь между доменом и его resolved IPs и что происходит, когда несколько доменов резолвятся в один адрес. Для среза по привилегиям попросил LLM разобраться, как инструмент ограничивает повышение привилегий на раннере и существуют ли альтернативные пути к повышенному доступу помимо тех, которые он явно блокирует.

LLM подняла конкретные поверхности атаки для каждого среза: DNS parser проверял только первое сообщение в TCP-сегменте, firewall добавлял IP в whitelist без привязки к домену, который вызвал это правило, а членство в группе Docker сохранялось после удаления пользователя из sudoers. Уточняющие запросы по каждому из этих пунктов подтвердили три отдельных bypass-а.

Better-Hub

Подробный разбор: Hacking Better-Hub

Better-Hub — альтернативный фронтенд для GitHub, который отзеркаливает GitHub-контент внутри собственного origin, рендерит Markdown в HTML и хранит GitHub OAuth-токены для аутентифицированных функций.

Здесь модель угроз сложилась не столько из прошлых CVE, которых не было, сколько из самой архитектуры. Когда я описал LLM дизайн Better-Hub, особенно то, что управляемый пользователем GitHub-контент рендерится внутри собственного origin Better-Hub, где в том же контексте доступны OAuth tokens, модель угроз сложилась сама собой: «Что произойдет, если управляемый пользователем контент будет небезопасно отрендерен в контексте, имеющем доступ к сохраненным учетным данным?». LLM выделила три области высокого риска: пайплайн рендеринга Markdown, слой кэширования и авторизации, а также логику обработки OAuth-токенов. 

Каждую из них я аудировал как отдельный срез — через направленное исследование, а не охоту за конкретной уязвимостью. Для rendering-среза я направил LLM на пайплайн обработки Markdown и попросил изучить поток данных: как сырой контент из GitHub-репозиториев преобразуется перед попаданием в браузер, какие шаги санитизации существуют и где недоверенный ввод может выжить в pipeline. Для caching-среза я попросил LLM посмотреть, как ответы кэшируются и отдаются, знает ли caching layer об authentication context, и что происходит, когда cached response из private repository запрашивает другой пользователь. Для OAuth-среза я попросил изучить, как хранятся и скорятся токены, доступны ли они из клиентского контекста и как выглядит жизненный цикл токенов.

Каждый срез принес отдельные находки, точно соответствующие поверхностям атаки, определенным LLM. Пайплайн рендеринга дал шесть вариантов XSS, все из одного и того же пути рендеринга Markdown без санитизации. Caching-срез выявил два cache-based authorization bypass, при которых контент из приватных репозиториев утекал неаутентифицированным пользователям. Остальные находки включали утечку приватных prompt-данных, раскрытие OAuth-токена на клиентской стороне и open redirect. Всего одиннадцать уязвимостей в трех срезах.

Найденные уязвимости

Примечание: это лишь небольшая часть уязвимостей, которые я нашел с помощью методологии из этой статьи; многие все еще ждут исправления или раскрытия.

Цель

Уязвимостей

Диапазон серьезности багов

CVE

Parse Server

4

Critical — Moderate

CVE-2026-29182, CVE-2026-30228, CVE-2026-30229, CVE-2026-30863

HonoJS

2

High

CVE-2026-22817, CVE-2026-22818

ElysiaJS

1

High

Pending (cookie signature bypass)

harden-runner

1

Moderate

CVE-2026-25598

BullFrog

3

High

DNS pipelining, sudo bypass, shared-IP bypass

Better-Hub

11

Critical — Low

XSS chain, cache deception, OAuth leak

Почему это сработало

Ни в одном из этих аудитов не использовался гигантский чеклист, двадцатистраничный промпт-каркас или «всеобъемлющий» security framework. Каждый начинался с короткой модели угроз, обычно укладывающейся в одно предложение, и сфокусированного среза кодовой базы, который напрямую соответствовал границе доверия или критически важной для безопасности операции. Каркас был минимальным, но это был правильный каркас. Он указывал LLM ровно на то, какое правило безопасности проверять и где смотреть.

Золотая середина

Хороший каркас — это модель угроз на одну страницу, короткий список критически важных функций системы и небольшой набор проверяемых условий вроде «вызывать X имеют право только админы» и «JWT issuer должен быть равен Y». Плохой каркас — это двадцатистраничный Agent.md со всеми политиками и гайдами по стилю, огромная библиотека Skill.md, заранее загружающая каждый security-чеклист, и повторение одних и тех же шаблонных инструкций на каждом шаге. Если ваш каркас сам превращается в стог сена, уязвимость превращается в иголку — а данные о работе моделей с длинным контекстом показывают, что иголки тем чаще теряются, чем больше стог. 

Разбивайте аудит на тонкие срезы, соответствующие реальным поверхностям атаки. Выберите срез: утентификация, управление сессиями, разбор запросов, загрузка файлов, десериализация, граница песочницы или граница плагина. Попросите модель сопоставить entry points этого среза с sensitive sinks. Требуйте доказательства в виде точных цепочек вызовов, защитных проверок, контрольных условий и указания, какие входные данные контролирует атакующий.

Не полагайтесь на «модель сказала, что это уязвимо». Используйте верификаторы задач: unit и integration tests, sanitizer builds и crash reproduction harnesses для native code, fuzzers, даже легковесные, статический анализ и grep-based-проверки условий, а также policy checks вроде «authz обязан закрывать эти endpoints».

Тратьте токены на покрытие и проверку, а не на бюрократию промптов. Практическое правило: меньше 10% токенов на стабильный каркас, то есть модель угроз и проверяемые условия, 60–80% на slice audits в сфокусированных контекстах и 20–30% на цикл, чтобы доказать, воспроизвести, минимизировать и исправить.

Prompt Injection

Правильный срез и модель угроз приводят вас в нужный «район», но когда вы уже там, формулировка промптов сильно влияет на то, получите ли вы список общих наблюдений или настоящую эксплуатируемую находку. За тысячи промптов в десятках аудитов я обнаружил, что некоторые приемы промптинга стабильно работают лучше других. Я называю их prompt injections, потому что вы внедряете в рассуждение модели рамку, смещение или ограничение, которое сдвигает ее поведение в полезную сторону. Вот техники, которые сработали для меня лучше всего.

Утверждайте, что уязвимость существует

Самый эффективный прием, который я нашел. Когда я говорил LLM «эта функция точно уязвима, в ней минимум 2–3 проблемы безопасности», качество анализа резко поднималось по сравнению с запросом: «уязвима ли эта функция?».

Причина проста. По умолчанию у LLM сильная склонность к согласию и подтверждению. Когда вы спрашиваете «это уязвимо?», путь наименьшего сопротивления для модели — ответить «в целом это выглядит безопасным, есть несколько мелких замечаний» и выдать список теоретических проблем. 

Когда вы утверждаете, что уязвимости есть, вы меняете цель оптимизации модели. Вместо того чтобы оценивать, есть ли уязвимости, она начинает искать баги, про которые вы сказали. Она читает код внимательнее, рассматривает пограничные случаи, которые иначе пропустила бы, и выдает находки с реальной конкретикой. 

По сути, вы обходите склонность модели быть успокаивающим code reviewer и переключаете ее в состояние человека, который знает, что баг там точно есть, и должен его найти. Это работает даже тогда, когда у вас нет предварительных оснований считать функцию действительно уязвимой.

Просите эксплоит, а не оценку

Вместо вопроса «Достаточна ли эта input validation?» попросите LLM: «Напиши proof-of-concept request, который обходит эту input validation». 

Это вынуждает модель производить конкретный, проверяемый результат, а не отделываться качественными оценками. Когда модель должна реально собрать вредоносный payload, ей приходится пошагово рассуждать, что код делает с этим вводом, где находятся проверки и как их пройти. Если валидация действительно надежна, модель будет испытывать трудности с рабочим payload, и часто прямо в процессе генерации поймет, что задуманный обход не работает. Это само по себе — полезный сигнал. Если же валидация сломана, вы получите рабочий PoC.

Настраивайте модель как атакующего, а не аудитора

Фрейминг важнее, чем большинство ожидает. «Ты security auditor, который ревьюит этот код» дает фундаментально другое распределение ответов, чем «Ты red team operator, которому заплатили за взлом этого приложения, и тебе нужно найти реальные эксплуатируемые баги, чтобы оправдать engagement». Аудиторская роль подталкивает модель к полноте и тщательности. Звучит хорошо, но на практике дает списки наблюдений с низким сигналом. Red team-фрейм смещает модель в сторону импакта и эксплуатируемости. Она начинает думать о том, что атакующий реально получает, какие предварительные условия нужны и является ли находка реальной или теоретической. 

Роль атакующего делает модель смелее в выводах: она охотнее доходит до неудобных формулировок вроде «этот механизм аутентификации фундаментально сломан» — вместо того чтобы смягчать их до «это можно было бы улучшить». 

Используйте ложную привязку, чтобы создать поисковое давление

Это вариант техники утверждения. Скажите LLM: «Я уже нашел одну уязвимость в этом модуле, но там есть и другие, которые я еще не нашел. Какие?». Это создает давление социального доказательства. Модель рассуждает, что если вы, человек, уже нашли один баг, то код действительно багованный, и нужно искать тщательнее. Это также меняет prior модели. Вместо старта с «этот код, скорее всего, нормальный» она начинает с «в этом коде есть подтвержденные баги, значит, вероятность дополнительных багов выше». Получается особенно эффективно, когда вы действительно уже нашли один баг и хотите понять, есть ли в том же модуле другие. В этом случае привязка честная, но техника работает, даже когда привязка выдумана.

Переворачивайте вопрос

Вместо «Безопасен ли этот код?» спрашивайте «Как бы ты это сломал?». Перестановка кажется тривиальной, но она радикально меняет задачу модели. «Это безопасно?» — задача бинарной классификации, и по умолчанию модель тяготеет к «да». «Как бы ты это сломал?» — генеративная задача без легкого ответа по умолчанию. Модель должна выдать стратегии атаки, а это заставляет смотреть на код с позиции атакующего. 

Я обнаружил, что инвертированные промпты дают в 2–3 раза больше практически применимых находок, чем их прямые аналоги, потому что модель не может удовлетворить запрос фразой «Выглядит нормально». Ей приходится действительно пробовать.

Раскладывайте на допущения и потом нарушайте их 

Сначала попросите LLM перечислить каждое условие корректности, допущение или предусловие, на которое функция опирается для корректной работы, и только потом — проверить, выполняется ли каждое из них. 

Например: «Перечисли каждое допущение, которое эта функция аутентификации делает относительно своих входных данных, окружения и вызывающего кода. Теперь для каждого допущения скажи, может ли атакующий его нарушить». Такая двухшаговая декомпозиция эффективна, потому что она разделяет задачу перечисления и задачу оценки. Модель хорошо перечисляет предположения, когда это ее единственная задача. И она хорошо рассуждает о выполнимости одного допущения за раз. Соединение того и другого в одном промпте часто дает поверхностный результат: модель пытается делать все сразу и слишком рано останавливается на удовлетворительном на вид ответе.

Предполагайте, что разработчик ошибся

Сформулируйте промпт так: «Предположим, что разработчик допустил ошибку в этой функции, что это за ошибка?» Это отличается от техники утверждения. Утверждение говорит модели, что баги там есть. А эта техника говорит ей исходить из несовершенства разработки, что меняет ее исходное представление о качестве кода. 

LLM склонны рационализировать код как корректный. Когда они видят паттерн, они часто предполагают, что он намеренный, и рассуждают дальше из этого предположения. Если сказать модели, что ошибка была допущена, это обрывает рационализацию. Модель начинает искать вещи, которые не имеют смысла, а не объяснять, почему они смысл имеют. Баг ElysiaJS с let decoded = true — идеальный пример. Модель, рационализирующая код, могла бы сказать: «Разработчик зачем-то инициализировал это в true». Модель, ищущая ошибки, тут же отметит, что начальное значение неправильное. 

Используйте сравнительные промпты против эталонных паттернов

Спросите LLM: «Чем эта реализация отличается от стандартной безопасной реализации такого паттерна?». Это задействует обучающие данные модели, где есть тысячи примеров корректных и некорректных реализаций распространенных паттернов: JWT validation, session management, CSRF protection и так далее. Когда вы просите найти разницу между тем, что делает код, и тем, что должна делать его безопасная версия, вы заставляете модель выполнять структурированное сравнение, а не открытое ревью. Это особенно эффективно для криптографического кода и кода аутентификации, где обычно есть один правильный способ и много неправильных. Модель отлично замечает отклонения от канонической реализации, если ее об этом прямо попросить.

Итеративно повышайте планку фразой «что еще?»

Получив первую партию находок от LLM, не считайте ее полной. Дожмите: «Это очевидные вещи. Какие более тонкие проблемы здесь легко пропустить?» или «Отложи все, что связано с [уже найденным классом багов]. Какие еще классы уязвимостей тут есть?».

Это работает, потому что LLM сначала выдают наиболее вероятные продолжения. 

Первые находки — те, в которых модель уверена больше всего, и они обычно самые очевидные. Тонкие баги, требующие более глубокого рассуждения или необычных моделей атаки, — это менее вероятные продолжения, и модель их не сгенерирует, пока вы прямо не вытолкнете ее за пределы очевидного. Каждое «а что еще?» сдвигает модель дальше в хвост распределения, где как раз и живут интересные находки. Обычно я делаю 2–3 таких раунда, пока сигнал не начнет деградировать. 

Явно ограничивайте модель атакующего

Вместо общего «найди уязвимости» задайте точную модель атакующего: «Ты — удалённый неаутентифицированный атакующий, который может только отправлять HTTP-запросы к публичному API. У тебя нет доступа к файловой системе, базе данных или внутренним сервисам. Найди все способы повысить свой доступ или причинить вред исключительно через публичный API». Это ограничение делает две важные вещи. Во-первых, отсекает целый класс ложных срабатываний. Модель не станет сообщать «Атакующий с доступом к базе данных может изменить эту таблицу», потому что вы явно это исключили. Во-вторых, заставляет модель творчески мыслить внутри ограничения. Когда модель атакующего широкая, LLM идет легким путем и сообщает о самом мощном векторе. Когда она узкая, модели приходится напрягаться, чтобы найти жизнеспособные пути атаки, — а именно такие труднонаходимые пути и эксплуатируют реальные атакующие, потому что защитники их упускают. 

Источники