Zum Inhalt springen

XML External Entity Injection (XXE)

CWE-611OWASP A05:2021Aktualisiert 29. August 20265 Min. Lesezeit

XML External Entity Injection (XXE) ist eine Schwachstelle, bei der ein Angreifer einen XML-Parser missbraucht, der externe Entitäten auflöst. Damit liest er lokale Dateien, zwingt den Server zu Anfragen an interne Systeme und legt im schlimmsten Fall die gesamte Anwendung lahm.

XML External Entity Injection, kurz XXE, ist eine klassische, aber hartnäckige Schwachstelle: Ein Angreifer versteckt eine Anweisung in einem XML-Dokument und lässt Ihren Parser Dateien auslesen oder Verbindungen aufbauen, die nie vorgesehen waren. Dieser Beitrag erklärt, was XXE ist, wie ein solcher Angriff abläuft, was ein Angreifer damit erreicht und wie Sie Ihren Parser wasserdicht machen.

Was ist XML External Entity Injection?

XML External Entity Injection (XXE) ist eine Schwachstelle, bei der ein Angreifer einen XML-Parser missbraucht, der externe Entitäten auflöst. XML kennt nämlich “Entitäten”: Abkürzungen, die der Parser beim Einlesen durch ihren echten Wert ersetzt. Eine externe Entität bezieht diesen Wert aus einer Quelle außerhalb des Dokuments, einer lokalen Datei oder einer URL. Verarbeitet der Parser das ungeprüft, bestimmt der Angreifer, welche Quelle geladen wird.

Stellen Sie sich ein Formular vor, auf dem Sie ein Wort abkürzen dürfen und unten auflisten, was jede Abkürzung bedeutet. Normalerweise schreiben Sie “die Firma” neben die Abkürzung “Fa.”. Doch der Ausfüller darf auch notieren: “Fa. bedeutet: Lies den Inhalt des Personaltresors vor und setze ihn an die Stelle von Fa.” Wer das Formular gedankenlos bearbeitet, liest daraufhin den Inhalt des Tresors laut vor. XXE ist genau dieser Trick, nur mit Dateien auf Ihrem Server.

Wie funktioniert ein XXE-Angriff?

XXE entsteht überall dort, wo eine Anwendung XML aus Benutzereingaben mit einem Parser einliest, der DTDs und externe Entitäten zulässt: eine SOAP-Anbindung, ein SAML-Login, eine hochgeladene Office- oder SVG-Datei, ein Konfigurationsimport oder ein REST-Endpunkt, der neben JSON auch XML akzeptiert. Solange der Parser externe Entitäten auflöst, ist der Endpunkt angreifbar.

Verwundbar:

// Verarbeitet hochgeladenes XML mit einem Standard-Parser, der externe Entitäten weiterhin auflöst
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(request.getInputStream());
String nachricht = doc.getElementsByTagName("message").item(0).getTextContent();

Dieser Code liest die Nachricht schlicht aus, ohne dem Parser irgendetwas zu verbieten. Der Angreifer sendet daraufhin kein gewöhnliches XML, sondern ein Dokument mit eigener DOCTYPE-Deklaration und einer externen Entität.

Die bösartige Eingabe:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<message>&xxe;</message>

Beim Einlesen sieht der Parser die Entität &xxe; und ersetzt sie gehorsam durch den Inhalt von file:///etc/passwd. Dieser Inhalt landet im Feld nachricht und gelangt über die Antwort zurück zum Angreifer. So wie das Schlüsselwort SYSTEM eine Datei bezeichnen kann, kann es auch eine URL bezeichnen, etwa http://169.254.169.254/, womit der Angriff in Server-Side Request Forgery übergeht.

Sicher:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Stärkste Maßnahme: jede DOCTYPE-Deklaration verbieten
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Zusätzliches Sicherheitsnetz: externe Entitäten und DTDs deaktivieren
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);

DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(request.getInputStream());

Die sichere Variante greift am Parser selbst ein. Die wichtigste Zeile ist die erste: disallow-doctype-decl weist jedes Dokument mit einer DOCTYPE-Deklaration ab, und ohne DTD kann ein Angreifer schlicht keine Entität mehr definieren. Die übrigen Zeilen bilden ein Sicherheitsnetz, falls ein Framework das DOCTYPE-Verbot nicht unterstützt: Sie deaktivieren externe allgemeine und Parameter-Entitäten und schalten XInclude ab, einen zweiten Weg, um externe Inhalte hereinzuholen. Verarbeitet Ihre Anwendung wirklich keine DTDs (und das trifft auf die allermeisten Anwendungen zu), genügt diese erste Zeile bereits für sich.

Es ist verlockend, nur externe Entitäten zu deaktivieren und DTDs weiter zuzulassen. Doch schon interne Entitäten ermöglichen einen Denial of Service: Eine Handvoll verschachtelter Entitäten, die aufeinander verweisen, bläht das Dokument auf Gigabytes Arbeitsspeicher auf, der bekannte “Billion Laughs”-Angriff. Verbieten Sie daher am besten die gesamte DOCTYPE-Deklaration.

Welche Auswirkungen hat XXE?

Der Schweregrad von XXE reicht von medium bis hoch, weshalb die Einschätzung je nach Situation schwankt. Am oberen Ende gibt der Angriff den Inhalt beliebiger Dateien preis: Konfigurationen mit Passwörtern, private Schlüssel, /etc/passwd oder Quellcode. Da XXE ebenso mühelos eine URL abruft, wird es außerdem zum Sprungbrett für SSRF, mit dem ein Angreifer interne Dienste abtastet und in einer Cloud-Umgebung den Metadaten-Dienst mit seinen temporären Zugangsdaten erreicht.

Technisch reicht der Schaden also über eine einzelne Datei hinaus. Über den Umweg in interne Systeme kartiert ein Angreifer das Netzwerk, und in bestimmten Konstellationen, etwa dem PHP-Modul expect, baut er ein XXE sogar zum Ausführen von Befehlen auf dem Server aus. Am unteren Ende der Skala gerät die Verfügbarkeit unter Druck: Ein Billion-Laughs-Dokument legt den Parser lahm und damit den Dienst. Für das Unternehmen bedeutet das abgeflossene Daten, einen Angreifer, der sich von einem einzigen XML-Endpunkt aus tiefer ins Netzwerk vorarbeitet, und im schlimmsten Fall eine ausgefallene Anwendung. Dass XXE unter die OWASP-Kategorie A05 (Security Misconfiguration) fällt, ist bezeichnend: Die Schwachstelle geht fast immer auf einen Parser zurück, der auf seiner unsicheren Voreinstellung belassen wurde.

Wie spürt man XXE auf?

Bei einem manuellen Test füttert ein Pentester jeden Endpunkt, der XML annimmt, mit einem Dokument samt eigener DOCTYPE-Deklaration und einer Test-Entität und beobachtet, was passiert. Erscheint der Inhalt einer Datei in der Antwort, ist der Fall sofort klar. Bleibt die Antwort leer, versucht der Tester die blinde Variante: eine externe Entität, die eine Verbindung zu einem Server unter eigener Kontrolle aufbaut. Trifft dort eine DNS- oder HTTP-Anfrage ein, löst der Parser externe Entitäten auf und der Endpunkt ist verwundbar, auch ohne sichtbare Ausgabe. Auch Dateiformate, die intern XML nutzen (SVG, DOCX, XLSX) werden erprobt, denn eine Upload-Funktion legt oft denselben Parser offen. Automatische Scanner markieren XML-Endpunkte, übersehen aber gerade die blinden und Out-of-Band-Fälle, die Handarbeit erfordern. AssistSec berücksichtigt XXE standardmäßig in einem Penetrationstest und prüft gezielt diese blinden Varianten.

Wie verhindert man XXE?

  • Verbieten Sie die DOCTYPE-Deklaration vollständig in Ihrem XML-Parser; dies ist die stärkste und einfachste Maßnahme.
  • Ist das nicht möglich, deaktivieren Sie externe allgemeine Entitäten, externe Parameter-Entitäten und die DTD-Verarbeitung ausdrücklich.
  • Schalten Sie XInclude ab, damit dieser zweite Weg zu externen Inhalten geschlossen bleibt.
  • Nutzen Sie, wo möglich, ein einfacheres Format wie JSON; brauchen Sie kein XML, akzeptieren Sie es auch nicht.
  • Prüfen Sie hochgeladene Dateien, die intern XML enthalten (SVG, Office-Dokumente), mit einem ebenso streng konfigurierten Parser.
  • Halten Sie Ihre XML-Bibliotheken und Frameworks aktuell, denn neuere Versionen wählen zunehmend eine sichere Voreinstellung.
  • Betreiben Sie den verarbeitenden Dienst mit minimalen Rechten, damit eine abgeflossene Datei oder eine ausgehende Verbindung möglichst wenig Schaden anrichtet.

Quellen

Häufige Fragen

Was ist der Unterschied zwischen XXE und SSRF?

XXE ist die Schwachstelle: Ein XML-Parser verarbeitet externe Entitäten, die er ablehnen sollte. SSRF ist häufig die Folge: mit einem XXE zwingt ein Angreifer den Server zu Anfragen an interne Adressen. XXE kann zudem lokale Dateien lesen, was SSRF allein nicht vermag.

Sind moderne XML-Parser standardmäßig für XXE anfällig?

Viele ältere Parser lösen externe Entitäten von jeher standardmäßig auf, und genau diese Voreinstellung ist das Problem. Neuere Versionen schalten es zunehmend ab, doch darauf sollten Sie sich nicht blind verlassen: Deaktivieren Sie DTD-Verarbeitung und externe Entitäten ausdrücklich in Ihrem eigenen Code.

Was ist blindes XXE?

Bei blindem XXE sieht der Angreifer die ausgelesenen Daten nicht direkt in der Antwort. Die Lücke bleibt dennoch ausnutzbar: über einen Out-of-Band-Kanal, etwa eine externe DTD, die die Daten an einen Server des Angreifers sendet, oder über Fehlermeldungen, die den Inhalt preisgeben.

Kann XXE zu Remote Code Execution führen?

In bestimmten Konstellationen ja, etwa wenn das PHP-Modul expect geladen ist. Meist bleibt es beim Auslesen von Dateien, bei SSRF oder einem Denial of Service, doch im schlimmsten Fall führt ein Angreifer Befehle auf dem Server aus.

Verwandte Artikel

Zum Suchen / drücken · Esc