XXE: XML external entity injection
XXE (XML External Entity) is a vulnerability class that exploits the external entity feature of the XML standard. XML entities are shortcuts: &name; can be defined to represent a string, but also an external resource (local file or remote URL). When an XML parser resolves these external entities without restriction, the attacker can define entities that read sensitive files or make network requests.
Local file reading
XXE payload: reading /etc/passwd
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<data>&xxe;</data>
</root>
<!-- L'application retourne le contenu de /etc/passwd
dans la réponse si elle affiche le contenu de <data> -->
In direct reading ("in-band"), the file contents are returned in the application's response. When the response doesn't directly contain the value ("out-of-band"), the attacker can exfiltrate data via DNS or HTTP request to a server they control, by defining an entity that loads an external URL containing the file's data.
XXE as SSRF vector
By replacing file:// with http://, XXE becomes an SSRF. The attacker can make requests to internal services, the cloud metadata service, or scan the internal network from the server.
Denial of service through entity expansion (Billion Laughs)
Billion Laughs attack: exponential expansion
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
<!-- Résultat : 10^9 "lol" en mémoire. Le parseur s'effondre. -->
Prevention
The fix is simple: disable external entity processing in the XML parser. This is a configuration option available in all modern parsers. If internal entities are needed, they can be kept by disabling only external entities.
Disabling external entities (Java, Python, PHP)
// Java (DocumentBuilderFactory)
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);
# Python (lxml)
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
# PHP
libxml_disable_entity_loader(true); // Avant PHP 8.0
// PHP 8.0+ : désactivé par défaut
If the application doesn't need to process XML at all (or can switch to JSON), that's the safest solution: don't process XML, or use a parser that ignores DTDs (Document Type Definitions) by default.
Further reading
A question after reading?
Does your application process XML? Send a message.
Or book a 30-minute scoping call directly
Book a call →