potatosafe
- 关注

前言
上一篇说了CC1链的利用方法,这篇紧接着继续说,之前的利用走的是TransformedMap,这篇我们按照yso的CC1链走一遍,其实就是把TransformedMap换成了LazyMap,利用动态代理方式触发。话不多说,我们直接开始。
一、利用链流程
老规矩,画了一张流程图,供大家参考。如图所示,这里还是按照三部分讲解
Transformer接口->制作链式调用恶意方法
LazyMap->套娃触发ChainedTransformer.transform
AnnotationInvocationHandler->动态代理触发invoke
二、Transformer接口
这里内容和CC1链-1一样,看过的师傅可以直接跳转该小节
这里需要说Transformer三个实现类,InvokerTransformer、ConstantTransformer、ChainedTransformer。
我不太想脱离分析流程按照顺序讲解三个类的原理在拼接。我为了更好的梳理思路,我们进行potato链式讲解(自定义)。
我们首先来看一下Transformer接口。
接口没什么特别的,做了个transform方法。我们看一下接口的实现类。这个InvokerTransformer就是我们这条利用链的触发点,我们进入这个类看一看。
1.InvokerTransformer
首先我们跟进transform方法,我们可以看到这个方法将传入的对象反射方法,然后执行方法,这就是个任意方法调用了。
我们看上图可以知道,transform可以进行任意方法调用,我们看一下iMethodName、iParamTypes、iArgs是怎么赋值的,我们跟进构造函数。
OK,参数全部可控,这样我们就可以进行直接写个任意方法执行了。
Runtime类分析
我想了想,还是说一嘴Runtime的问题,师傅们别嫌我啰嗦哈。
首先我们知道我们一般执行命令是通过Runtime.getRuntime().exec("command");执行命令的,而Runtime是一个单例模式,我们是没办法直接创建的,所以我们都是通过getRuntime去获取然后执行exec方法。
所以我们这里想要实现InvokerTranformer,通过getRuntime获取就可以了。
我们知道Runtime是无法序列化的,但是InvokerTransformer可以任意执行方法,我们只需要将反射的反射通过InvokerTransformer的格式去调用就OK了,
2.ChainedTransformer
我们根据上面InvokerTransformer可以看出,这个相当于InvokerTransformer一层一层的调用,也就是链式调用,所以这就引出了我们这里说的Transformer接口的第二个实现类ChainedTransformer,我们跟进去这个类看一看。
我们看到它写了个循环,应该也叫递归吧,他的transform就是一层一层的调用,然后我们在通过Runtime.class触发就可以了。我们接下来看一下它的构造函数。
chainedTransformer.transform(Runtime.class);
这里我们就明白了,我们只需要将需要执行的方法通过链式的形式写到Transformer里面就可以了,其实就是个链式调用。但是我们触发的话还需要传一个Runtime.class,当然你将传入transform(Runtime.classs)的链在放一个里面也行,还有更美观的一个方法,就是Transformer接口的第三个实现类ConstantTransformer。
3.ConstantTransformer
其实用这个实现类的目的就是将Runtime.class返回放到chainedTransformer的链子里面,其实这个不是利用链的关键。我们跟进这个类看一看吧
这个实现类将我们传入的对象通过transform原封不动的返回了,这样我们就可以和InvokerTransformer一起封装到ChainTransformer链里面了。
至此为止我们Transformer接口分析结束,简单总结一下就是通过InvokerTransformer制作runtime类的反射,ConstantTransformer传入runtime.class制作链的触发,chainTransformer将链封装起来。
三、LazyMap
其实到这里之前都是cc1-1链的内容,我们CC1-1调用的是transformMap而CC1-2调用的是LazyMap。我们跟着上面的思路继续走,看看谁调用了transform方法。
定位到LazyMap,我们继续跟进,查看一下get方法。
可以看到factory.transform是我们需要的,我们看一下factory是怎么定义的,跟进构造函数。
上图我们可以得知factory为我们可控,我们看一下谁调用了get,当然这个get其实很难找上面的调用。我们也不纠结那么多,我们直接跟到AnnotationInvocationHandler的invoke方法
四、AnnotationInvocationHandler
跟进Invoke方法,我们可以看到调用了memberValues.get。我们去查看构造方法可以看到我们其实是可以控制type、memberValues,memberValues是我们传入的可控Map。
接下来我们再次查看该类,invoke方法,看到这里我们可以想到~动态代理~InvocationHandler接口
接下来我们制作动态代理Map
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class,lazyMap);
Map Proxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
我们知道动态代理方法在执行时调用h里面的invoke方法去执行,且分析AnnotationInvocationHandler的invoke方法,我们可以知道需要无参方法通过。
这里我们查看readObject,正好有一个无参方法调用。
最后我们正向捋一下思路,annotationInvocationdhdlConstructor反序列化执行readobject到memberValues.entrySet触发动态代理,跳转到AnnotationInvocationHandler.invoke触发memberValues.get,跳转到LazyMap.get触发chaintransform.transform,之后直接往下走就调用恶意方法了。
五、断点调试利用链
可爱小尾巴~~~
我只是个基础很差 技术很菜 脚本小子里面的小菜鸡,文章里面有什么写的不对的地方,望师傅们多加指正,我肯定狂奔加小跑的学。CC1链就算说完了,之后这个篇章可能继续按这个线说CC6也可能按顺序说一下优先队列的CC2。写写思路总结锻炼锻炼自己还是挺好的。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
