Java反序列化CC1链子之国内国外版本总结
空城亡灵 WEB安全 584浏览 · 2025-04-10 08:29

环境配置

漏洞存在在JDK8U71以下的JDK版本中



commons-collections-3.2.1源码



在pom.xml中添加

如图所示



JDK版本

使用的是JDK8u65



下载地址:

如图所示



CC1(国内版)

链子源头

按 Clrt + alt + B ,如图点击来到这里



在commons-collections-3.2.1源码中的 InvokerTransformer类继承了Transformer接口,我们进去来到这里看



可以看到这是一个很经典的反射调用执行方法,通过InvokerTransformer有参构造,然后调用Transform方法,从而可以构造任意命令执行。

代码演示

原理

代码构造:一一对应



最后有参构造的InvokerTransformer方法整理好之后再调用transform方法



因为input.getClass()的意思获取类,所以我们只需要在 transform方法中传递一个能够调用系统命令执行的类就可以了

运行之后可以看见成功弹出计算器



初步构造链

从而得出构造链:

向上寻找链子

我们只需要寻找谁调用transform方法,就可以知道下一条链子了



最终在这里可以看见 TransformedMap中的checkSetValue方法调用了

但是由于checkSetValue方法是收保护的类,所以我们不能直接调用这个方法



所以直接谁寻找调用checkSetValue方法即可



在这里可以看到调用方法,双击进去



来到最顶部可以发现AbstractInputCheckedMapDecorator是抽象类



返回关键方法查看可以setValue是公共方法,所以在这里我们可以进行利用。但是由于setValue哈需要parent参数,所以会用到上面那个方法的键值



链子构造

原理

由于第一次 transform 寻找调用的方法在 TransformedMap类中的checkSetValue方法



可以看见调用这个方法还需要 valueTransformer 参数



恰好 TransformedMap 类中的 decorate方法 刚好就构造了valueTransformercabs参数,所以我们需要调用TransformedMap类的decorate方法



从而刚好满足这三种条件,hashMap是为了满足decorateMap方法中的第一个参数。



所以当调用上一层链子调用checkSetValue方法的时候相当于

那是因为 TransformedMap.decorate的构造参数是 valueTransformer,我们使用了invokerTransformer放到参数中去,所以当 checkSetValue方法调用的时候就会变成 invokerTransformer.transform(value)

可控的value

查看调用checkSetValue方法,可以看见这里有一个 setValue,相当于设置value参数,我们可以将自己精心构造的自定义value放入进去就相当于,第一个链子的命令执行



可以看见MapEntry方法中有 parent参数



所以我们可以通过for函数进行设置键值

最后执行构造链代码,可以看见命令执行成功,弹出计算器



构造链拼接

最终链

继续按原来的套路寻找向上调用的方法



可以看见这个方法在readObject中,readObject有一个好处就是当执行反序列化的时候自动执行readObject方法



构造链

所以我们现在只需要构造AnnotationInvocationHandler类即可,最终执行序列化与反序列化,就能够任意命令执行。



原理

至于为什么要加载Target.class

可以看见readObject中有两个if判断,他会判断成员中是否有内容,可以看见Target类中有value



由于它是键名检测,所以前面的 hashMap中就构造好了 "value"



CC1最终exp代码

原理

从而构造原理我们写出以下EXP,但是可以发现少了任意命令执行的类,所以不可以执行成功

反射与Transformer[]

按Ctrl+Alt+B来到 ChainedTransformer方法中



我们可以看见这个ChainedTransformer方法可以整合 transformers,成为一个数组。

然后transformer会将数组中的 transformers全部会变成 iTransformers[i].transform(object)

这就是我们前面很熟悉的命令执行了



所以我们可以整理这个语句

在来到 ConstantTransformer构造方法中



可以看见这个也是很熟悉的


经过代码对比,很明显知道

所以我们可以构造代码为以下:



最终exp

从而构造最终的国内版本的CC1链EXP代码:

运行代码可以看见成功弹出计算器



CC1(LazyMap国外版)

可以看见国内版对比国外版从这里开始就不一样了



地址:

寻找LazyMap.get()

按原来的方法继续构造代码

可以看见能够弹出计算器



查看调用 transform方法的来源



最终我们在这里找到了



可以看见这个get方法执行了transfrom方法,但是我们需要factory参数



构造链代码

我们可以看到 LazyMap类中的 decorate构造方法有 factory参数



我们可以利用这个方法从而构造代码:

构造代码与原来的代码进行拼接:

运行代码可以看见计算器成功执行了



动态代理&AnnotationInvocationHandler类

AnnotationInvocationHandler类

我们来到AnnotationInvocationHandler类中invoke方法,可以看见这里调用了get方法,其中memberValues参数是可控的



Ctrl+左键点击 memberValues 可以看见来到 AnnotationInvocationHandler构造类的方法中



所以我们可以构造自己想要的 Map.class 类型放入 memberValues参数类型中,然后就可以执行想要的get方法了。

动态代理

在java中如果继承了 InvocationHandler类,就可以实现动态代理。我们可以利用动态代理来实现get方法的调用。

从而构造代码:

构造方法参数类型



就是参数对应就可以了



最后构造方法加载动态代理类

最终exp代码

从而构造最终exp代码

运行之后可以看见计算器成功弹出



总结

CC1的国内外半的前半链子一样,就后面不一样。一个是方法调用任意命令执行,一个是动态代理类调用方法任意命令执行。

0 条评论
某人
表情
可输入 255