Pentesterlab系列:Play框架xml实体攻击

Play框架xml实体攻击

1 介绍

这个课程详细讲述了利用Play框架xml实体漏洞的过程,这个漏洞可被用来获取任意文件,也可以列出任意目录的内容。

关于这个bug的一个有趣的事情在于,这个漏洞是完全公开的,却在很长一段时间没有被注意到。要想在黑盒测试中找到这个bug,你需要明白自己要找什么。如果你不想看本文,可以去看官方的文档https://www.playframework.com/security/vulnerability/20130920-XmlExternalEntity

2 Play框架

Play框架是一个web框架,它允许开发人员用Java或Scala快速构建web应用程序。 代码的组织方式和URL映射非常类似于ruby on rails。

3 漏洞详情

解析XML消息时,最重要的安全检查是确保XML实体已被禁用。 XML实体可以用来告诉XML解析器获取特殊的内容,比如从以下位置:

  1. 从文件系统
  2. 从web服务器
  3. 从ftp服务器

这显然可以从被攻击者获取应用程序的敏感信息(路径、密码、源代码,…)。

这个影响play框架的bug是一个xml实体bug,然而这个攻击在响应中是不会显示任何信息的,这就是为什么下面我们会需要其他方法来获取信息。

4 利用过程

利用过程有以下几步

  1. 发送初始化请求(第一步)
  2. 提供DTD(第二,三步)
  3. 获取服务器发送的信息
  4. 调试目标

图片1

1 发送初始化请求

首先我们需要发送正确的HTTP请求,做到这件事最简单的方法是就是构建一个脚本来连接服务器,发送请求,我们并不关心响应但仍能获取他,你能用一个代理(推荐burp的repeater模式)做到同样的事情或者手动的用netcat,区别在于使用netcat你需要手动设置Content-Length头

初始化请求需要是一个POST请求,来保证框架会解析请求的内容,这里的这个应用非常简单,在登陆的时候,我们可以看到,一个POST请求会被发送:

 

现在我们需要修改请求来发送xml,有以下几个步骤

  1. 移除所有不需要的信息让调试更简单
  2. 在请求主体中增加xml信息
  3. 改变请求中的 Content-Type

最后我们需要增加xml实体payload

http://192.168.159.1:3000/test.dtd 是dtd文件的位置

现在我们已经有了包含xml的正确HTTP请求,我们将其发送到服务器,如果一切正常,服务器会返回一个400错误,因为我们的dtd文件不存在。

2 提供DTD

为了提供一个dtd文件或其他文件,我们需要一个web服务器。我们可以使用任何服务器做到这一点,但是我们首先需要确认服务器是否真的能获取到DTD文件。在一个真实的环境中,被攻击服务器或许无法访问到我们的服务器,所以我们要先测试一下。

要做到这件事最简单的方法是:

1 运行一个小型服务器,我个人一般是使用Webrick,并且使用一个shell别名随时准备启动web服务器

2 运行web服务器,对日志文件使用tail -f来查看每一条接收到的请求

使用上面的别名,你会看到如下的请求:

当看到以上的信息时,要确保你能通过浏览器访问到dtd文件,然后你会看到如下请求

为了让服务器发送内容给我们,我们需要提供如下的DTD

DTD文件会强迫XML解析器读取/etc/passwd的内容并赋值给变量p1,然后创建另一个变量p2,包含有到我们服务器的连接和p1的值,然后会使用%p2;输出p2的值,当解析后,DTD会变为

[/etc/passwd] 是 /etc/passwd 的内容

如果你回头去看我们最初发送的请求,内容就包括了对e1的引用<foo>&e1;</foo>

当服务器结束了对DTD文件的处理,就会解析对e1的引用,然后发送/etc/passwd的内容到我们的服务器

3 获取信息

最后,我们需要一个获取信息的方法,可以像下面这样做:

1 netcat -l -p 3001 缺点是每次你使用此端口后需要重启进程

2 socat TCP-LISTEN:3001,reuseaddr,fork – 在第一次请求之后不会关闭,但是在一些请求之后或许会阻塞

现在我们一切就绪,让我们获取/etc/passwd的内容

图片2

在上半部分右边,我们能看到最后的请求和/etc/passwd的内容一起在url中

5 在一般情况下检测这种类型的漏洞

在一般情况下,你不能确定服务器是否会被允许反向连接到你,为了检测这种bug(并且判断服务器是否解析外部名称),你可以使用DNS

要做到这一点,你只需要配置一个DNS服务器,并且监控他的日志。然后你可以发送一个包含指向你自己域名的xml实体的请求http://rand0m123.blah.ptl.io/,如果服务器存在xml实体漏洞(并且可以解析外部域名),你会看到一个来自漏洞服务器的DNS查询

6 找到秘密URL

现在一切顺利的话,我们就需要开始找那个秘密URL,play框架使用了一个名为route的文件哪些url可用,哪些方法能被调用。我们需要找到这个文件来获得对秘密URL的访问。

找到应用位置的常见方法是访问环境,这可以通过读/proc/self/environ做到。然而,当不支持解析器从/proc读取时不会奏效(或许是因为他使用了DataInputStream).

如果我们返回到/etc/passwd的内容并且解开url编码(例如使用ruby),可以看到一个名叫play的用户存在

这个用户的home目录在/opt/play-2.1.3/xxe/,这是一个很好的确定应用位置的机会。

取决于不同的xml解析器,也有可能获取一个目录的文件列表,测试的唯一方法就是实际去尝试获取。在这儿,我们可以修改DTD文件,使其指向/opt/play-2.1.3/xxe/

我们会看到目录的文件列表

同样可以被解码为

通过这种方法,你可以找到conf/routes。当你能够获取routes文件时,你也就能够访问到秘密URL了

7 拦截session

另一个对play框架应用来说很重要的文件是application.conf,这个文件包含了用于生成session的密钥.这个文件同样也在conf目录里,一旦获取这个文件之后,就可以通过这个密钥很容易的生成自己想要的session.

首先,你需要用上面讲解的方法获取conf/application.conf.第二步是通过这个密钥伪造自己的session.要做到这一步,我们需要对session有更深的理解,我们可以通过获取源码来更好的理解这儿的逻辑

根据conf/routes,我们可以知道,当提交login表单时controllers.Application.login方法会被调用,一般来说,这段代码位于app/controllers/Application.java.

当我们获取了这个控制器的源代码,我们可以看到session管理是通过存取一个名为user的变量

我们需要伪造一个session,包含变量user和值admin.如果你看过我们之前另一个关于play框架的课程:play框架session注入,你可能会很惊讶的发现paly框架dsession的结构改变了.

之前的模式是这样的

再这个版本的play框架里,会使用如下的方式

程序使用的代码可以在framework/src/play/src/main/scala/play/api/mvc/Http.scala找到

我们现在可以增加我们自己的变量:user=admin

最后,我们可以生成session,程序生成代码如下

再ruby中可以这样写

最后一步是找到session所在cookie的名字,这个名字是没有改变的,我们可以在conf/application.conf找到,默认的名字是
PLAY_SESSION

在我们的浏览器设置cookie后,可以看到我们已经以admin登陆了

图片3

总结

这个课程讲解了怎样利用play框架中的xml实体漏洞,,这个漏洞的有趣之处在于他影响了程序本身,但却是以和程序员使用方式相反的方式.希望你能享受在pentesterlab学习的过程.

镜像下载及原文地址 https://www.pentesterlab.com/exercises/play_xxe

【版权属于原作者Pentesterlab.com 翻译by:91RI团队-君莫笑】