目录
[4、Apache Commons Collections漏洞](#4、Apache Commons Collections漏洞)
[(1)、Apache Commons Collections介绍](#(1)、Apache Commons Collections介绍)
[(3)、Apache Commons Collections 漏洞](#(3)、Apache Commons Collections 漏洞)
1、java序列化和反序列化的用途
我们在写java代码的时候,一般是通过类 + 对象来编程,但是这样的方式只能保存在内存上面,一旦服务器关闭,那么保存在内存上面的数据就会消失,如果想在磁盘上保存或者网络传输,我们需要将对象进行序列化转换成字符串二进制流,然后我们再将字符串二进制流进行反序列化就能还原成对象

类似于php序列化和反序列化

2、JSON概念
JSON:JavaScript Object Notation 【JavaScript 对象表⽰法】
JSON是⼀种轻量级的数据交互格式. 它基于 ECMAScript (欧洲计算机协会制定的js规范)的⼀个⼦集, 采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。简单来说:JSON就是⼀种数据格式, 有⾃⼰的格式和语法, 使⽤⽂本表⽰⼀个对象或数组的信息, 因此 JSON本质是字符串. 主要负责在不同的语⾔中数据传递和交换.
objectivec
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true
}
我们定义好了一个person类,里面有他的构造方法以及其他get和set方法
java
package com.wuya;
import java.io.*;
public class Person implements Serializable {
public String name;
public int age;
public Person(String s, int i) {
this.name = s;
this.age = i;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
我们通过下面的Person person去new 了一个person对象,然后再通过JSONObject 去转换成json对象,此时可以打印json,下面就是将json格式转换成对象
java
public static void test1(){
Person person = new Person("fast",999);
JSONObject json = (JSONObject) JSON.toJSON(person);
System.out.println(json);
System.out.println(json.get("name"));
System.out.println(json.get("age"));
}
public static void test2(){
String s ="{\"name\":\"fast\",\"age\":999}";
JSONObject json1 = JSON.parseObject(s);
System.out.println(json1);
Person real = JSON.parseObject(s, Person.class);
System.out.println(real);
System.out.println(real.name);
System.out.println(real.age);
}

3、反序列化漏洞概念
我们注意上面定义的Person类,它继承了Serializable接口,如果要将一个对象进行序列化,必须继承这个接口
java
public class TestToFile {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person obj= new Person("wuya", 666);
String filePath = "D:/wuya.xxx";
// 序列化
ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(filePath));
outStream.writeObject(obj);
// 反序列化
ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(filePath));
// readObject 方法
Person readObject = (Person)inStream.readObject();
System.out.println("反序列化后:name="+readObject.name +",age="+readObject.age);
}
}
通过Person来new一个对象obj,通过ObjectOutputStream new出来的对象中writeObject方法,将obj对象序列化成二进制流,其中filePath是存放二进制流的文件路径,通过010editor打开可以看到,他是有特定规范格式,在java官网上面可以看到

随后,通过ObjectInputStream new出来的对象中readObject方法将二进制流反序列化成对象,readObject反序列化成功以后,这里可以开始调用对象方法,前面Person类中的getName都可以调用
注意此时我们定义了一个Unsafeclass类,继承了Serializable接口,我们去重写readObject方法,他不会调用默认readObject方法,而是执行我下面这个,Runtime.getRuntime是获得java环境,执行计数器
java
public class UnsafeClass implements Serializable {
public String name;
//重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
//执行默认的readObject()方法
in.defaultReadObject();
//执行命令
Runtime.getRuntime().exec("calc.exe");
}
}
接下来我们使用上面定义的UnsafeClass
java
public class UnsafeTest {
public static void main(String args[]) throws Exception {
UnsafeClass Unsafe = new UnsafeClass();
Unsafe.name = "hacked by wuya";
// 序列化
FileOutputStream fos = new FileOutputStream("D:/wuya.yyy");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(Unsafe);
os.close();
//从 反序列化
FileInputStream fis = new FileInputStream("D:/wuya.yyy");
ObjectInputStream ois = new ObjectInputStream(fis);
UnsafeClass objectFromDisk = (UnsafeClass) ois.readObject();
System.out.println(objectFromDisk.name);
ois.close();
}
}
通过序列化文件流之后,在反序列化成对象,调用重写的readObject方法,并且重写的readObject方法是可以控制的,此时就会执行恶意代码,这就是反序列化漏洞,此时这个例子是比较简单,这里只是给个思路,要想达成反序列化漏洞还是比较难的,首先必须别人的代码中有一个类似上面定义好的类,利用自定义的readObject方法执行,寻找重写readObject方法的类

4、Apache Commons Collections漏洞
(1)、Apache Commons Collections介绍
在java的jdk里面集成了各种容器,List、Map、Set等,而Apache Commons Collections是一个开源了对jdk容器再次封装集合了各种新的容器


(2)、Java反射机制
java通过(编译器)javac对源码进行编译成字节码文件,生成.Class文件,然后java虚拟机就会将Class文件翻译成机器指令吗,在操作系统上执行,所以不同平台编译的class文件都是一样的,交给jvm解析,对象的构造是通过new关键字创建出来,如果我们去跑一个程序,只有确定的情况下,jvm才会去执行,但是灵活性就稍微弱一些在程序运行的时候动态创建一个类的实例, 调用实例的方法和访问它的属性
比如下面我们静态执行下面代码
java
Runtime.getRuntime().exec("calc");
通过反射
java
public static void test2(){
try {
//初始化Runtime类
Class clazz = Class.forName("java.lang.Runtime");
// 调用Runtime类中的getRuntime方法得到Runtime类的对象
Object rt = clazz.getMethod("getRuntime").invoke(clazz);
//再次使用invoke调用Runtime类中的方法时,传递我们获得的对象,这样就可以调用
clazz.getMethod("exec",String.class).invoke(rt,"calc");
}catch (Exception e){
e.printStackTrace();
}
}
(3)、Apache Commons Collections 漏洞
l InvokeTransformer 利用Java反射机制来创建类实例
ChainedTransformer 实现了Transformer链式调用,我们只需要传入一个Transformer数组
ChainedTransformer就可以实现依次的去调用每一个Transformer的
transform()方法
l ConstantTransformer transform()返回构造函数的对象
l TransformedMap

首先我们来到InvokerTransformer.java里面的transform方法
java
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
(4)Fastjson反序列化漏洞
说到这里我们看看Alibaba开源系列

其中我们需要了解Fastjson使用方法速度快 使用广泛 测试完备 使用简单 功能完备的特点

我们试着运行下面代码,序列化的时候,会调用成员变量的get方法,私 有成员变量不会被序列化。 反序列化的时候,会调用成员变量的set方法,publibc修饰的成员全部自动赋值。
java
package com.wuya.test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class JsonTest {
public static void main(String[] args) {
// 从1.2.25开始,autotype默认关闭
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 序列化字符
String serializedStr = "{\"@type\":\"com.wuya.test.User\",\"name\":\"wuya\",\"age\":66, \"flag\": true,\"sex\":\"boy\",\"address\":\"china\"}";//
System.out.println("serializedStr=" + serializedStr);
System.out.println("-----------------------------------------------\n\n");
//通过parse方法进行反序列化,返回的是一个JSONObject]
System.out.println("JSON.parse(serializedStr):");
Object obj1 =JSON.parse(serializedStr);
System.out.println("parse反序列化对象名称:" + obj1.getClass().getName());
System.out.println("parse反序列化:" + obj1);
System.out.println("-----------------------------------------------\n");
// 通过parseObject,不指定类,返回的是一个JSONObject
System.out.println("JSON.parseObject(serializedStr):");
Object obj2 = JSON.parseObject(serializedStr);
System.out.println("parseObject反序列化对象名称:" + obj2.getClass().getName());
System.out.println("parseObject反序列化:" + obj2);
System.out.println("-----------------------------------------------\n");
// 通过parseObject,指定为object.class
System.out.println("JSON.parseObject(serializedStr, Object.class):");
Object obj3 = JSON.parseObject(serializedStr, Object.class);
System.out.println("parseObject反序列化对象名称:" + obj3.getClass().getName());
System.out.println("parseObject反序列化:" + obj3);
System.out.println("-----------------------------------------------\n");
// 通过parseObject,指定为User.class
System.out.println("JSON.parseObject(serializedStr, User.class):");
Object obj4 = JSON.parseObject(serializedStr, User.class);
System.out.println("parseObject反序列化对象名称:" + obj4.getClass().getName());
System.out.println("parseObject反序列化:" + obj4);
System.out.println("-----------------------------------------------\n");
}
}

通过对User进行序列化得到{name= 'wuya' , age=66, flag=true, sex= 'boy' , address= 'null'},另外Fastjson反序列化的时候需要指定类名,如上面User.class,子类中包含接口或抽象类的时候,类型丢失,或者下面加上@type,给类的路径{"@type":"com.wuya.test.User" , "age":33, "flag":false, "name":"wuya"}既然你这里可以给定一个类名关键字,那我是不是可以任意构造
我们找到了如下可以利用的类,jdbc是我们java连接数据库的
com.sun.rowset.JdbcRowSetImpl

(4)关于JdbcRowSetImpl链利用的分析
从上面我们学习了绕过黑白名单的学习,接下来看 JdbcRowSetImpl 利用链的原理。
根据 FastJson 反序列化漏洞原理, FastJson 将 JSON 字符串反序列化到指定的 Java 类时,
会调用目标类的 getter 、 setter 等方法。 JdbcRowSetImpl 类的 setAutoCommit() 会调用
connect() 方法, connect() 函数如下:

具体情况是这样的我们去反序列化JdbcRowSetImpl这个类的时候,他就会去调用dataSourceName的set方法,并传入下面恶意地址代码,这个工程就会去下载恶意代码并执行从而攻击就发生了

这里反序列化调用dataSourceName的set方法

同时反序列化也会调用setAutoCommit方法,其中有一个核心方法connect

connect中有一个lookup方法,这个方法会去查找资源,其中去getDataSourceName,而dataSourceName已经被我们赋值成一个恶意链接资源,他就会下载恶意链接的代码,随后就会执行,进行反弹链接

5漏洞挖掘思路
漏洞挖掘
1、找到发送JSON序列化数据的接口
2、判断是否使用fastjon
1)非法格式报错
{"x":"
2)使用dnslog探测
{"x":{"@type":"java.net.Inet4Address" , "val":"xxx.dnslog.cn"}}
Burp 插件
https://github.com/zilong3033/fastjsonScan
6漏洞修复
1、升级JDK
6u211 / 7u201 / 8u191 /11.0.1
2、升级Fastjson到最新版
fastjson.parser.safeMode=true
3、使用安全产品过滤非法内容
4、更换其它序列化工具
Jackson/Gson