序言
环境配置还是与CC1一样:java环境:1.7 , commons-collections: 3.1
如果说CC1是从AnnotationInvocationHandler
的readOject入手,利用Map(proxy).entrySet()
触发TransformedMap->checkSetValue()
,进而调用ChainedTransformer->transform()
,CC2是利用InvokerTransformer.transform()
调用newTransformer
去加载字节码
RCE的话,那么CC3就像是CC1和CC2二者的结合。
前置知识
InstantiateTransformer
org.apache.commons.collections.functors.InstantiateTransformer
其transform方法:
注意这里最终返回的是一个对象的初始化,并且其中参数都可控,看到这个实例化,有没有一丝丝CC2加载字节码的感觉。
TrAXFilter
com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
_transformer = (TransformerImpl) templates.newTransformer();
看到这个newTransformer()
是不是更像CC2了,CC2是用InvokerTransformer
来反射触发TemplatesImpl.newTransformer()
,最后触发_class.newInstance()
实例化加载字节码触发RCE。
而这里的TrAXFilter
可以代替InvokerTransformer
调用newTransformer()
,上面的InstantiateTransformer
再来充当实例化TrAXFilter
的角色。
POC分析
1 | package CC3; |
利用链:
1 | ObjectInputStream.readObject() |
前半段生成字节码文件和设置TemplatesImpl的值与CC2一模一样。可以参考CC2。
但是接下来和CC2有不同的地方,CC2是用ComparatorTransformer
来调用InvokerTransformer.transform()
去触发TemplatesImpl.newTransform()
,进而实例化字节码文件。而根据前置知识的了解可以知道,这里还能用InstantiateTransformer
对象去实例化TrAXFilter
,在里面触发newTransform()
实例化。
所以这里这样构造:
而接下来如何触发ChainedTransformer.transform()
的方法也是CC1中接触过的,那就是Lazymap
在CC1中分析过,可以知道LazyMap.get()
可以触发transform
方法
而接下来readObject()入口依然采用CC1中的AnnotationInvocationHandler
。
看着有点迷,来分析一下。
首先反射创建了一个AnnotationInvocationHandler
对象,实例化的时候传入了Target.class
和lazymap
(传入Target.class的原因cc2中也讲过,因为这是一个处理注解的类,构造方法的第一个参数是Annotation类型参数。)
而下面是一个动态代理,传递三个参数ClassLoader,要代理的接口数组和调用接口时触发的参数(这里就是实例化过后的AnnotationInvocationHandler
)。
当调用代理对象的任意方法时,就会触发代理类的invoke方法。这里就是AnnotationInvocationHandler
的invoke
方法,而在他的invoke()里就会调用到get
方法。
命令执行过程
在readObject()中,由于被代理的lazymap
对象调用了entrySet
方法,所以就调用了AnnotationInvocationHandler
的invoke
方法.
调用LazyMap的get()
触发ChianedTransformer.transform()
这里将第一次返回的TrAXFilter.class
作为InstantiateTransformer
的input输入,iArgs
为包含恶意_bytecodes
的TemplatesImpl
对象,iParamTypes
为对应构造函数类型的Templates.class
。
con.newInstance(iArgs)
实例化TrAXFilter
在TrAXFilter的构造函数中,调用Templates.newTransformer()
接下来的步骤也是与CC2中的一样,从newTransformer()开始,经过处理后将字节码还原成类,并最终实例化触发static中代码块
newInstance()实例化的时候就会执行静态代码块中的内容。
结尾
调过CC1和CC2的利用链之后再来看CC3,明显感觉简单了很多,前半部分用的CC2的代码,包括字节码生成,TemplatesImpl构造,后半部分就是用的CC1中的内容了。
在分析CC3的时候也是在温习CC1和CC2,发现有些东西的理解更加深刻了,对java反序列化以及开始有一丝丝“灵性”了。