• Твой профиль заполнен на 0%. Заполни за 1 минуту, чтобы тебя нашли единомышленники и работодатели. Заполнить →

Статья Sandbox escape в AI-агентах 2026: разбираем CVE-2026-39888 и CVE-2026-34208

Матрёшки на тёмном антистатическом коврике: внешняя с гравировкой CVE-2026-39888, внутренняя с платой и зелёным светодиодом. Янтарный свет лампы, боке, глубокие тени.


Апрель 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​


1776527969354.webp


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_frame
  • f_back
  • f_builtins
Вдобавок (по advisory GHSA-qf73-2hrx-xprp) функция _safe_getattr перехватывает только явные вызовы getattr(), но не доступ через точечную нотацию. То есть exc.[B]traceback[/B] проходит без единой проверки. На заборе написано «sandbox», а калитка открыта.

Цепочка эксплуатации PraisonAI sandbox escape​

Атака использует стандартную интроспекцию фреймов Python через объект исключения. Логика:
  1. Код намеренно вызывает исключение внутри sandbox
  2. Через except получаем доступ к [B]traceback[/B]
  3. Из traceback через tb_frame достаём объект фрейма
  4. Через f_back поднимаемся по стеку до фрейма subprocess-обёртки
  5. f_builtins этого фрейма - настоящий, неограниченный [B]builtins[/B]
  6. Извлекаем exec под именем переменной, не входящей в блоклист
  7. Вызов exec с произвольным кодом - полный RCE на хосте
Минимальный PoC:
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:NNetworkЭксплуатация через сеть
AC:LLowНизкая сложность атаки
PR:LLowНужна минимальная авторизация (отправка кода агенту)
UI:NNoneДействий пользователя не требуется
S:CChangedПобег из sandbox - воздействие выходит за границу компонента
C:H/I:H/A:HHighПолная компрометация хоста

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:NNetworkЭксплуатация через сеть
AC:LLowНизкая сложность атаки
PR:NNoneАутентификация НЕ требуется
UI:NNoneДействий пользователя не требуется
S:CChangedВыход за границу sandbox
C:H/I:H/A:LHigh/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)
CVSS9.910.0
Язык sandboxPythonJavaScript
Требуется аутентификацияДа (PR:L)Нет (PR:N)
Вектор атакиFrame traversal через исключениеConstructor.call() prototype mutation
ПерсистентностьНет - однократное исполнениеДа - мутации живут между сессиями
Корневая причинаНеполный AST-блоклист (11 vs 30+ атрибутов)Незаблокированный путь через SandboxGlobal constructor
CWECWE-657, CWE-693CWE-693, CWE-915
Патч-версияpraisonai >= 1.5.115sandboxjs >= 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
Для CVE-2026-34208 ищите обращения к 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:

ТехникаIDCVE-2026-39888CVE-2026-34208
PythonT1059.006Основной вектор executionНет
JavaScriptT1059.007НетОсновной вектор execution
Escape to HostT1611Да - выход из subprocess на хостЧастично - мутация хостовых глобалов
Exploitation for Privilege EscalationT1068Да - из sandbox в хост-привилегииДа - из ограниченного контекста в полный доступ
Virtualization/Sandbox EvasionT1497Обход sandbox-ограниченийОбход property assignment checks

Обе уязвимости подпадают и под T1106 (Native API) - атака использует нативные механизмы языка (frame introspection в Python, prototype chain в JavaScript), а не внешние инструменты. Красиво, если вдуматься: язык сам помогает из себя сбежать.

Практический чеклист: побег из песочницы AI - что проверить​

Если вы проводите red team или аудит AI-системы с code execution - вот последовательность:
  1. Инвентаризация sandbox-метода. Что используется для изоляции: RestrictedPython, AST-блоклист, vm2/SandboxJS, Docker, gVisor? Прикладные песочницы - первый кандидат на обход.
  2. Проверка симметричности контролей. Есть ли разные code paths с разным уровнем блокировки? CVE-2026-39888 возник из-за асимметрии между direct и subprocess modes. Ищите аналогичные расхождения - они почти всегда есть.
  3. Тестирование frame traversal (Python). Отправьте код с цепочкой [B]traceback[/B] -> tb_frame -> f_back -> f_builtins. Если sandbox не блокирует - вы нашли вектор. Занимает минуту.
  4. Тестирование prototype pollution (JavaScript). Проверьте доступность this.constructor, [B]proto[/B], Object.getPrototypeOf. Попробуйте this.constructor.call() с глобальным объектом как target.
  5. Проверка персистентности. Запустите две sandbox-сессии подряд. Мутации, пережившие перезапуск - критический индикатор (как в CVE-2026-34208). Если первая сессия отравила Math.random, а вторая это видит - у вас 10.0 CVSS в продакшене.
  6. Верификация патча. После обновления повторите шаги 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 реально блокировали вашу цепочку?
 
Последнее редактирование модератором:
  • Нравится
Реакции: Marylin
Коллеги, тема висит без ответов. Напоминаю вопрос из статьи:

Оба CVE эксплуатируют прикладные блоклисты, которые не покрывают все пути к опасным атрибутам. При аудите AI-фреймворков с code execution — какой метод sandbox escape давал вам результат быстрее: frame traversal через __traceback__.tb_frame.f_back.f_builtins (как в CVE-2026-39888) или классический обход через type.__subclasses__() с восстановлением os.system? Если тестировали PraisonAI до 1.5.115 — какой payload отработал первым и сколько атрибутов из тех 11 в blocked_attrs реально блокировали вашу цепочку?

Буду благодарен за конкретный опыт — даже короткий ответ ценен.
 
Мы в соцсетях:

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

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

HackerLab