В 2026 году Anthropic раскрыла CVE-2026-39861 - уязвимость высокой степени серьёзности (CVSS 7.7, High) в Claude Code, через которую можно записывать произвольные файлы за пределами рабочей области с помощью символических ссылок. На первый взгляд - стандартный symlink following, ничего нового. Но при детальном разборе - архитектурно изящная атака: ни sandboxed-команда, ни unsandboxed-приложение по отдельности не нарушают своих ограничений, а вот их комбинация ломает всю модель изоляции. EPSS 0.71% (чуть выше медианы), единственное предусловие - prompt injection в контекстное окно агента. Красиво, если не ты жертва.
Бизнес-логика атаки: зачем ломать sandbox AI-инструмента разработки
Claude Code - агентный инструмент, который читает, модифицирует и исполняет код в рабочем пространстве разработчика. Sandbox ограничивает исполняемые команды рамками workspace-директории. Если атакующий выходит за эти рамки, он получает:- Запись произвольных файлов от имени пользователя:
~/.bashrc,~/.ssh/authorized_keys,crontab - Чтение секретов за пределами проекта: API-ключи, credentials, конфигурации CI/CD pipeline
- Закрепление через persistence: cronjob, shell profile injection, git hooks
Технический разбор уязвимости Claude Code CVE-2026-39861
CWE-22 path traversal и CWE-61 symlink following: две слабости одной цепочки
CVE-2026-39861 ассоциирована с двумя CWE, и обе принципиально важны. Это не дублирование - каждая CWE описывает свой аспект одной цепочки эксплуатации.CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) - sandbox Claude Code ограничивает операции записи рамками рабочей директории, но не учитывает, что путь внутри workspace может быть символической ссылкой, ведущей за его пределы. Path traversal здесь реализуется не через канонические
../ последовательности, а через symlink-индirection: путь формально лежит внутри workspace, но резолвится ядром ОС за его пределы. CWE-22 исторически покрывается OWASP A01 (Broken Access Control) - и эта уязвимость характерная иллюстрация класса.CWE-61 (UNIX Symbolic Link Following) - unsandboxed-процесс Claude Code следует по символическим ссылкам при записи файлов, не проверяя, что конечный target находится внутри workspace. Ключевой момент: два процесса с разными уровнями привилегий работают с одной файловой системой, и граница доверия проходит между ними.
Связка CWE-22 + CWE-61 формирует цепочку: sandboxed-процесс создаёт symlink (с точки зрения sandbox - легитимная операция создания файла внутри workspace), а unsandboxed-процесс переходит по нему при записи (CWE-61, отсутствие проверки реального пути). Ни один из процессов по отдельности не нарушает собственных ограничений - уязвимость возникает именно в зазоре между ними. Классический confused deputy, только deputy тут два.
Для надёжной эксплуатации атакующему нужно спровоцировать sandboxed-процесс на создание symlink - это напрямую попадает под OWASP LLM Top 10 2025 - LLM01 (Prompt Injection): crafted user input изменяет поведение LLM, обходя механизмы контроля.
CVSS 4.0 вектор: метрики и интерпретация
Вектор:CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Компонент | Значение | Интерпретация |
|---|---|---|
| AV:N | Network | Prompt injection приходит из внешнего источника: вредоносный репозиторий, npm/pip-зависимость, PR с отравленным кодом |
| AC:L | Low | Сама эксплуатация symlink following тривиальна - стандартная UNIX-операция |
| AT | Present | Предусловие: нужна возможность внедрить контент в контекстное окно Claude Code |
| PR:N | None | Атакующему не нужна аутентификация в системе жертвы |
| UI | Passive | Пользователь просто работает с проектом, содержащим вредоносный контент. Никаких специальных действий не требуется |
| VC:H / VI:H / VA:H | High | Полная компрометация конфиденциальности, целостности и доступности уязвимой системы |
| SC:N / SI:N / SA:N | None | Прямого воздействия на downstream-системы нет, хотя реальный lateral movement через украденные credentials - отдельная история |
EPSS score: 0.0071 (0.71%), percentile 0.5031 - чуть выше медианы по всей базе CVE. Вероятность эксплуатации в дикой природе в 30-дневном окне невысока, но и не нулевая.
CISA SSVC decision: Track (мониторить). Exploitation:
none (на момент оценки нет подтверждённых атак в дикой природе). Automatable: no (массовая автоматическая эксплуатация затруднена). При этом Technical Impact - total: при успешной эксплуатации компрометация полная. Характерный профиль для supply chain attack: высокий импакт при целевой атаке, но низкий потенциал для mass exploitation.Архитектура sandbox Claude Code и точка отказа
Модель изоляции рабочей области AI-агента
[Применимо: любая среда, где Claude Code < 2.1.64 обрабатывает внешний/недоверенный контент]Claude Code оперирует в модели с двумя уровнями процессов:
Unsandboxed-процесс (основное Node.js-приложение) имеет полные права текущего пользователя и выполняет файловые операции от его имени. Когда Claude Code записывает сгенерированный код в файл - это делает именно unsandboxed-процесс.
Sandboxed-процесс (исполняемые команды) ограничен рамками workspace-директории. Когда Claude Code запускает
bash, python или другой интерпретатор, этот процесс не должен иметь возможности воздействовать на файловую систему за пределами рабочего пространства.Модель безопасности строится на двух допущениях: sandboxed-процесс не может выйти за пределы workspace, а unsandboxed-процесс записывает файлы только по путям, которые логически принадлежат workspace. Нарушение любого из них ломает изоляцию. И вот тут начинается самое интересное.
Создание символической ссылки - это создание нового inode внутри workspace. С позиции sandbox, ограничивающего запись по prefix пути, это легитимная операция: новый файл появляется в разрешённой директории. Всё чисто.
Но symlink - не данные, а указатель на произвольный путь. Когда unsandboxed-процесс Claude Code позже обращается к этому пути для записи, он видит обычный путь внутри workspace и вызывает
openat() без O_NOFOLLOW. Ядро прозрачно разрешает symlink, и запись уходит в target за пределами workspace. Приложение не получает никакого сигнала о том, что реальный файл находится в другом месте.По сути - TOCTOU-паттерн (Time-of-Check-Time-of-Use): проверка (путь внутри workspace) и использование (запись по реальному пути через symlink) разнесены во времени и пространстве, выполняются разными процессами.
Цепочка эксплуатации symlink attack CVE-2026-39861
Предусловия и ограничения
Работает если:- Claude Code версии < 2.1.64
- Атакующий может внедрить контент в контекстное окно (prompt injection через файлы проекта)
- Пользователь активно работает с Claude Code (UI
- passive interaction) - Целевая ОС: Linux или macOS (filesystem поддерживает symlinks)
- Claude Code >= 2.1.64 (патч)
- Filesystem смонтирована с
nosymfollow(Linux 5.10+) - seccomp-профиль фильтрует
symlinkatдля sandboxed-процессов - SELinux/AppArmor в enforcing с политикой, запрещающей symlink creation вне определённых путей
Attack flow: от prompt injection до записи произвольных файлов
Фаза 1: Prompt Injection (LLM01:2025, Initial Access)Атакующий размещает инструкцию в файле проекта - скрытый комментарий в
.py-файле, директива в YAML-конфиге, markdown в README.md, или payload в зависимости npm/pip. Claude Code сканирует workspace, загружает контент в контекстное окно - вредоносная инструкция активируется. Пользователь ничего не замечает.Фаза 2: Sandboxed Code Execution - User Execution (T1204, Execution)
Claude Code, следуя prompt injection, штатно исполняет команду в sandbox-контексте (T1204 - User Execution, спровоцированное вредоносным контентом). Sandboxed-процесс создаёт символическую ссылку внутри workspace, указывающую на target за его пределами.
Фаза 3: Symlink Following - Sandbox Escape (маппинг приблизительный - T1497 описывает anti-analysis evasion, точного T-кода для privilege boundary escape между процессами в ATT&CK нет)
Claude Code записывает данные по пути, который логически находится внутри workspace. Unsandboxed-процесс открывает файл, ядро разрешает symlink, запись уходит в target за пределами workspace. Пользователь не получает подтверждающий запрос - Claude Code считает операцию легитимной. С его точки зрения, всё в порядке.
Фаза 4: Arbitrary File Write - Unsandboxed File Write (Sandbox Escape)
Атакующий контролирует содержимое произвольного файла в контексте текущего пользователя:
~/.ssh/authorized_keys- добавление SSH-ключа атакующего, Credentials In Files (T1552.001, Credential Access)~/.bashrc/~/.zshrc- persistence через Hidden Files and Directories (T1564.001, Defense Evasion / Discovery)~/.gitconfig- supply chain pivot, подмена remote URL- Crontab - persistence через планировщик
Через ту же symlink-механику (но в обратном направлении - для чтения) атакующий получает доступ к credential-файлам, SSH-ключам, cloud-токенам, конфигурациям CI/CD. File and Directory Discovery (T1083, Discovery) позволяет маппировать файловую систему за пределами workspace.
Kill chain mapping
| Этап | MITRE ATT&CK | Роль в цепочке |
|---|---|---|
| Initial Access | Prompt Injection (LLM01:2025) | Внедрение инструкции через вредоносный файл в проекте |
| Execution | User Execution (T1204) | Sandboxed-процесс создаёт symlink по prompt injection |
| Defense Evasion | Sandbox Escape (точный T-код отсутствует в ATT&CK) | Переход от sandboxed к unsandboxed через symlink bridge |
| Credential Access | Credentials In Files (T1552.001) | Чтение/перезапись credential-файлов |
| Collection | Data from Local System (T1005) | Сбор данных за пределами workspace |
| Discovery | File and Directory Discovery (T1083) | Маппинг файловой системы через symlink |
Воспроизведение: минилаб и концептуальный PoC
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Ключевой индикатор: отсутствие вызова
readlinkat() или lstat() между определением пути и вызовом openat(). Unsandboxed-процесс не проверяет, является ли target символической ссылкой. В пропатченной версии (>= 2.1.64) ожидается либо проверка realpath перед записью, либо блокировка symlinkat для sandboxed-процессов.Когда техника НЕ работает
Claude Code >= 2.1.64 - патч устраняет symlink following. Конкретный механизм патча не раскрыт в advisory, но предполагаемые варианты: запретsymlinkat через seccomp-фильтр для sandboxed-процессов, или проверка realpath() с O_NOFOLLOW при записи.Mount option
nosymfollow - доступен в Linux 5.10+, блокирует следование по symlinks на уровне VFS для конкретной точки монтирования. Если workspace смонтирован с этой опцией, ядро вернёт ELOOP при попытке открыть symlink.gVisor / Firecracker - песочницы на уровне системных вызовов перехватывают
symlinkat и могут валидировать target path до передачи в host kernel. Для dev-инструмента это перебор (overkill), но класс атак блокирует наглухо.Контейнер с seccomp-профилем, фильтрующим
symlinkat - если Claude Code запущен в Docker с ограниченным seccomp, sandboxed-процесс не сможет создать символическую ссылку. Стандартный Docker seccomp-профиль symlinkat не фильтрует - учтите.SELinux/AppArmor в enforcing - при наличии политики, ограничивающей symlink creation для процесса Claude Code, атака блокируется на уровне MAC.
Детектирование и форензика sandbox escape
Проактивное детектирование
Аудит файловых операций через auditd. Правило для отслеживания создания символических ссылок:
Bash:
# /etc/audit/rules.d/claude-symlink.rules
-a always,exit -F arch=b64 -S symlinkat -F dir=/home/ -k ai_symlink_create
symlinkat в /home/. В активной dev-среде false positive rate будет значительным - npm, pip и другие менеджеры пакетов создают symlinks штатно. Более точный подход - eBPF-программа или osquery с custom query, проверяющим, что target symlink выходит за пределы workspace.Периодический скан workspace. Команда
find <workspace> -type l -exec readlink -f {} \; покажет все symlinks и их реальные targets. Symlink, указывающий за пределы workspace - IoC, требующий расследования.Real-time мониторинг.
inotifywait -m -r -e create,moved_to <workspace> отслеживает создание файлов в реальном времени, но не различает symlink и обычный файл без дополнительной проверки через test -L.Форензика: что проверять, если использовали уязвимую версию
- Версия Claude Code:
claude --version- если < 2.1.64, система была уязвима в период использования - Поиск внешних symlinks:
find <workspace> -type l -lname '/[I]' -not -lname '<workspace>/[/I]'- покажет ссылки, ведущие за пределы workspace - Timeline анализ: mtime файлов
~/.bashrc,~/.ssh/authorized_keys,~/.gitconfig,~/.config/- совпадение mtime с временем работы Claude Code указывает на возможную эксплуатацию - Логи Claude Code: директория
~/.claude/может содержать историю взаимодействий, включая prompt injection payload, если логирование было включено - Аудит SSH:
grepпо~/.ssh/authorized_keysна наличие незнакомых ключей, добавленных в период использования уязвимой версии
Ограничения детектирования
Auditd-правила наsymlinkat создают шум - стандартные build-системы активно используют symlinks. Фильтрация по target path (выходит ли за workspace) невозможна на уровне auditd rules - для этого нужен eBPF-хук или постобработка логов. На macOS эквивалентный аудит через dtrace / Endpoint Security framework потребует отдельного агента. Короче, серебряной пули нет - придётся комбинировать.Патчинг и hardening безопасности AI-инструментов разработки
Обновление
Основное исправление: обновление Claude Code до версии 2.1.64 или позднее. Автоматический механизм обновления уже доставил патч пользователям, не отключавшим auto-update. Ручное обновление:npm update -g @anthropic-ai/claude-code.Defense-in-depth помимо патча
- Контейнеризация Claude Code - запуск в Docker с bind-mount только workspace-директории, без монтирования домашнего каталога целиком. Ограничивает поверхность атаки даже при наличии 0-day
nosymfollowmount option (Linux 5.10+) для workspace-раздела - блокирует symlink following на уровне VFS, делая класс атак нерелевантным для этой точки монтирования- AppArmor/SELinux профиль для процесса Claude Code, явно запрещающий запись за пределы workspace
- Separation of secrets - SSH-ключи в hardware token (YubiKey/FIDO2), cloud credentials через temporary STS tokens с коротким TTL, git credentials через credential helper вместо plaintext в
~/.gitconfig - Принцип минимальных привилегий для dev-среды - отдельный пользователь или namespace для работы с AI coding agents, без доступа к production-credentials
Чеклист
- Проверить
claude --version>= 2.1.64 - Убедиться, что auto-update включён
find <workspace> -type l -lname '/*'по всем workspace-директориям- Рассмотреть контейнеризацию AI coding tools
- Добавить auditd/eBPF-мониторинг
symlinkatв developer-сегменте инфраструктуры - Ротировать SSH-ключи и cloud-токены на dev-машинах, где использовалась уязвимая версия
Symlink-атаки на AI-агенты как класс уязвимостей
CVE-2026-39861 - не единичный инцидент. Архитектура "AI-агент с доступом к файловой системе" воспроизводит паттерн, знакомый по setuid-бинарям и container escape: два уровня привилегий, между которыми можно построить мост через filesystem primitive. Здесь мост - symlink. В контейнерных escape -overlayfs race conditions или /proc/self/exe трюки. Суть одна: filesystem API в Unix не предоставляет атомарной операции "проверить путь И открыть файл, гарантируя неизменность пути между проверкой и открытием" - классическая TOCTOU-проблема, и ей уже лет тридцать.AI coding agents усугубляют ситуацию: prompt injection даёт атакующему контроль над действиями sandboxed-процесса без необходимости аутентификации в системе жертвы. В классическом setuid-сценарии атакующий должен иметь shell-доступ. Здесь достаточно подготовленного файла в git-репозитории или отравленной npm-зависимости. Порог входа - ниже плинтуса.
Я проверял sandbox-реализации нескольких AI coding agents за последний год, и паттерн повторяется: разработчики ограничивают
write() / open() по prefix пути, но забывают, что symlink - layer of indirection, который ядро разрешает прозрачно. Те, кто догадывается использовать realpath() перед открытием, попадают в TOCTOU: между вызовом realpath() и open() symlink можно создать или подменить. Надёжных решений два: O_NOFOLLOW в openat() плюс запрет symlinkat() через seccomp для sandboxed-процессов, или работа в отдельном mount namespace с собственным mount tree - тогда symlink физически не может указать за пределы видимого filesystem.CISA оценила technical impact как total, но automatable - no. Массовой эксплуатации ожидать не стоит, а вот при целевой атаке на конкретного разработчика (или через supply chain - отравленную зависимость, которую клонирует тысяча проектов) импакт максимальный. Именно такой профиль - low blast radius, high damage per target - характерен для supply chain атак нового поколения, где AI-агент становится amplifier.
Рассчитывать на то, что вендоры AI-инструментов закроют все подобные примитивы патчами - стратегически наивно. Каждый новый coding agent проходит тот же цикл: "sandbox = chdir + PATH check" → CVE с symlink/hardlink/mount escape → патч → следующий filesystem primitive. Пока архитектура AI-агентов не перейдёт на capability-based sandbox (WASI, Firecracker, gVisor с полной изоляцией filesystem), symlink-атаки на рабочие области будут появляться с регулярностью нового LLM-релиза.
И если вы пишете или аудитите sandbox для AI-инструмента - начинайте не с политик на
write(), а с полного перечня filesystem syscalls, создающих indirection: symlinkat, linkat, renameat2, mount. Блокируйте мост, а не трафик по нему. Если хочешь повторить подобную symlink-цепочку в контролируемой среде - задачи с filesystem-примитивами на HackerLab.pro дают именно эту практику, от race condition до контроля записи.