Direct naar inhoud

XML external entity injection (XXE)

CWE-611OWASP A05:2021Bijgewerkt 29 augustus 20265 min leestijd

XML external entity injection (XXE) is een kwetsbaarheid waarbij een aanvaller misbruik maakt van een XML-parser die externe entiteiten verwerkt. Daarmee leest hij lokale bestanden, dwingt hij de server verbindingen naar interne systemen te maken en legt hij in het ergste geval de hele toepassing plat.

XML external entity injection, kortweg XXE, is een klassieke maar hardnekkige kwetsbaarheid: een aanvaller verstopt een instructie in een XML-document en laat uw parser bestanden uitlezen of verbindingen opzetten die nooit de bedoeling waren. In deze uitleg leest u wat XXE is, hoe zo’n aanval verloopt, wat een aanvaller ermee bereikt en hoe u uw parser waterdicht maakt.

Wat is XML external entity injection?

XML external entity injection (XXE) is een kwetsbaarheid waarbij een aanvaller misbruik maakt van een XML-parser die externe entiteiten oplost. XML kent namelijk “entiteiten”: afkortingen die de parser tijdens het inlezen vervangt door hun echte waarde. Een externe entiteit haalt die waarde uit een bron búiten het document, een lokaal bestand of een URL. Verwerkt de parser dat klakkeloos, dan bepaalt de aanvaller welke bron wordt ingeladen.

Vergelijk het met een formulier waarop u een woord mag afkorten en achterin een lijstje zet wat elke afkorting betekent. Normaal schrijft u “de firma” achter de afkorting “fa.”. Maar de invuller mag ook noteren: “fa. betekent: lees de inhoud van de personeelskluis voor en zet die op de plek van fa.” Wie het formulier gedachteloos afhandelt, spreekt vervolgens hardop de inhoud van de kluis uit. XXE is precies die truc, maar dan met bestanden op uw server.

Hoe werkt een XXE-aanval?

XXE ontstaat overal waar een applicatie XML uit gebruikersinvoer inleest met een parser die DTD’s en externe entiteiten toestaat: een SOAP-koppeling, een SAML-login, een geüpload Office- of SVG-bestand, een import van een configuratie of een REST-endpoint dat naast JSON ook XML accepteert. Zolang de parser externe entiteiten oplost, is het endpoint aanvalbaar.

Kwetsbaar:

// Verwerkt geüploade XML met een standaardparser die externe entiteiten nog steeds oplost
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(request.getInputStream());
String bericht = doc.getElementsByTagName("message").item(0).getTextContent();

Deze code leest het bericht simpelweg uit, zonder de parser ook maar iets te verbieden. De aanvaller stuurt dan geen gewone XML, maar een document met een eigen DOCTYPE en een externe entiteit.

De kwaadaardige invoer:

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

Bij het inlezen ziet de parser de entiteit &xxe; staan en vervangt die gehoorzaam door de inhoud van file:///etc/passwd. Die inhoud belandt vervolgens in het veld bericht en komt via de respons weer bij de aanvaller terug. Precies zoals het SYSTEM-sleutelwoord een bestand aanwijst, kan het ook een URL aanwijzen, http://169.254.169.254/ bijvoorbeeld, waarmee de aanval overgaat in server-side request forgery.

Veilig:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Sterkste maatregel: verbied elke DOCTYPE-declaratie
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Extra vangnet: schakel externe entiteiten en DTD's uit
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());

De veilige variant grijpt in bij de parser zelf. De belangrijkste regel is de eerste: disallow-doctype-decl weigert elk document met een DOCTYPE, en zonder DTD kan een aanvaller domweg geen entiteit meer definiëren. De overige regels vormen een vangnet voor het geval een raamwerk het DOCTYPE-verbod niet ondersteunt: ze zetten externe algemene en parameter-entiteiten uit en schakelen XInclude uit, een tweede route om externe inhoud binnen te halen. Verwerkt uw applicatie echt geen DTD’s (en dat geldt voor verreweg de meeste toepassingen), dan is die eerste regel op zichzelf al afdoende.

Het is verleidelijk om alleen externe entiteiten uit te zetten en DTD’s toe te staan. Maar met louter interne entiteiten is nog steeds een denial of service mogelijk: een handvol geneste entiteiten die naar elkaar verwijzen, blaast het document op tot gigabytes geheugen, de bekende “billion laughs”-aanval. Verbied daarom bij voorkeur de hele DOCTYPE-declaratie.

Wat is de impact van XXE?

De ernst van XXE loopt uiteen van medium tot hoog, en dat verklaart waarom de inschatting per situatie verschilt. Aan de bovenkant lekt de aanval de inhoud van willekeurige bestanden: configuraties met wachtwoorden, privésleutels, /etc/passwd of broncode. Omdat XXE net zo makkelijk een URL opvraagt, wordt het bovendien een opstapje naar SSRF, waarmee een aanvaller interne diensten aftast en in een cloudomgeving de metadata met tijdelijke inloggegevens bereikt.

Technisch reikt de schade dus verder dan één bestand. Via de omweg naar interne systemen brengt een aanvaller het netwerk in kaart, en in specifieke opstellingen, denk aan de PHP-module expect, buigt hij een XXE zelfs om tot het uitvoeren van commando’s op de server. Aan de onderkant van de schaal staat de beschikbaarheid onder druk: een billion-laughs-document legt de parser plat en daarmee de dienst. Voor het bedrijf betekent dat weggelekte gegevens, een aanvaller die zich vanuit één XML-endpoint dieper het netwerk in werkt, en in het slechtste geval een uitgevallen applicatie. Dat XXE onder OWASP-categorie A05 (Security Misconfiguration) valt, is veelzeggend: de kwetsbaarheid komt bijna altijd voort uit een parser die op zijn onveilige standaard blijft staan.

Hoe spoor je XXE op?

Bij een handmatige test voert een pentester elk endpoint dat XML inneemt een document met een eigen DOCTYPE en een testentiteit toe, en let op wat er gebeurt. Verschijnt de inhoud van een bestand in de respons, dan is de zaak meteen duidelijk. Blijft de respons leeg, dan probeert de tester de blinde variant: een externe entiteit die een verbinding opzet naar een server onder eigen beheer. Komt daar een DNS- of HTTP-verzoek binnen, dan verwerkt de parser externe entiteiten en is het endpoint kwetsbaar, ook zonder zichtbare uitvoer. Ook bestandsformaten die intern XML gebruiken (SVG, DOCX, XLSX) worden beproefd, want een uploadfunctie ontsluit vaak dezelfde parser. Geautomatiseerde scanners markeren XML-endpoints, maar missen juist de blinde en out-of-band-gevallen die handwerk vergen. AssistSec neemt XXE standaard mee in een penetratietest en test nadrukkelijk die blinde varianten.

Hoe voorkom je XXE?

  • Verbied de DOCTYPE-declaratie volledig in uw XML-parser; dit is de sterkste en eenvoudigste maatregel.
  • Kan dat niet, schakel dan externe algemene entiteiten, externe parameter-entiteiten en DTD-verwerking expliciet uit.
  • Zet XInclude uit, zodat die tweede route naar externe inhoud dichtblijft.
  • Gebruik waar mogelijk een eenvoudiger formaat zoals JSON; heeft u geen XML nodig, accepteer het dan ook niet.
  • Controleer geüploade bestanden die intern XML bevatten (SVG, Office-documenten) met een parser die net zo streng is ingesteld.
  • Houd uw XML-bibliotheken en frameworks up-to-date, want nieuwere versies kiezen steeds vaker een veilige standaard.
  • Draai de verwerkende dienst met minimale rechten, zodat een gelekt bestand of een uitgaande verbinding zo min mogelijk schade aanricht.

Bronnen

Veelgestelde vragen

Wat is het verschil tussen XXE en SSRF?

XXE is de kwetsbaarheid: een XML-parser verwerkt externe entiteiten die hij zou moeten weigeren. SSRF is vaak het gevolg: met een XXE dwingt een aanvaller de server verbindingen te maken naar interne adressen. XXE kan daarnaast ook lokale bestanden lezen, wat SSRF op zichzelf niet doet.

Zijn moderne XML-parsers standaard kwetsbaar voor XXE?

Veel oudere parsers verwerken externe entiteiten van oudsher standaard, en juist die standaardinstelling is het probleem. Nieuwere versies zetten het steeds vaker uit, maar u kunt er niet blind op vertrouwen: schakel DTD-verwerking en externe entiteiten expliciet uit in uw eigen code.

Wat is blinde XXE?

Bij blinde XXE krijgt de aanvaller de uitgelezen data niet rechtstreeks in de respons te zien. Toch is de kwetsbaarheid uitbuitbaar: via een out-of-band-kanaal, bijvoorbeeld een externe DTD die de data naar een server van de aanvaller stuurt, of via foutmeldingen die de inhoud lekken.

Kan XXE tot remote code execution leiden?

In specifieke opstellingen wel, bijvoorbeeld wanneer de PHP-module expect is geladen. Meestal blijft het bij het uitlezen van bestanden, SSRF of een denial of service, maar in het slechtste geval draait een aanvaller commando's op de server.

Verwante artikelen

Druk op / om te zoeken · Esc