0x01

序列化/反序列化是为了能够保存对象状态,并且可以走网络中传输等。接受方通过这些数据来重建对象,依托于 Java 反射机制,理论上可以实例化绝大多数的类并控制绝大多数类的成员函数,这里可以进一步走到反射调用危险方法,字节码加载等 sink 点。

0x02

我们在反序列化的时候重建一个 Java 类,构造出具备攻击性例如rce、文件读取、dns 外链、内存马植入的类,通过调用该类的成员函数,在合适的地方填入相应的参数来实现攻击目的。

走到最后要么是动态加载字节码,urlClassloader loadClass defineClass 要么是 method.invoke() 这样的危险方法调用

0x03 readObject

接下来讲解一些常见的入口点,例如

0x01 hashCode()

image-20260603211804597

我们可以通过 HashMap 对象反序列化时为了将对象放回相应的位置,会对其进行 hashCode() 计算,来构造一个 map 对象来实现。

package com.wsh.unserialize;

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, InstantiationException {
        Person p1 = new Person("aaa",13);
//        p1.hashCode();
        HashMap<Object, Object> map = new HashMap<>();

        Class<?> nodeClass = Class.forName("java.util.HashMap$Node");
        Object[] table = (Object[]) Array.newInstance(nodeClass, 16);

        Constructor<?> nodeCons = nodeClass.getDeclaredConstructor(int.class, Object.class, Object.class, nodeClass);
        nodeCons.setAccessible(true);
        Object node = nodeCons.newInstance(1, p1, p1, null);
        table[0] = node;

        Field tableField = HashMap.class.getDeclaredField("table");
        tableField.setAccessible(true);
        tableField.set(map, table);

        Field sizeField = HashMap.class.getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.set(map, 1);

        FileOutputStream fos = new FileOutputStream("ser.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(map);
        oos.close();

        FileInputStream fis = new FileInputStream("ser.bin");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
    }
}

调用栈如下

image-20260603211822107

0x02 toString()

通过 BadAttributeValueExpException 类的反序列化来触发

image-20260603212351282

BadAttributeValueExpException bavee = new BadAttributeValueExpException()
Method_u.setFieldValue(badAttributeValueExpException,"val",p1);

反序列化时会执行 val.toString()

image-20260603212406309

0x03 equals()

https://wele.vercel.app/posts/post19-fastjson2/1/ ,HashMap 的 反序列化的过程中如果 HashCode 值相同,那么会调用 equals() ,可以使用 map yy zZ 的技巧或者 hsts 类来满足 HashCode 值相同

0x04 compareTo()

我们通常选用 PriorityQueue 来包装恶意类,通过反射赋值将恶意类写入优先队列中,反序列化 PriorityQueue 对象时会触发到 compareTo() 方法。

PriorityQueue 类的 readObject 方法调用 heapify ,反序列化填充完值之后需要还原合法堆结构或者说成小顶堆,保证序列化/反序列化之后结构正确。

image-20260603212426180

因此会涉及到元素之间的比较,进而触发 compareTo() 方法

image-20260603212437842

0x04 setter 方法

todo

fastjson jackson

0x05 getter 方法

Fastjson rome

0x06 动态代理的应用

Fastjson2 fastjson 1.2.83

jdk8u 原生链