XXE漏洞
DTD:
//EMPTY关键字表示元素是个空元素<!ELEMENT root ANY> //ANY关键字表示元素中可以出现任何内容,也可以为空 //下面这个声明表示root中可以有文本,也可以是空 <!ELEMENT root (#PCDATA)> //()表示一个分组,其中是放的允许在元素出现的内容,#PCDATA表示文本 <!ELEMENT root (child)> //child是子元素的名称,这个声明表示root中必须且只能有一个child元素 <!ELEMENT root (child1,child2)> //以逗号分隔,表示子元素依次出现 <!ELEMENT root (child1|child2)> //竖线与"OR"的意思相近,表示root元素中只能出现child1或child2 <!ELEMENT root (child?)> //root中child子元素可以出现一次,也可以不出现 <!ELEMENT root (child+)> //root中child子元素至少出现一次 <!ELEMENT root (child*)> //root中child子元素可以出现任意次数或不出现 <!ELEMENT root (child,(a,b))> //()还可以嵌套,这里表示root元素中第一次子元素必须是child //紧接着是a或b <!ELEMENT root (child,(a,b)+)> //*,?,+这些量词可作用于分组,这里表示root元素中第一次子元素必须是child //紧接着是a或b出现一次或多次
实验
环境搭建
xmlres.php代码:
1 |
|
实验一:踩坑普通XXE
思考:为什么不能直接在XML文件中进行参数实体拼接然后引用呢?如下:
建立a.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
访问php文件
1 | http://127.0.0.1/XML/xmlres.php?file=a.xml |
报错:Warning: DOMDocument::loadXML(): PEReferences forbidden in internal subset in Entity…
原因:In the internal subset of DTD, references to parameter entities are not allowed within markup declarations. You have to use an external DTD规则 也就是说在DTD内部子集中,参数实体的引用不允许在标记声明里。在外部DTD中,是可以的
更改如下:
新建外部dtd文件a.dtd :
1 | <?xml version="1.0" encoding="utf-8"?> |
修改a.xml如下:
1 | <?xml version="1.0" encoding="utf-8"?> |
执行php文件,继续报错:Warning: DOMDocument::loadXML(): EntityValue: ‘%’ forbidden except for entities references in file:///var/www/html/XML/a.dtd
原因:报错已经说的很清楚了,%不允许出现在Entity的value中. 所以需要将%进行Unicode编码为%
(在xml中Unicode编码与字符本身是一样的)
更改如下:
修改a.dtd :
1 | <?xml version="1.0" encoding="utf-8"?> |
执行PHP后发现并没有对%start;%goodies;%end
进行解析
原因:需要在外面包一个参数实体int的声明,%int;
引用的时候%file;会被成功解析,然后再%all;就可以成功的将解析%goodies;
的内容传出。所以%int
只是辅助的作用,用于辅助解释send实体内容。
更改如下:
修改a.dtd :
1 | <?xml version="1.0" encoding="utf-8"?> |
修改a.xml :
1 | <?xml version="1.0" encoding="utf-8"?> |
正常解析file中的文件内容
实验二 :通过重写本地dtd中的参数实体实现调用
有时候服务器配置好防火墙,禁止服务器请求外网dtd文件的话,就不能外带数据了。这时候就可以考虑利用本地dtd来进行XXE。
Ubuntu系统自带/usr/share/yelp/dtd/docbookx.dtd
它定义了很多参数实体并调用了它。我们可以在内部重写一个dtd文件中含有的参数实体,而此时调用是在外部,可以实现。
1 | <?xml version="1.0"?> |
OOB
1 | <?xml version="1.0" ?> |
基于报错的Bind XXE
基于报错的原理和OOB类似,OOB通过构造一个带外的url将数据带出,而基于报错是构造一个错误的url并将泄露文件内容放在url中,通过这样的方式返回数据。
请求外部服务器方式
a.xml :
1 | <?xml version="1.0"?> |
a.dtd :
1 | <!ENTITY % start "<!ENTITY % send SYSTEM 'file:///hhhhhhh/%file;'>"> |
执行php
1 | echo "YXNkCjw+Cjw/Cg==" | base64 -d |
请求本地dtd文件方式
ubuntu系统自带/usr/share/yelp/dtd/docbookx.dtd
1 | <?xml version="1.0"?> |
SSRF
通过回显时间、回显长度判断端口是否开放
1 | <?xml version="1.0" encoding="UTF-8"?> |
命令执行=
php的expect://
协议可以用来执行命令
为了使用 expect:// 封装器,必须安装 » PECL 上的 » Expect 扩展
关于XXE漏洞挖掘
XML作为介质传输流程应该是这样的:
用户传输敏感数据->xml形式传输->后端解析xml(loadXML)->将各DOM节点转化为SimpleXML节点(最终为数组形式,节点名为键名,节点值为键值)->提取对应节点键值->数据提取/用户判断
漏洞点就在后端解析xml。
当后端使用**loadXML()**的方法解析xml文档时,会解析恶意xml语句即外部实体的引用,从而造成漏洞。
在挖掘漏洞的时候尤其注意两点:
- content-type: application/xml
- xml形式的数据传输e.g:
<user>admin</user>
关于防御
-
对于PHP,禁止引用外部实体
1
libxml_disable_entity_loader(true);
-
对于其它语言,其实做好过滤就行了。
相关CTF题目
网鼎杯2020 filejava
DozerCTF2020 svgggggg!
GoogleCTF2019 Bnv
https://www.freebuf.com/vuls/207639.html
在今年的Google CTF 中出了一道Blind XXE 题 bnv,这道题完整WP可以参考这里,这里只分析Blind XXE部分。
这题目可以从错误响应中泄露信息。因为题目无法和外界通信,通过引入本地DTD文件,payload并不复杂,就和上面分析的一样
1 | <?xml version="1.0"?> |
如果不引用外部DTD文件,直接通过嵌套参数实体,这道题同样可以做出来。
1 | <?xml version="1.0"?> |
https://blog.csdn.net/lileiyuyanqin/article/details/72828922
https://www.cnblogs.com/backlion/p/9302528.html
https://www.freebuf.com/column/156863.html
https://www.freebuf.com/column/208904.html
https://www.freebuf.com/articles/web/126788.html
https://www.freebuf.com/vuls/194112.html
https://www.cnblogs.com/r00tuser/p/7255939.html
https://www.freebuf.com/column/188849.html
https://www.jianshu.com/p/d73b0ee9eeae