Тринадцать CVE с оценкой от 9.0 до 10.0 за первую половину 2026 года - в одной библиотеке, которую мейнтейнер пометил deprecated ещё в 2023-м. По данным The Hacker News, каждая ведёт к выполнению произвольного кода на хост-системе. Три из них - CVE-2026-43997, CVE-2026-44005 и CVE-2026-44006 - набрали максимальный балл 10.0 и показывают три фундаментально разных примитива побега из песочницы: утечку host Object, prototype pollution через bridge proxy и инъекцию через
BaseHandler.getPrototypeOf. Разбираю каждый по отдельности и показываю, почему proxy-модель изоляции vm2 - это не стена, а занавеска.Бизнес-логика атаки: зачем ломать песочницу vm2
vm2 - npm-пакет для запуска недоверенного JavaScript в изолированной среде Node.js. По данным Endor Labs, библиотека набирает больше миллиона еженедельных загрузок, хотя мейнтейнер ещё в 2023 году пометил проект как deprecated: "contains critical security issues and should not be used for production""Содержит критически важные элементы безопасности и не должен использоваться в производственных целях". Но кого это когда останавливало? vm2 продолжают тащить в IDE, FaaS-платформы, пентест-фреймворки и - что сейчас особенно больно - в фреймворки AI-агентов для выполнения LLM-сгенерированного кода. Подробнее - в нашем руководстве по cve эксплойт разработка.Побег из песочницы Node.js превращает выполнение JavaScript внутри vm2 в полноценный RCE на хост-системе. Для AI-агентов, как отмечает Kodem Security, цепочка выглядит так: prompt injection -> вредоносный JS в sandbox -> vm2 sandbox escape -> shell на хосте. CISA через программу ADP-Vulnrichment классифицирует все три CVE как automatable с total technical impact (SSVC Decision: Track*). PoC-код существует для каждой из трёх.
Место в цепочке атаки: Initial Access (T1190, Exploit Public-Facing Application) -> Execution (T1059.007, JavaScript) -> Privilege Escalation (T1611, Escape to Host). Код попадает в vm2 через пользовательский ввод, LLM-генерацию или плагинную систему - дальше хватает одного из трёх описанных ниже примитивов для полной компрометации хоста.
Модель изоляции vm2: proxy-обёртки и их пределы
Требования к окружению для воспроизведения
- Node.js: v18+ (для воспроизведения CVE-2026-43997, CVE-2026-44005, CVE-2026-44006)
- npm-пакет vm2: версии до 3.10.5 включительно (все три CVE исправлены в 3.11.0)
- ОС: Linux, macOS или Windows - vm2 платформенно-независима
- RAM: стандартные требования Node.js (от 256 МБ)
- Контекст: только лабораторная среда без доступа к продакшн-данным
handleException() в catch-клаузы, чтобы перехватывать host-ошибки. Второй - bridge proxy: каждый объект, пересекающий границу sandbox <-> host, оборачивается в Proxy, который перехватывает обращения к свойствам и методам. По замыслу sandbox-код никогда не получает прямую ссылку на host-объект - только на его проксированную копию.Загвоздка в том, что оба механизма работают целиком в userland JavaScript. Никаких V8 Isolates (отдельных куч памяти), никаких OS-примитивов изоляции (namespaces, seccomp). Любая ошибка в логике проксирования, обработке исключений или обходе прототипной цепочки даёт атакующему прямую ссылку на host-объект. А от host-объекта до
Function constructor - ровно два шага по цепочке obj.constructor.constructor.vm2 закрывала такие ошибки точечными патчами начиная с 2022 года: CVE-2022-36067 (CVSS 10.0, EPSS 0.4787 - Top 5%), CVE-2023-29017 (CVSS 10.0, EPSS 0.6319 - Top 1%), CVE-2023-30547 (CVSS 9.8, EPSS 0.7209 - Top 1%). Каждый раз фикс закрывал один конкретный вектор - и каждый раз находился следующий. Майская волна 2026 года - продолжение этого бесконечного цикла.
CVE-2026-43997: утечка host Object через Symbol
CVSS: 10.0 (CRITICAL) -
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:HCWE: CWE-94 (Code Injection), CWE-653 (Improper Isolation or Compartmentalization)
Затронутые версии: npm
vm2 до 3.11.0 (по данным OSV.dev: introduced 0, fixed 3.11.0)EPSS: 0.0098 (percentile 57.94%)
Суть: sandbox-код получает прямую ссылку на host Object - не на проксированную копию, а на настоящий
Object из host-контекста. По описанию NVD, один из способов эксплуатации - вызов HostObject.getOwnPropertySymbols() для получения Symbol(nodejs.util.inspect.custom).Почему это плохо:
Symbol(nodejs.util.inspect.custom) - внутренний символ Node.js для кастомной сериализации объектов. Если ты видишь его из sandbox - значит, работаешь с реальным host-объектом, а не с proxy-обёрткой. Дальше стандартная цепочка: host Object -> Object.constructor -> Function -> произвольный код в host-контексте.
JavaScript:
// Пример для демонстрации концепции - CVE-2026-43997
// Атакующий внутри sandbox получает host Object
const symbols = Object.getOwnPropertySymbols(hostObject);
const inspectSym = symbols.find(
s => String(s).includes('nodejs.util.inspect.custom')
);
// Наличие этого символа подтверждает работу с host-объектом
// Далее: hostObject.constructor.constructor('return process')()
Function constructor. Исследователь c0rydoras, которому The Hacker News приписывает обнаружение этой CVE, продемонстрировал полную цепочку от получения host Object до RCE.Предусловия и ограничения: нужна возможность выполнить произвольный JavaScript внутри vm2 sandbox (класс VM или NodeVM). Если приложение фильтрует входной код до передачи в vm2 (AST-анализ, whitelist-функций), вектор может быть заблокирован до достижения уязвимого пути. На практике большинство деплоев vm2 не применяют pre-sandbox фильтрацию - в этом и смысл использования песочницы. Зачем городить AST-парсер, если у тебя якобы sandbox?
CVE-2026-44005: prototype pollution через bridge proxy
CVSS: 10.0 (CRITICAL) -
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:HCWE: CWE-94 (Code Injection), CWE-1321 (Improper Control of Modification of Object Prototype Attributes Prototype Pollution), CWE-653 (Improper Isolation)
Затронутые версии: npm
vm2 от 3.9.6 до 3.10.5 (по данным OSV.dev)EPSS: 0.0084 (percentile 53.60%)
Эта уязвимость отличается от остальных побегов - атакующий не получает прямой RCE через
Function constructor, а мутирует host-прототипы прямо из sandbox-кода. Обратите внимание на CVSS-вектор: C:N/I:H/A:H - конфиденциальность не затронута напрямую, зато целостность и доступность - полностью.По описанию NVD: vm2-bridge экспонирует мутабельные proxy для реальных host-прототипов (
Object.prototype, Array.prototype, Function.prototype) и проксирует записи из sandbox в host через otherReflectSet() и otherReflectDefineProperty(). Sandbox-код пишет в Object.prototype - и эта мутация проходит сквозь bridge в реальный host-контекст.
JavaScript:
// Пример для демонстрации концепции - CVE-2026-44005
// Внутри sandbox: запись в Object.prototype
Object.prototype.__polluted__ = true;
// vm2 bridge вызывает otherReflectSet() на host Object.prototype
// В host-контексте: ({})['__polluted__'] === true
// Весь Node.js-процесс скомпрометирован - middleware, auth-проверки
child_process.exec() напрямую. JavaScript sandbox bypass через prototype pollution в host-контексте бьёт по всему Node.js-процессу - другие модули, middleware, проверки авторизации. В комбинации с CVE-2026-43997 или CVE-2026-44006 получается полная цепочка: сначала prototype pollution для подготовки среды, затем прямой побег для RCE. Красиво, если смотреть со стороны атакующего.Ограничения: эксплуатация требует версии vm2 начиная с 3.9.6 (до неё bridge-механизм работал иначе). Если приложение запускает vm2 в отдельном Worker Thread с
--frozen-intrinsics, мутация прототипов может быть частично ограничена - но я не встречал такой конфигурации в проде.CVE-2026-44006: произвольные прототипы через getPrototypeOf
CVSS: 10.0 (CRITICAL) -
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:HCWE: CWE-94 (Code Injection), CWE-914 (Improper Control of Dynamically-Identified Variables)
Затронутые версии: npm
vm2 до 3.11.0 (по данным OSV.dev: introduced 0, fixed 3.11.0)EPSS: 0.0081 (percentile 52.77%)
Третий примитив атакует механизм проксирования в лоб. По описанию NVD: sandbox-код добирается до метода
BaseHandler.getPrototypeOf, который возвращает произвольные прототипы из host-контекста.BaseHandler - внутренний обработчик vm2, управляющий Proxy-ловушками для всех объектов, пересекающих границу sandbox <-> host. Если атакующий добирается до getPrototypeOf этого обработчика, он получает прямой доступ к прототипной цепочке host-объектов - без проксирования и санитизации. Обход изоляции JavaScript здесь опирается на сами механизмы прототипного наследования языка. Ирония в том, что vm2 пытается контролировать именно те механизмы, которые используются для её обхода.CWE-914 (Improper Control of Dynamically-Identified Variables) - корневая проблема: vm2 не контролирует доступ к внутренним переменным (
BaseHandler), которые идентифицируются динамически через прототипную цепочку. Атакующий использует механизмы самого JavaScript (prototype chain traversal) для доступа к внутренним компонентам изоляции.Исследователь c0rydoras обнаружил и эту CVE (по данным The Hacker News) - что указывает на системный аудит proxy-модели vm2, а не случайные находки. Когда один человек вытаскивает три CVSS 10.0 из одной библиотеки - это не везение, это методичный разбор архитектуры.
Классификация уязвимостей: CWE, CWE-913, CWE-74 и MITRE ATT&CK
Иерархия CWE для тройки CVE
Три CVE объединены корневой слабостью CWE-94 (Code Injection), но каждая добавляет свой специфичный CWE:| CVE | CVSS | Специфичный CWE | Примитив побега |
|---|---|---|---|
| CVE-2026-43997 | 10.0 | CWE-653 (Improper Isolation) | Host Object leak |
| CVE-2026-44005 | 10.0 | CWE-1321 (Prototype Pollution) | Bridge proxy mutation |
| CVE-2026-44006 | 10.0 | CWE-914 (Dynamic Variable Control) | getPrototypeOf injection |
В иерархии CWE все три попадают под два родительских класса, которые точно описывают природу vm2-побегов:
CWE-913 (Improper Control of Dynamically-Managed Code Resources) - CWE-94 дочерний для CWE-913 (связь ChildOf). Библиотека не контролирует динамически управляемые ресурсы кода - прототипы, конструкторы, символы - к которым sandbox-код получает доступ. CWE-913 явно присвоен родственной CVE-2026-22709 (CVSS 9.8) и историческим CVE-2022-36067, CVE-2023-29017, CVE-2023-29199. Одна и та же корневая слабость, разные пути эксплуатации.
CWE-74 (Improper Neutralization of Special Elements in Output Used by a Downstream Component) - второй родительский класс CWE-94. Атакующий формирует "специальные элементы" (обращения к прототипам, символам, внутренним обработчикам), которые vm2 не нейтрализует при пересечении границы sandbox -> host. CWE-74 явно присвоен CVE-2023-30547 (CVSS 9.8, EPSS 0.7209 - Top 1%), подтверждая общий паттерн.
Маппинг на MITRE ATT&CK и EPSS-прогноз
Цепочка атаки через побег из песочницы Node.js:- Initial Access - T1190 (Exploit Public-Facing Application): код попадает в vm2 через веб-интерфейс, API или LLM-генерацию
- Execution - T1059.007 (JavaScript): вредоносный JS выполняется внутри vm2
- Privilege Escalation - T1611 (Escape to Host): один из трёх примитивов пробивает границу sandbox
- Post-Exploitation - T1106 (Native API): доступ к
child_process,fs,netчерез host-контекст
Обнаружение и митигация vm2 sandbox escape
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Вся майская волна 2026 года - не аномалия, а закономерный результат архитектурного решения строить sandbox на JavaScript-уровне без аппаратной или OS-уровневой изоляции. За время работы с vm2 breakouts наблюдаю одну и ту же последовательность: исследователь находит новый путь к host-объекту -> мейнтейнер закрывает конкретный путь -> через недели появляется следующий. Бесконечный whack-a-mole.
CVE-2026-43997, CVE-2026-44005 и CVE-2026-44006 показывают три принципиально разных примитива (host Object leak, prototype pollution, getPrototypeOf injection) - и все три обходят ту же proxy-модель разными дорогами. Это не баги. Это свойство архитектуры.
Единственный честный вопрос для команд, которые используют vm2 в проде: не "какую версию поставить", а "почему всё ещё vm2, а не V8 Isolate или контейнерная изоляция". Prototype pollution через bridge proxy (CVE-2026-44005) - особенно показательный случай: атакующий даже не пытается выбраться из sandbox классическим путём, он мутирует host-прототипы изнутри и ломает весь Node.js-процесс. Следующая волна CVE будет - вопрос когда, не "если". EPSS для исторических vm2-побегов стабильно растёт до Top 1% в течение года после раскрытия, и у свежей тройки нет архитектурных причин отклониться от этой траектории. Запустите
npm ls vm2 в своих проектах прямо сейчас - если увидите версию ниже 3.11.2, у вас та же проблема.
Последнее редактирование модератором: