fastjson反序列化<=1.2.47绕过
0x01 前言
上篇文章主要总结了fastjson在1.2.24版本的漏洞利用,在后续的版本中,fastjson做了一个漏洞的修复,导致原先的payload用不了了。本篇文章主要分析fastjson<=1.2.47版本下的绕过,重点分析在不开启AutoTypeSupport下的漏洞利用。
0x02 fastjson在1.2.25版本的修复
我们可以先尝试打一下原来的payload
报错了,提示反序列化的类不支持,看一下它的源码是怎么修复的。
checkAutoType
这里由原来的loadClass替换为checkAutoType函数,其实其他地方都大差不差,都是在这个checkAutoType函数中做了检测和限制。在后续高版本fastjson的修复也都是在这个函数中做进一步的优化。重点来分析一下checkAutoType函数内部的代码逻辑,直接看注释。
public Class<?> checkAutoType(String typeName, Class<?> expectClass) { |
这个函数加了黑白名单做限制,白名单默认为空,黑名单里的类写死了改不了。
有很多,里面也包含了com.sun包下的漏洞类。当然,黑白名单这里我们是没法绕过去的,那么就找一下有什么特殊情况可以返回我们反序列化的恶意类。
寻找利用点
在我们对checkAutoType函数逻辑分析的时候,有这么一段代码:
它会从mapping缓存和反序列化器里面找有没有与之对应的类,然后返回我们的类。如果我们可以将恶意的类放进缓存中,它直接从缓存里拿并反序列化,就绕过了所谓的安全限制。这也就是接下来主要分析的1.2.47版本之前的绕过手法。
如果开启了,或者说我们能够开启autoTypeSupport开关,
直接可以loadClass,那就简单多了,本篇文章不主要讲。
0x03 fastjson<=1.2.47绕过方法分析
跟进到getClassFromMapping方法
TypeUtils.loadClass
mappings继承了HashMap,查找用法看看有哪些地方可以put进去我们的恶意类。可以利用的地方在TypeUtils类的loadClass方法里。
classLoader默认为null,自然会走到这个if里面,如果className赋值为我们的恶意类,就可以放到缓存里面了。继续查找方法看看什么地方调用了TypeUtils的loadClass方法。
MiscCodec.deserialze
在MiscCodec类的deserialze方法里
如果clazz是Class类型的话,就调用这个loadClass方法,先看看MiscCodec是个什么东西
很明显,它是一个反序列化器。那什么情况下调用该反序列化器的deserialze方法呢?在DefaultJSONParser类中有这样一段逻辑:
其实checkAutoType函数检测返回类后是会走到这里来的,它从config中,也就是ParserConfig类中获取反序列化器,然后从获取来的反序列化器反序列化我们的类。
可以看一下deserializers的默认配置
有很多,指定的类对应不同的反序列化器,这里Class类型的类也是默认用MiscCodec反序列化器,那么就与上文调用loadClass的那个if判断对接上了。还有一个细小的代码段:
检测传入的JSON串的键值是否为val,如果不为val,就抛出异常,这点在写payload的时候要注意。
思路整理与利用
目的是为了让我们的恶意类放入缓存中,可以提前反序列化一个java.lang.Class类,添加val属性,属性值为执行的恶意类,它会走到TypeUtils的loadClass方法中,将恶意类添加到缓存中,然后反序列化我们的恶意类,它会直接从缓存里拿,从而绕过安全检测。
java.lang.Class-->MiscCodec.deserialze-->TypeUtils.loadClass-->mappings.put |
编写exp:
package fastjson.test3; |
运行代码
调试与分析
可以调试看一下,只看关键部分
进入到checkAutoType函数,此时的typeName是java.lang.Class
这个类可以直接从缓存里面找到,然后直接返回类
然后就到了最下面,根据typeName去查找对应的反序列化器,这里我们可以看到反序列化器类是MiscCodec类,接下来就是调用它的deserialize函数
此时满足这个if条件,去调用loadClass,strVal已经赋值为我们的恶意类了
可以看到mappings里面已经有我们的恶意类了,然后到了下一轮,开始反序列化第二个@type,也就是恶意类
在这个安全函数里,typeName已经换成了com.sun.rowset.JdbcRowSetImpl
缓存里面有,直接拿了,没有走到黑白名单检测那里,直接返回。后续就正常反序列化执行恶意代码了,不再看了。这种绕过手法在fastjson的1.2.25到1.2.47版本下都是能用的。
简单说说开启AutoTypeSupport的利用
上面总结的在缓存里放恶意类的利用手法前提是不开启这个开关,当然默认也是不开启的。那么如果开启了,回看上面的代码,其实更简单了
直接loadClass了,当然前面需要经过黑名单的检测,那么问题就变成了如何绕过黑名单了。本次用的fastjson版本是1.2.25,就拿这个版本分析一下。看一下loadClass函数
重点是这段代码,检测反序列化的类开头是否是L,结尾是否是分号。如果是,就去掉,再进行loadClass,一个递归操作。如果修改恶意类是这样子
Lcom.sun.rowset.JdbcRowSetImpl; |
就可以绕过黑名单,另外在检测到L和分号的时候去空,就可以正常的反序列化了。
后续版本可能做了修复,但是在开启AutoTypeSupport的前提下,还是有办法绕过黑名单。可以参考以下文章:
Java反序列化Fastjson篇03-Fastjson各版本绕过分析
0x04 小结
本篇文章主要分析了fastjson在1.2.25到1.2.47版本下的漏洞利用,后续在更高版本的fastjson,这个利用手法也是打不通的,在后续的时间里,我会继续分析更高版本,例如1.2.68的反序列化漏洞利用。
反序列化之路任重而道远