2023阿里云CTF-ezbean题目复现与分析
0x01 前言
最近也不怎么想学习了,这道题的复现前天就开始了,一直鸽到了现在。感觉对fastjson还是不太熟,这道题复现完也不是很容易,里面有些细小的知识点还不太弄懂,先水一篇文章吧,后续再完善一下。
0x02 题目分析
当时比赛后我直接把源码赋值粘贴在idea里,这样更好看。先看依赖

没啥特别的,就是1.2.60版本的fastjson
控制器中也只有一个路由,作为反序列化的入口点

另外重写了一个输入流的类,有黑名单的限制,这只是在传统的反序列化中做的限制,后面会说到。

另外还定义了一个javabean,代码就不全贴出来了,不过有一个getter方法值得我们注意

调用任意类的connect方法,这可以作为提示,也是突破口。
0x03 利用思路分析
RMIconnetor类与JNDI注入
前段时间看过二次反序列化,对这个类也不太陌生,它也可以二次反序列化,但是本题中不需要用到它的这个功能。跟进到它的connect方法来看一看
RMIconnetor#connect

我们需要走到这个findRMIServer方法里,就需要满足rmiServer为空,这个方法的参数是jmxServiceURL对象

不赋值就默认为空了,跟进findRMIServer方法

它这里会获取到jmxServiceURL对象的路径,这里路径开头要是”/jndi/“,因为我们要打JNDI注入。跟进到findRMIServerJNDI方法

漏洞利用点就在这里,接下来就是jmxServiceURL对象里的路径怎么写,跟进看一下


按照严格的路径格式,不然会抛出异常
根据这些信息就可以写一个漏洞触发的demo了
package com.ctf.ezser.test; |
运行后也是可以弹出计算器的。RMIConnector类的全限定名是在黑名单里面的,这里就需要把它放在fastjson里,黑名单只限制在了普通的序列化中,而fastjson序列化有自己独有的一套规则,不受黑名单的限制,而在序列化的时候会调用getter方法,也就是需要调用Mybean类的getConnect方法,那么这一切都说的通了。
JSONArray的toString方法触发getter的分析
链子的后半段分析完了,前半段就是寻找能够调用调用getter的利用链,这里主要分析toString方法触发getter。
我们去看JSONArray的toString方法,实际上就是看JSON的toString方法。跟进后看,调用的是toJSONString方法,这个方法不就是fastjson序列化的方法嘛,总的来说就是实现一个fastjson序列化的功能

创建一个字符流对象out,实例化一个序列化器,把序列化后的结果赋值给out,所以这个write算是更深层次的序列化函数,跟进。
这种源码还是调试看比较好

通过不同的java类选择自己的ObjectSerializer,无关紧要,

ListSerializer#write
跟进到ListSerializer的write方法里

遍历出JSONArray中的键值是Mybean类,然后就判断这个类的类型。

找到Mybean这个类所对应的序列化器。可以跟进这个方法看一下,原本没有Mybean所对应的序列化器,找不到,所以它需要动态的创建自己的序列化器。

跟进这个方法,

遍历Mybean的字段信息,

此时的beanInfo就是对Mybean类的封装,获取到这些信息后创建ASM序列化器,这是动态生成的,可以跟进看一下创建序列化器的工厂方法。

遍历它的字段和getter方法,然后创建如下命名格式的序列化器。然后我们回到ListSerializer的write方法

可以看到是创建了它的序列化器的,接下来调用write方法完成序列化,调用Mybean的所有getter方法。
那么回过头来,什么方法调用了toString方法,那么这个方法就很容易想到了
BadAttributeValueExpException#readObject
这个作为反序列化的入口类就很常见了

不必再说。
0x04 题目复现
思路整理
可以简单总结一下这道题的思路,大致分为三个部分
第一,也就是题目中的反序列化点,利用BadAttributeValueExpException类的readObject方法来调用到fastjson库里json的toString方法,从而转变为fastjson的序列化,也顺便绕过黑名单。
第二,触发toString方法后会自动调用toJSONString方法,执行fastjson的序列化并且触发getter。
第三,Mybean的getConnect触发后,会调用RMIconnetor的connect方法,导致JNDI注入执行恶意命令。
EXP
package com.ctf.ezser; |
复现
运行exp生成base64编码串,全部url编码

Yakit做好反连,执行谈计算器的命令。

疑惑与思考
这里发包三次才会成功,前两次在控制台的时候可以看到,是发生报错的

它说JMXServiceURL类没有默认的无参构造方法。
网上看到有文章解释这个问题,fastjson反序列化的时候会先从一个map中,会判断这个类是否在map中,如果在,就直接拿出来反序列化,如果不在,就把它放到map里,然后判断是否存在无参构造方法。
这里发包三次分别为
将RMIConnector存入map,将JMXService存入map,将这两个类直接拿出来反序列化。
感兴趣可以自己调试一下。
0x05 小结
最近这段时间的学习效率确实不是太高,往后得多安排时间学了。后续打算还是刷一些java题,或者复现一些CMS漏洞,积累一些经验和看代码的能力。
参考文章: