Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=cls.getDeclaredConstructor(Class.class,map1);
@H_403_57@
反射创建了一个AnnotationInvocationHandler对象,传入Override.class和lazyMap的对象,并使用AnnotationInvocationHandler作为调用处理器,为lazyMap做一个动态代理。关于这里为什么要传入一个Override.class的问题,其实因为AnnotationInvocationHandler本来就是一个处理注解的类,构造方法的第?个参数是?个Annotation类类型参数,第二个是map类型参数(所有的注解类型都继承自这个Annotation接口)。在这里面不管传入的是Retention.class还是Override.class都是可行的。
这的lazyMap作为被代理的对象后,调用任意的方法都会去执行调用处理器的invoke方法。AnnotationInvocationHandler实现了InvocationHandler ,可以被当作调用处理器传入。而我们在这时候调用lazyMap的任意方法的话,就会执行一次AnnotationInvocationHandler中的invoke方法。而在AnnotationInvocationHandler的invoke方法中就会调用get方法。

在调用get方法后又回到了前面说到的地方,这里就会去调用transform方法去完成后面的命令执行。这里先不细说。

在分析完POC代码后其实并没有去看到一个完整的调用链,这里有必要去调试一遍。
0x03 CC3链调试
先在AnnotationInvocationHandler的readobject方法中去打个断点进行调试分析

在这里可以看到这里的this.memberValues的值为被代理的lazyMap的对象,调用了lazyMap的entrySet方法。那么这时候被代理对象的调用处理器的invoke方法会执行。前面说过使用的AnnotationInvocationHandler作为调用处理器,这里调用的就是AnnotationInvocationHandler的invoke方法,跟进一下invoke方法。

invoke方法在内部调用了lazyMap的get方法,再来跟进一下get方法
@H_705_301@
到这里其实就能看到了 this.factory.transform(key);,调用了transform方法,在这里的this.factory为ChainedTransformer的实例化对象。再来跟进一下transform方法就能看到ChainedTransformer的transform内部的调用结构。

在POC构造的时候为ChainedTransformer这个对象传入了一个数组,数组的第一值为ConstantTransformer实例化对象,第二个为InstantiateTransformer实例化对象。
所以在这里第一次遍历this.iTransformers[i]的值为ConstantTransformer。ConstantTransformer的transform会直接返回传入的对象。在POC代码构造的时候,传入的是TrAXFilter对象,所以在这里会直接进行返回TrAXFilter,并且会作为第二次遍历的传参值。

而在第二次遍历的时候,this.iTransformers[i]的值为InstantiateTransformer的实例化对象。所以调用的是InstantiateTransformer的transform方法并且传入了TrAXFilter对象。跟进一下InstantiateTransformer的transform方法。

这里其实是比较有意思的,刚刚传入的是TrAXFilter对象,所以这里的input为TrAXFilter,this.iParamTypes为Templates,this.iArgs为构造好的恶意TemplatesImpl实例化对象。(这里之所以说他是恶意的TemplatesImpl对象是因为在前面使用反射将他的_bytecodes设置成了一个使用javassist动态创建的恶意类的字节码)
该transform方法中使用getConstructor方法获取TrAXFilter参数为Templates的构造方法。

使用该构造方法创建一个对象,并且传入恶意的TemplatesImpl实例化对象。在该构造方法当中会调用TemplatesImpl的newTransformer方法。跟进一下newTransformer方法。

newTransformer方法内部调用了getTransletInstance方法再跟进一下。

这里可以看到先是判断了_name的值是否为空,为空的话就会执行返回null,不向下执行。这也是前面为什么使用反射获取并且修改_name值的原因。
下面一步是判断_class是否为空,显然我们这里的_class值是null,这时候就会调用defineTransletClasses方法,跟进一下。

下面标注出来这段是_bytecodes对_class进行赋值,这里的_bytecodes的值是使用javassist动态创建的恶意类的字节码 执行完后,来到下一步。


这里会对该字节码进行调用newInstance方法实例化一个对象,然后就可以看到命令执行成功。

关于这个为什么调用newInstance实例化一个对象,命令就直接执行成功的问题,其实我的在CC2链分析里面也说到过,主要还是看使用javassist动态创建一个类的时候,他是怎么去构造的。
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections22222222222");
payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");");
payload.writeFile("./");
@H_403_57@
先将该类写出来到文件中,然后再去查看。

看到这个其实就一目了然了,使用setBody设置主体的时候,代码其实是插入在静态代码块中的。静态代码块的代码在实例化对象的时候就会进行执行。
调用链
AnnotationInvocationHandler.readobject->(proxy)lazyMap.entrySet
->AnnotationInvocationHandler.invoke->lazyMap.get
->ChainedTransformer.transform->ConstantTransformer.transform
->InstantiateTransformer.transform->TrAXFilter(构造方法)
->TemplatesImpl.newTransformer->TemplatesImpl.getTransletInstance
->TemplatesImpl.defineTransletClasses
->(动态创建的类)cc2.newInstance()->Runtime.exec()
@H_403_57@
0x04 结尾
其实在调试CC3这条利用链的时候,会发现前半部分使用的是CC2利用链的POC代码,而后半部分则是CC1的利用链代码。调试过这两条利用链的话,调试CC3这条利用链会比较简单易懂。
在写这篇文的时候,第一次刚码完字,电脑就蓝屏了。重新打开文件的时候,文章的文件也清空了。只能重写一遍,但是重写完后,发现虽然字数也差不多,但是感觉细节点的地方还是少了东西,但是又不知道具体在哪些地方少了。
(编辑:北几岛)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!