fastjson反序列化漏洞
背景
FastJson是开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到Java Bean。Fastjson应用范围非常广,在github上star数超过22k。2017年3月15日,fastjson官方主动爆出fastjson在1.2.24及之前版本存在远程代码执行高危安全漏洞。攻击者可以通过此漏洞远程执行恶意代码来入侵服务器。2020年6月1日 fastjson爆发新的反序列化远程代码执行漏洞,fastjson 1.2.68及以前版本存在黑客利用漏洞,可绕过autoType限制,直接远程执行任意命令攻击服务器,风险极大。本文简要介绍Fastjson反序列化漏洞利用原理并复现被利用过程,以使得技术人员在工作中对Fastjson反序列化漏洞有更进一步的认识,提高重视程度。
原理分析
漏洞原理
Fastjson反序列化漏洞被利用的原因,可以归结为两方面:
- Fastjson提供了反序列化功能,允许用户在输入JSON串时通过“@type”键对应的value指定任意反序列化类名;
- Fastjson自定义的反序列化机制会使用反射生成上述指定类的实例化对象,并自动调用该对象的setter方法及部分getter方法。
对编程人员而言,在使用Fastjson反序列化时会使用到Fastjson所提供的几个静态方法:
- parse (String text)
- parseObject(String text)
- parseObject(String text, Class clazz)
无论使用上述哪种方式处理JSON字符串,都会有机会调用目标类中符合要求的Getter方法或者Setter方法,如果一个类中的Getter或者Setter方法满足调用条件并且存在可利用点,那么这个攻击链就产生了。
|
|
输出顺序
|
|
Fastjson允许用户在输入JSON串时通过**“@type”键对应的value指定任意反序列化类名**,为了让服务端按照指定类型反序列化,所以这里再引入一个@type。通过type指定了反序列化的类型,在给parseObject传参时,@type指定了当前字符串按照Poc类来解析。在Poc类中写了一个无参构造方法、getter和setter方法。根据结果显示,在反序列化时同时调用了无参构造函数以及getter和setter方法,setter方法中中通过exec执行了外部命令计算器。
漏洞利用
对于 **fastjson版本 <= 1.2.24
**的情况,利用思路主要有2种
- 通过触发点**
JSON.parseObject()
这个函数,将json
中的类设置成com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
**并通过特意构造达到命令执行 - 通过**
JNDI注入
**
|
|
≤1.2.24
|
|
绕过
|
|
JDNI(≤1.2.47)
过程
将com.sun.rowset.JdbcRowSetImpl存入 mappings 中
DefaultJSONParser
→checkAutoType
->getClassFromMapping(为null)
-> deserializers.findClass(typeName)
,所以返回java.lang.class
→通过 config.getDeserializer
获得反序列化的路由类 MiscCodec
->调用该路由类的deserialze(this, clazz, fieldName)
方法→
parser.parse()
提取com.sun.rowset.JdbcRowSetImpl
赋值给objVal
→TypeUtils.loadClass
->loadClass的重载方法将com.sun.rowset.JdbcRowSetImpl
存入checkAutoType的mappings中
绕过并执行实现RCE
前面一样不过传入的typename是com.sun.rowset.JdbcRowSetImpl→checkAutoType
->由于第一次解析中将com.sun.rowset.JdbcRowSetImpl
存入 mappings
中通过TypeUtils.getClassFromMapping(typeName)
;获取到 com.sun.rowset.JdbcRowSetImpl
对象然后返回该对象(完成绕过)→调用deserializer.deserialze(this, clazz, fieldName)
,不同的是这次得到的反序列化路由类为FastjsonASMDeserializer
。→deserialze
方法→在setValue
中通过method.invoke(object, value)
反射执行com.sun.rowset.JdbcRowSetImpl.setAutoCommit
方法
JdbcRowSetImpl利用链
跟进setAutoCommit
中的this.connect()
方法
this.connect
对成员变量dataSourceName
进行lookup
,成功利用jndi注入
利用链
|
|
漏洞复现
文章内环境配置完全省略,请自行寻找环境配置教程
复现针对1.2.47的反序列化漏洞,采用JDNI注入的方式
环境配置
- docker
- docker-compose
- vulhub
- win10
- Ubuntu18.04 VPS
- java1.8
- python3.7
- marshalsec
- burpsuite
准备工作
主要是用python3起的http目录服务和marshalsec起的rmi服务,靶机则是用的vulhub+docker-compose
靶机
https://github.com/vulhub/vulhub
使用vulhub的docker文件
目录为vulhub/fastjson/1.2.47-rce
|
|
docker和docker-compose以及相关使用请自行学习
目录服务
首先准备一个java恶意类
|
|
并且编译
|
|
然后在恶意类的目录下用python起一个http服务(注意这里使用的python是3.7,python2语法不同请自行百度)
|
|
准备RMI服务器
https://github.com/mbechler/marshalsec
借助marshalsec项目,启动一个RMI项目,然后编译项目,然后在target下运行
|
|
成功启动RMI服务
payload
result
|
|
有seccess,漏洞利用成功
exit退出容器 down掉docker,复现结束