607 字
3 分钟
Java serialize
2026-06-03

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 原生链

Java serialize
https://fuwari.vercel.app/posts/post-20-unserialize/1/
作者
wele
发布于
2026-06-03
许可协议
CC BY-NC-SA 4.0