Zwei klassische OWASP-dokumentierte Schwachstellen-Klassen, die in keinem der vertiefenden Kapitel (XSS, Injection, Auth, Crypto) ein Zuhause haben — und trotzdem zu den wirkungsvollsten Web-Angriffen der letzten 15 Jahre gehören. XML External Entities (XXE) erlauben File-Read, SSRF und manchmal RCE über manipulierte XML-Eingaben. Unsafe Deserialization verwandelt eine harmlos aussehende Datenstruktur in einen Remote-Code-Execution-Vektor. Dieser Artikel ordnet beide.
XXE — was es ist
XML External Entity Injection nutzt eine wenig bekannte XML-Funktion aus: External Entities, die Inhalte aus externen Quellen laden können. Wenn eine Anwendung XML-Eingaben von Nutzer:innen entgegennimmt und einen XML-Parser nutzt, der External Entities standardmäßig zulässt, kann ein:e Angreifer:in:
- Lokale Dateien lesen (
/etc/passwd, AWS-Credentials, Application-Configs). - Server-seitige HTTP-Anfragen auslösen (SSRF — siehe api-top-10-konkret API7).
- DoS über exponentielle Entity-Expansion (Billion-Laughs).
- In manchen Konfigurationen sogar Remote Code Execution.
Klassisches XXE-Payload:
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<data>&xxe;</data>
</root>Wenn die Anwendung diesen XML-Body parst und den Wert von <data> irgendwo zurückgibt (Fehlermeldung, Response, generierte PDF), ist der Inhalt von /etc/passwd durchgereicht.
SSRF-Variante:
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root><data>&xxe;</data></root>Der XML-Parser baut die HTTP-Anfrage im Namen des Servers auf — und kommt damit an interne Cloud-Metadaten oder andere nicht-öffentliche Endpunkte.
Wo XXE in der Praxis auftritt
Ein häufiges Missverständnis: „Wir nutzen kein XML mehr, also kein XXE-Risiko." Falsch. XML steckt in vielen Formaten und Schnittstellen, oft unsichtbar:
- SOAP-APIs — XML ist Pflicht-Format.
- SAML-Authentifizierung — Identity-Provider liefert XML-Tokens.
- DOCX, XLSX, PPTX (Office Open XML) — gezippte XML-Strukturen.
- SVG-Dateien — XML-basiert, oft als Bild-Upload akzeptiert.
- RSS/Atom-Feeds.
- XML-RPC-Endpunkte (z. B. WordPress).
- PDF-Workflow-Komponenten.
- EPUB, GPX, KML, RDF — viele Spezial-Formate.
Jede Eingabe eines dieser Formate ist ein potenzieller XXE-Vektor, wenn der Parser nicht gehärtet ist.
Reale Vorfälle:
- Apache Struts2 (CVE-2017-9805) — XXE in REST-Plugin, führte zu RCE bei mehreren großen Unternehmen.
- Cisco WebEx — XXE in SAML-Verarbeitung, 2017.
- Microsoft Word — XXE über bösartige .docx-Dateien, mehrere CVEs über die Jahre.
- Apple — iWork-XXE in iOS und macOS, 2018.
XXE ist seit 1999 dokumentiert und seit den 2010ern in der OWASP Top 10 — und trotzdem regelmäßig in neuen CVEs zu finden.
XXE verhindern
Konkrete Härtungs-Patterns pro Sprache:
Java (klassisches DocumentBuilder):
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Schadhaft: Default lässt External Entities zu (je nach JDK-Version)
// Sicher: Alle riskanten Features deaktivieren
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
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);Python (lxml):
from lxml import etree
# Schadhaft
# tree = etree.parse(xml_file)
# Sicher
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
load_dtd=False,
)
tree = etree.parse(xml_file, parser=parser)Empfehlung Python: defusedxml (Drop-in-Ersatz für xml.etree, lxml, etc., mit sicheren Defaults).
PHP:
// libxml < 2.9.0: External Entities Default an — riskant
// libxml >= 2.9.0 (heute Standard): Default aus
// Defensiv explizit setzen
libxml_disable_entity_loader(true); // legacy bis PHP 8.0
// PHP 8.0+: External Entities sind generell Default deaktiviert.NET:
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
};
var reader = XmlReader.Create(input, settings);Universelle Empfehlung:
- Wenn XML-Verarbeitung nötig: External Entities und DTD-Processing explizit deaktivieren.
- Defusedxml-artige Libraries nutzen (Python
defusedxml, JavaOWASP Java Encoder, etc.). - Wenn möglich auf JSON migrieren — JSON hat kein External-Entity-Konzept.
- Bei nötigen XML-Verarbeitungen in Sandboxes laufen lassen.
Unsafe Deserialization — was es ist
Programmiersprachen bieten Serialisierung — Mechanismen, Objekte in Strings/Bytes umzuwandeln und wieder zurück. Wenn eine Anwendung untrusted Daten deserialisiert, kann ein:e Angreifer:in Daten konstruieren, die beim Deserialisieren Code ausführen.
Klassische Sprach-spezifische Mechanismen:
- Java:
ObjectInputStream.readObject()— instanziiert Klassen mit beliebigen Werten, ruftreadObject()/readResolve()der Klasse auf. „Gadget Chains" können RCE auslösen. - PHP:
unserialize()— wenn Magic Methods (__wakeup,__destruct) in erreichbaren Klassen Side-Effects haben, RCE möglich. - Python:
pickle.loads()— Pickle-Format kann beliebigen Python-Code beim Deserialisieren ausführen. Eingaben aus untrusted Quellen sind niemals sicher. - .NET:
BinaryFormatter,LosFormatter,NetDataContractSerializer— alle gelten als deprecated, weil Deserialization-RCE strukturell möglich ist. - Ruby:
Marshal.load. - Node.js:
serialize-javascriptund ähnliche Libraries hatten historisch Deserialization-Probleme.
Klassisches Java-Beispiel (vereinfacht):
// Schadhaft: Direkt aus Request lesen
public Object handleRequest(HttpServletRequest request) throws Exception {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
return ois.readObject(); // RCE möglich
}Ein:e Angreifer:in baut mit ysoserial ein Payload, das beim Deserialisieren über Gadget-Chains (Commons-Collections, Spring, Hibernate) Runtime.exec() aufruft.
Reale Deserialization-Vorfälle
Apache Commons Collections (2015) — Gadget-Chain in einer der meistgenutzten Java-Libraries; ermöglichte RCE in vielen Java-Apps, die ObjectInputStream verwendeten und Commons-Collections im Classpath hatten. Stephen Breen veröffentlichte den ursprünglichen Exploit, später verallgemeinert in ysoserial.
Apache Shiro (CVE-2016-4437) — RememberMe-Cookie wurde mit hardcoded Default-Schlüssel verschlüsselt. Angreifer baute Java-Serialized-Payload mit ysoserial, RCE.
Oracle WebLogic — mehrere kritische Deserialization-CVEs über die Jahre (2017, 2018, 2020).
Jenkins — mehrere CVEs zu Deserialization in Plugins.
PHP unserialize() / phpMyAdmin — über Jahre wiederkehrende CVEs.
Python Pickle in ML-Modellen — siehe LLM Top 10 LLM03. PyTorch-.pt-Dateien sind Pickle. Wer ein Modell von HuggingFace lädt, lädt potenziell ausführbaren Code. Daher Safetensors-Format als Antwort.
Deserialization verhindern
Universelle Regel: Deserialisiere niemals untrusted Daten in Binärformaten.
Was das praktisch heißt:
- JSON statt Java-Serialization / Pickle / unserialize. JSON deserialisiert auf primitiven Daten-Strukturen ohne Code-Ausführung. (Vorsicht: manche JSON-Libraries haben Polymorphism-Features, die Type-Confusion ermöglichen — siehe Jackson-
enableDefaultTyping-Probleme.) - Schema-Validierung nach Deserialisierung — strenge Typen, keine unerwarteten Klassen.
ObjectInputFilter(Java 9+) — JVM-eingebauter Filter, der erlaubte Klassen für Deserialization whitelisten kann.- Safetensors statt Pickle für ML-Modelle.
- Signaturen auf serialisierten Daten — wer sie verändert, ändert die Signatur; Server lehnt ab.
Konkrete Patterns:
Java ObjectInputFilter:
ObjectInputStream ois = new ObjectInputStream(input);
ois.setObjectInputFilter(info -> {
Class<?> clazz = info.serialClass();
if (clazz == null) return ObjectInputFilter.Status.UNDECIDED;
// Whitelist erlaubter Klassen
if (clazz == MySafeDto.class) return ObjectInputFilter.Status.ALLOWED;
return ObjectInputFilter.Status.REJECTED;
});
return (MySafeDto) ois.readObject();Python — Pickle ersetzen:
import json
# Schadhaft (nie tun mit untrusted Input)
# import pickle; data = pickle.loads(request.body)
# Sicher
data = json.loads(request.body) # nur primitive Typen
# plus Pydantic-/jsonschema-Validierung darüberFür ML-Modelle:
# Schadhaft
# import torch; model = torch.load('model.pt') # Pickle, RCE möglich
# Sicher
from safetensors.torch import load_file
weights = load_file('model.safetensors') # kein Code-AusführungGemeinsame Schutz-Prinzipien
Sowohl XXE als auch Deserialization haben dieselbe strukturelle Wurzel: Daten und Verhalten vermischen sich. Eine vermeintliche Daten-Eingabe enthält Anweisungen, die der Parser bzw. der Deserialisierer als Verhalten interpretiert.
Drei strukturelle Empfehlungen:
- Trennung von Daten und Code in Format-Wahl: JSON statt XML, Schema-validierte Strukturen statt Object-Streams.
- Default-Deny in Parsern — riskante Features explizit aus.
- Defense-in-Depth — selbst bei korrektem Parser nicht alle Eier in einen Korb: Eingabe-Validierung + Sandboxing + Monitoring.
Häufige Stolperfallen
XXE in SAML ist ein klassischer Vektor
SAML-basierte SSO-Implementierungen verarbeiten XML-Token vom Identity-Provider. Wenn der XML-Parser unsicher konfiguriert ist, kann ein:e Angreifer:in über manipulierte SAML-Responses XXE auslösen. Viele Bibliotheken haben historisch Defaults gehabt, die External Entities zuließen — auch wenn das aus Sicht der SAML-Spezifikation niemals nötig ist.
ysoserial: das Java-Deserialization-Standardwerkzeug
ysoserial ist seit 2015 das Standard-Tool für Java-Deserialization-Exploitation. Generiert Payloads basierend auf in der Anwendung verfügbaren Gadget-Chains (Commons-Collections, Spring, Hibernate, etc.). Wer Java-Web-Apps testet, kommt am Tool nicht vorbei.
Pickle ist niemals sicher mit untrusted Input
Python-Docs sagen es seit Jahren explizit: „The pickle module is not secure. Only unpickle data you trust." Trotzdem nutzen unzählige Anwendungen Pickle für Cache, Session-Speicherung, ML-Modelle, ohne sich der RCE-Implikation bewusst zu sein. Safetensors-Format ist die Antwort speziell für ML-Modelle.
Office Open XML als Trojaner-Vektor
DOCX, XLSX, PPTX sind ZIP-Archive mit XML-Inhalten. Ein präpariertes DOCX kann XXE auslösen, wenn das verarbeitende System (Word, LibreOffice, headless Office-Renderer auf Server) den XML-Parser nicht gehärtet hat. Bei Web-Apps mit Office-Upload-Funktion (CV-Portale, Cloud-Office) ist das ein häufiger Test-Vektor.
.NET BinaryFormatter ist deprecated
Microsoft hat 2020 offiziell erklärt, dass BinaryFormatter nicht zu retten ist — er wird in zukünftigen .NET-Versionen entfernt. Wer noch BinaryFormatter, LosFormatter, SoapFormatter oder NetDataContractSerializer nutzt, sollte migrieren — auf JSON (System.Text.Json) oder XML mit strikter Schema-Validierung.
XXE über DTDs auch ohne explizite XML-Eingabe
Manche XXE-Vektoren funktionieren über externe DTDs, die der Parser aus dem Netz lädt. Das passiert auch dann, wenn das Eingabe-XML selbst keine External Entity definiert — der Parser holt sich die DTD-Datei und verarbeitet darin definierte Entities. Daher: auch DTDs deaktivieren, nicht nur External Entities.
ObjectInputFilter ist eine späte JVM-Antwort
Java 9 hat den ObjectInputFilter eingeführt — eine eingebaute Allowlist-Mechanik für Deserialization. Wer auf Java 9+ läuft und Deserialization nicht völlig vermeiden kann, sollte ihn nutzen. Vor Java 9 mussten externe Libraries (z. B. Contrast SafeObjectInputStream) den Job machen.
Weiterführende Ressourcen
Externe Quellen
- OWASP XXE Prevention Cheat Sheet
- OWASP Deserialization Cheat Sheet
- defusedxml — sicherer XML-Parser für Python
- ysoserial — Java Deserialization Payloads
- Safetensors – sicheres Modell-Format
- Java ObjectInputFilter – Doku
- Microsoft – BinaryFormatter Security Guide
- PortSwigger Web Security Academy – XXE
- PortSwigger Web Security Academy – Deserialization