Исследователи AIR Security описали Plugin4Shell — zero-click RCEудалённое выполнение кода, для срабатывания которого не требуется ни одного действия пользователя в цепочке поставок плагинов. Уязвимость затрагивает четыре крупнейших ИИ-агента для кодинга: Claude Code, Codex, GitHub Copilot и Gemini CLI. Атакующий, получивший контроль над репозиторием плагина, заставляет агент установить чужой код вместо закреплённого коммита — и делает это без установки, запроса или иного заметного действия со стороны разработчика.
Оценка охвата — миллионы установок агентов. Нулевой клик обеспечивается фоновым автообновлением: агент сам перепроверяет и переустанавливает плагины, поэтому подмена доходит до уже установленных расширений.
Что именно изменилось
Речь не о новом классе атак, а о конкретном пропущенном шаге проверки. Все затронутые агенты выполняют checkoutпереключение рабочего каталога на выбранную версию кода в репозитории закреплённого коммита, но не проверяют, что в рабочем каталоге оказался именно он. Закрепление коммита (pinned SHA) — это привязка плагина к конкретному Git-коммиту, чтобы последующие изменения репозитория не влияли на одобренную версию; SHAхеш коммита, по которому Git однозначно находит нужную версию кода. Этого одного пропуска достаточно для эксплуатации.
Механика опирается на поведение Git при неоднозначности имён. Ссылка (ref) — это именованная метка в репозитории, например ветка, указывающая на коммит; объект — это сам коммит, на который метка указывает. Если имя одновременно является допустимой ссылкой и идентификатором объекта, Git предпочитает ссылку и лишь печатает предупреждение о неоднозначности. Этим и пользуется атакующий.
Для Claude Code, Codex и GitHub Copilot схема такая: атакующий создаёт ветку, имя которой в точности совпадает с 40-символьным SHA закреплённого коммита, и делает её веткой по умолчаниюосновной веткой репозитория, на которую Git переключается, если ветка не указана явно репозитория. При checkout Git разрешает имя в ветку, а не в коммит, и в рабочем каталоге оказывается код атакующего. Агент при этом сообщает об успешной установке закреплённого коммита.
У Gemini CLI механизм другой. Клиент получает коммит через git fetch, а затем выполняет git checkout FETCH_HEAD. FETCH_HEADслужебная метка Git, указывающая на коммит, полученный последней командой git fetch. Здесь git checkout FETCH_HEAD не обязан читать файл .git/FETCH_HEAD: если ветка по умолчанию сама называется FETCH_HEAD, checkout разрешается в неё, а полученный коммит молча отбрасывается в пользу содержимого ветки атакующего.
Условие срабатывания для не-Gemini варианта: ветка с именем-хешем должна быть веткой по умолчанию. Ветка, не являющаяся веткой по умолчанию, загружается только как remote-tracking refлокальная метка, отслеживающая состояние ветки в удалённом репозитории, но не используемая для переключения по умолчанию, и checkout откатывается к коммиту.
Предыстория
Plugin4Shell — атака на слой распространения, то есть на маркетплейсы, через которые агентные дополнения попадают на машины. Это не атака на модель или самого агента, а эксплуатация доверия к плагинам, которые агент устанавливает и обновляет автоматически.
В более широком контексте это продолжение серии инцидентов, показывающих, что агент выполняет то, что читает. EchoLeak (CVE-2025-32711, CVSS 9.3), найденный Aim Labs в Microsoft 365 Copilot, — первая публично описанная zero-click атака на ИИ-агента: RAG подтягивает в контекст письмо со скрытыми инструкциями, и пейлоад срабатывает без действий пользователя. CVE-2026-22708 против Cursor показал, что отравление переменных окружения через шелл-встроенные команды обходит allowlist, и одобренные команды вроде git branch становятся носителями пейлоада. Sentry agentjacking (Tenet Security, июнь 2026): в issue-трекер подкладывается «предложение по исправлению» с командой запуска пакета атакующего, агент получает это через MCP как обычный результат инструмента и запускает пакет. s1ngularity (Wiz, август 2025): скомпрометированный пакет Nx печатает в stdout postinstall-скрипта текст, адресованный агенту, и тот публикует инвентарь секретов в публичный репозиторий. Comment-and-Control (апрель 2026): инъекция через заголовки и комментарии в pull request заставляет агента выполнить ps auxeww и запостить переменные окружения обратно в комментарий. Pandora (Trend Micro) при загрузке вредоносного документа Word извлекла встроенную Python-полезную нагрузку и выполнила её, передав данные на C&C.
Масштаб предыдущих эпизодов подтверждает порядок величин: в истории Skills было захвачено более 26 000 агентов, в SkillJacking перехвачено 925 навыков, что затронуло 134 000 агентов.
Почему это сделали возможным
Корневая причина — единственная: агент выполняет checkout закреплённого коммита, но не проверяет, что рабочее дерево действительно оказалось на нём. Атакующий получает контроль над репозиторием двумя путями: либо сам публикует изначально безобидный плагин, проходит проверку и позже подменяет его содержимое на вредоносное, либо захватывает репозиторий, стоящий за плагином другого автора, которому маркетплейс уже доверяет.
Фоновое автообновление превращает подмену в zero-click. Агенты обновляют установленные плагины в фоне, и в Claude Code и Codex это поведение по умолчанию. Тот же checkout запускается повторно, поэтому когда маркетплейс поднимает закреплённый SHA, подмена доходит до уже установленных плагинов без действий пользователя. Уже установленный и доверенный плагин заменяется вредоносной версией без установки, запроса или иного заметного действия.
Plugin4Shell эксплуатирует доверие к автоматически обновляемым плагинам: доверенный плагин незаметно подменяется вредоносным и автоустанавливается в обход SHA-пиннинга, что даёт полную компрометацию агента и хоста, а значит полный доступ ко всем активам и данным, до которых агент может дотянуться.
Что это меняет на практике
Исправление должно поставляться в самом агенте: закрепление разрешается на стороне клиента, поэтому ни один маркетплейс не может обеспечить гарантию, которую рекламирует. Одного контроля каталога расширений недостаточно, поскольку ошибка возникает на стороне агента при разрешении Git-ссылки.
Закрывающая проверка сравнивает фактически оказавшийся в рабочем дереве коммит с закреплённым SHA и прерывает установку при несовпадении:
test "$(git rev-parse HEAD)" = "<pinned-sha>" || abortПроверять нужно именно разрешённый HEAD, а не запрошенный ref — эту разницу пропускает вариант Gemini. И проверка обязана выполняться внутри агента.
Статус исправлений по агентам различается. Anthropic закрыла уязвимость в Claude Code 2.1.179, OpenAI исправила Codex в 0.146.0 — эти агенты следует обновить до указанных версий. Для GitHub Copilot исправление не выпущено: уязвимость раскрыта Microsoft, но патча у пользователей нет. Gemini CLI объявлен устаревшим и патчиться не будет, поэтому каждая установка остаётся уязвимой навсегда; пользователям рекомендуется перейти на Antigravity, до которого эта атака не достаёт — там нет закрепления SHA для плагинов маркетплейса, которое можно было бы обойти.
Ограничения и открытые вопросы
Сканер навыков Anthropic не закрывает этот класс атак. Вредоносные навыки обходят его теми же старыми приёмами, которые работают против любого LLM-based static scan, включая реальные образцы в дикой природе. Причина архитектурная: любой текст в контекстном окне может влиять на поведение модели, и она не отличает доверенные инструкции от недоверенных данных — все входы имеют одинаковый вес доверия. Если недоверенный текст попадает в то же контекстное окно, что и системные инструкции, никакой постфактум-фильтр не закроет дыру надёжно. Классификаторы и гардрейлыдополнительные защитные фильтры, ограничивающие поведение модели работают против вчерашних атак и проваливаются против завтрашних.
Классификатор auto modeрежим автоматического выполнения действий агентом без подтверждения пользователя структурно слеп к каналу непрямых инъекций: результаты инструментов вырезаются из его входа, а сам он — удобство на основе best-effort классификатора, а не гарантия безопасности. Диалог подтверждения не знает, что агент минуту назад прочитал README с инструкцией запустить ровно эту команду. Пользователи подтверждают 97% запросов разрешения, ловя лишь 13,6% опасных команд, тогда как классификатор — 89%. Самодельные хуки-защиты контент-слепы: они судят строку команды на PreToolUseсобытие, наступающее перед вызовом инструмента агентом и ничего не знают о прочитанном.
На архитектурном уровне помогает защита в глубину: даже если инъекция сработает на уровне модели, следующий слой инструментального контроля увидит происхождение и сможет заблокировать действие. OWASP формулирует это как segregation of trustразделение доверия между разными источниками данных и инструкций и подчёркивает, что в 2026 году prompt injectionКласс атак, при котором недоверенный текст попадает в контекстное окно модели и влияет на её поведение, поскольку все входы имеют одинаковый вес доверия и модель не отличает доверенные инструкции от недоверенных данных. и memory/RAG poisoningподмена данных в памяти агента или в базе знаний, подтягиваемой при поиске делают такое разделение архитектурным требованием, а не просто хорошей практикой промпт-инжиниринга. Изоляция инструментов означает предоставление каждому инструменту минимально необходимого набора прав и работу в изолированном контексте. Human-in-the-loop — архитектурный компонент, встроенный в поток выполнения агента, где действия классифицируются по уровню риска и только низкорисковые выполняются автоматически.
Авторы перечисляют и честные ограничения собственных защитных механизмов. Латентность хука — 100–250 мс на вызов, причём доминирует старт процесса Node, а не сам скан; локальный демон лишь в планах. ReDoS смягчён, но не устранён: начавшийся матч нельзя прервать, бюджет по времени проверяется только между правилами и вариантами, а при выходе за бюджет результат трактуется как подозрительный. Обрезание хвоста аудит-лога не детектируется, пока не появятся подписанные чекпоинты. Обходы через shell quote-splicing (c"u"rl, $'curl') известны, quote-aware лексер в планах. Отслеживание происхождения команды работает на уровне текста: агент, перефразировавший команду, по происхождению не поймается. Secret egress смотрит только на значения в аргументах, а раскрытие $VAR происходит в shell уже после того, как защита увидела команду. Ложные срабатывания реальны: как только вывод помечен подозрительным, все его части считаются продиктованными им, включая легитимные setup-команды. И общее для всех hook-based решений: защита не принимает решения о вызове, который агент не пропустил через хук. Открытым остаётся вопрос, полезно ли прерывание отслеживания происхождения для тех, кто гоняет агентов в auto mode.