序言
环境配置还是与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反序列化以及开始有一丝丝“灵性”了。