Апрель 2026-го принёс два advisory, которые разносят иллюзию безопасной изоляции в мультиагентных фреймворках. CVE-2026-39888 с CVSS 9.9 - побег из Python-песочницы PraisonAI через frame traversal. CVE-2026-34208 с максимальным CVSS 10.0 - пробой JavaScript-изоляции SandboxJS через prototype mutation. Оба бага эксплуатируются по сети, с низкой сложностью, оба дают полный RCE на хосте. Два разных языка, одна и та же архитектурная ошибка.
Разберу оба CVE по осям: вектор атаки, условия триггера, цепочка эксплуатации и патч-статус. Покажу, как каждая строка payload пробивает изоляцию и почему AST-блоклисты - это «забор с калиткой» для sandbox AI-агентов.
Почему песочницы AI-агентов стали новой attack surface
AI-агенты давно перестали быть обёрткой над чатом. Системы вроде PraisonAI, CrewAI и AutoGen принимают произвольный код от LLM, исполняют его в runtime и возвращают результат. Модель генерирует payload - фреймворк его послушно исполняет. Звучит как сценарий для CTF-таска, но нет - это продакшен.
Проблема в том, как строится изоляция. Вместо полноценного контейнера с ограниченными syscall-политиками (gVisor, Kata Containers) многие фреймворки лепят прикладную песочницу - ограниченный
[B]builtins[/B], AST-блоклисты, перехват getattr. По данным Cymulate Research Labs (апрель 2026), аналогичный класс уязвимостей - Configuration-Based Sandbox Escape (CBSE) - нашли в Claude Code, Gemini CLI и Codex CLI. Вывод исследователей прямой: «если AI-агент не может защитить собственную границу исполнения, как ему доверять защиту среды разработчика?»Оба разбираемых CVE - прямое следствие этого подхода. Смотрим, как ломается каждый.
CVE-2026-39888: побег из песочницы PraisonAI через frame traversal
Что такое PraisonAI и где живёт уязвимость
PraisonAI - мультиагентная система, где команды агентов исполняют Python-код. Функцияexecute_code() в модуле praisonaiagents.tools.python_tools принимает код от агента и запускает его в двух режимах: прямое исполнение (direct) или subprocess (sandbox). По умолчанию - sandbox_mode="sandbox".В sandbox-режиме код исполняется в subprocess с ограниченным
[B]builtins[/B] и AST-блоклистом атрибутов. Тут и начинается цирк.Корневая причина: несимметричный блоклист
AST-блоклист в subprocess-обёртке (переменнаяblocked_attrs, строка 143 файла python_tools.py) содержит всего 11 имён атрибутов. В direct-execution пути блокируется 30+. Разница почти в три раза - и именно в ней живёт баг. Четыре атрибута, формирующих цепочку frame traversal, в subprocess-блоклисте отсутствуют полностью:[B]traceback[/B]tb_framef_backf_builtins
_safe_getattr перехватывает только явные вызовы getattr(), но не доступ через точечную нотацию. То есть exc.[B]traceback[/B] проходит без единой проверки. На заборе написано «sandbox», а калитка открыта.Цепочка эксплуатации PraisonAI sandbox escape
Атака использует стандартную интроспекцию фреймов Python через объект исключения. Логика:- Код намеренно вызывает исключение внутри sandbox
- Через
exceptполучаем доступ к[B]traceback[/B] - Из traceback через
tb_frameдостаём объект фрейма - Через
f_backподнимаемся по стеку до фрейма subprocess-обёртки f_builtinsэтого фрейма - настоящий, неограниченный[B]builtins[/B]- Извлекаем
execпод именем переменной, не входящей в блоклист - Вызов
execс произвольным кодом - полный RCE на хосте
Python:
try:
raise Exception()
except Exception as e:
tb = e.__traceback__
frame = tb.tb_frame.f_back
real_builtins = frame.f_builtins
run = real_builtins['exec']
run('__import__("os").system("id")')
[B]traceback[/B] в blocked_attrs - цепочка обрывалась бы на первом шаге. Но его там нет.CVSS-вектор и его интерпретация
CVSS 3.1: 9.9 CRITICAL -AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Компонент | Значение | Что это значит |
|---|---|---|
| AV:N | Network | Эксплуатация через сеть |
| AC:L | Low | Низкая сложность атаки |
| PR:L | Low | Нужна минимальная авторизация (отправка кода агенту) |
| UI:N | None | Действий пользователя не требуется |
| S:C | Changed | Побег из sandbox - воздействие выходит за границу компонента |
| C:H/I:H/A:H | High | Полная компрометация хоста |
S:C (Scope Changed) - ключевой момент: атакующий начинает внутри песочницы, заканчивает на хосте. Классический Escape to Host (T1611 по MITRE ATT&CK).
CWE от GitHub: CWE-657 (Violation of Secure Design Principles) и CWE-693 (Protection Mechanism Failure). Песочница спроектирована с нарушением симметричности контролей, защитный механизм не работает. Всё по учебнику - только учебник грустный.
CVE-2026-34208: prototype mutation в SandboxJS
Механизм изоляции SandboxJS и его обход
SandboxJS (пакетsandboxjs от nyariv) - библиотека для изоляции JavaScript-кода. Она блокирует прямое присваивание свойствам глобальных объектов: Math.random = maliciousFunc перехватывается и отклоняется.Но есть нюанс. Защита обходится через exposed callable constructor path. Атакующий использует цепочку
this.constructor.call(target, attackerObject), где this.constructor резолвится в SandboxGlobal, а Function.prototype.call никто не заблокировал. Результат - произвольные свойства записываются в хостовые глобальные объекты, и мутации живут между sandbox-сессиями.Это не просто обход изоляции - это prototype pollution с выходом за границу песочницы. Один запрос к AI-агенту мутирует
Math.random через SandboxJS - следующий запрос от другого пользователя получает отравленный глобал. Персистентный яд в shared state.CVSS-вектор и критичность
CVSS 3.1: 10.0 CRITICAL -AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L| Компонент | Значение | Что это значит |
|---|---|---|
| AV:N | Network | Эксплуатация через сеть |
| AC:L | Low | Низкая сложность атаки |
| PR:N | None | Аутентификация НЕ требуется |
| UI:N | None | Действий пользователя не требуется |
| S:C | Changed | Выход за границу sandbox |
| C:H/I:H/A:L | High/High/Low | Полный доступ к данным и модификации |
Максимальный балл 10.0 - из-за PR:N. Любой, кто может передать JavaScript-код в sandbox, получает полный контроль. Без логина, без токена, без ничего.
CWE: CWE-693 (Protection Mechanism Failure) - общий с PraisonAI, и это не совпадение: оба бага из одного класса. Плюс CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes) - прямое указание на prototype pollution.
Атака ложится на T1059.007 (JavaScript execution по ATT&CK), а персистентность мутаций - фактически T1068 (Exploitation for Privilege Escalation): отравленный глобал даёт повышенные привилегии последующим сессиям.
Сравнительный анализ: Python sandbox bypass vs JS изоляция
Сведём оба CVE в таблицу. Именно такое сравнение помогает выбрать приоритет для патчинга и детекта.| Параметр | CVE-2026-39888 (PraisonAI) | CVE-2026-34208 (SandboxJS) |
|---|---|---|
| CVSS | 9.9 | 10.0 |
| Язык sandbox | Python | JavaScript |
| Требуется аутентификация | Да (PR:L) | Нет (PR:N) |
| Вектор атаки | Frame traversal через исключение | Constructor.call() prototype mutation |
| Персистентность | Нет - однократное исполнение | Да - мутации живут между сессиями |
| Корневая причина | Неполный AST-блоклист (11 vs 30+ атрибутов) | Незаблокированный путь через SandboxGlobal constructor |
| CWE | CWE-657, CWE-693 | CWE-693, CWE-915 |
| Патч-версия | praisonai >= 1.5.115 | sandboxjs >= 0.8.36 |
| ATT&CK тактика | Python (T1059.006), Escape to Host (T1611) | JavaScript (T1059.007), Privilege Escalation (T1068) |
| Scope Impact | Хост-система через subprocess | Все последующие sandbox-сессии + хост |
Что опаснее на практике
Для пентестера важны условия триггера. CVE-2026-34208 не требует аутентификации - если SandboxJS обрабатывает пользовательский ввод (а это основной сценарий в AI-агентах), любой посетитель может отправить payload. CVE-2026-39888 требует возможности отправить код агенту PraisonAI, что обычно подразумевает хотя бы минимальный доступ к API.С другой стороны, CVE-2026-39888 даёт немедленный RCE на хосте (
A:H), а CVE-2026-34208 в первую очередь отравляет runtime - A:L, зато C:H/I:H. Я бы в red team-кампании использовал PraisonAI-баг для initial access, а SandboxJS - для persistence и lateral movement внутри мультитенантного AI-сервиса. Два CVE - два этапа kill chain.Уязвимость AI агентов: шире, чем два CVE
CVE-2026-39888 - не единственная проблема PraisonAI. GitHub Security Advisories показывают системный паттерн:- GHSA-6vh2-h83c-9294 - ещё один Python sandbox escape, но через переопределение
startswith()в подклассеstrвнутриexecute_code. Другой вектор, та же функция, та же дыра. - GHSA-44c2-3rw4-5gvh - SSRF в
FileTools.download_file()через невалидированный URL (CVE-2026-34954). Агент сканирует внутреннюю сеть за вас. - GHSA-766v-q9x3-g744 - утечка состояния памяти и path traversal в обработке контекста MultiAgent.
execute_code() ломается тремя разными способами, проблема не в блоклисте, а в самом подходе к изоляции.Детектирование и митигация remote code execution в AI framework
Что проверить в своём стенде прямо сейчас
Если у вас PraisonAI или SandboxJS в продакшене (или в пентест-лабе) - вот конкретные шаги.PraisonAI:
pip show praisonaiagents | grep Version. Всё ниже 1.5.115 - уязвимо. Если обновиться прямо сейчас нельзя - отключите code execution полностью или оберните процесс в контейнер с seccomp/gVisor. Прикладная песочница вас не спасёт.SandboxJS:
npm ls sandboxjs. Версии ниже 0.8.36 уязвимы. Учитывая PR:N и персистентность мутаций - это патчить первым. Без вариантов.Правила детекции frame traversal уязвимости
Для обнаружения эксплуатации CVE-2026-39888 мониторьте паттерны в коде, отправляемом агентам. Ключевой индикатор - цепочка[B]traceback[/B] -> tb_frame -> f_back -> f_builtins в одном фрагменте. На уровне SIEM ловится grep-паттерном по логам code execution:
Код:
__traceback__.*tb_frame.*f_back.*f_builtins
this.constructor.call с двумя аргументами, где первый - глобальный объект (Math, Object, JSON). Мониторьте аномальные изменения свойств глобалов между sandbox-сессиями - если Math.random вдруг стал возвращать не то, что ожидалось, у вас проблема.На уровне хоста: по данным SentinelOne, индикаторы компрометации для CVE-2026-39888 - неожиданные дочерние процессы от PraisonAI worker-процессов, аномальные системные вызовы и нестандартные исходящие соединения от агентских процессов.
Архитектурные выводы для изоляции Python и JS
Оба CVE бьют в одну точку: прикладные блоклисты - ненадёжная граница безопасности. AST-фильтрация в Python не перехватывает все пути доступа к атрибутам. Блокировка прямого присваивания в JavaScript не покрывает constructor-based мутации. Блоклист - это список того, о чём разработчик подумал. А атакующий думает о том, о чём разработчик не подумал.Cymulate подтверждает это на примере Claude Code, Gemini CLI и Codex CLI: «sandbox рассматривается как граница безопасности, тогда как реальная граница - host-side конфигурация и логика исполнения - остаётся записываемой изнутри sandbox». Рекомендация: контейнеризация под контролем пользователя (Docker с seccomp, gVisor, Kata Containers) вместо прикладных песочниц фреймворка.
По данным Northflank, в 2026 году production-grade решения для AI-агентов используют microVM-изоляцию (Firecracker, Kata Containers) с выделенным ядром на каждый workload. Прикладные песочницы - Python
RestrictedPython, Node.js vm, AST-блоклисты - годятся как defense-in-depth слой, но не как основная граница. Строить на них безопасность - всё равно что вешать замок на картонную дверь.Маппинг на MITRE ATT&CK
Как оба CVE ложатся на тактики ATT&CK:| Техника | ID | CVE-2026-39888 | CVE-2026-34208 |
|---|---|---|---|
| Python | T1059.006 | Основной вектор execution | Нет |
| JavaScript | T1059.007 | Нет | Основной вектор execution |
| Escape to Host | T1611 | Да - выход из subprocess на хост | Частично - мутация хостовых глобалов |
| Exploitation for Privilege Escalation | T1068 | Да - из sandbox в хост-привилегии | Да - из ограниченного контекста в полный доступ |
| Virtualization/Sandbox Evasion | T1497 | Обход sandbox-ограничений | Обход property assignment checks |
Обе уязвимости подпадают и под T1106 (Native API) - атака использует нативные механизмы языка (frame introspection в Python, prototype chain в JavaScript), а не внешние инструменты. Красиво, если вдуматься: язык сам помогает из себя сбежать.
Практический чеклист: побег из песочницы AI - что проверить
Если вы проводите red team или аудит AI-системы с code execution - вот последовательность:- Инвентаризация sandbox-метода. Что используется для изоляции: RestrictedPython, AST-блоклист, vm2/SandboxJS, Docker, gVisor? Прикладные песочницы - первый кандидат на обход.
- Проверка симметричности контролей. Есть ли разные code paths с разным уровнем блокировки? CVE-2026-39888 возник из-за асимметрии между direct и subprocess modes. Ищите аналогичные расхождения - они почти всегда есть.
- Тестирование frame traversal (Python). Отправьте код с цепочкой
[B]traceback[/B]->tb_frame->f_back->f_builtins. Если sandbox не блокирует - вы нашли вектор. Занимает минуту. - Тестирование prototype pollution (JavaScript). Проверьте доступность
this.constructor,[B]proto[/B],Object.getPrototypeOf. Попробуйтеthis.constructor.call()с глобальным объектом как target. - Проверка персистентности. Запустите две sandbox-сессии подряд. Мутации, пережившие перезапуск - критический индикатор (как в CVE-2026-34208). Если первая сессия отравила
Math.random, а вторая это видит - у вас 10.0 CVSS в продакшене. - Верификация патча. После обновления повторите шаги 3-5. Убедитесь, что
praisonaiagents >= 1.5.115иsandboxjs >= 0.8.36действительно закрывают вектор. Доверяй, но проверяй - особенно когда речь о блоклистах.
Вопрос к читателям
Оба CVE эксплуатируют прикладные блоклисты, которые не покрывают все пути к опасным атрибутам. При аудите AI-фреймворков с code execution - какой метод sandbox escape давал вам результат быстрее: frame traversal через[B]traceback[/B].tb_frame.f_back.f_builtins (как в CVE-2026-39888) или классический обход через type.[B]subclasses[/B]() с восстановлением os.system? Если тестировали PraisonAI до 1.5.115 - какой payload отработал первым и сколько атрибутов из тех 11 в blocked_attrs реально блокировали вашу цепочку?
Последнее редактирование модератором: