cc1调用链
1. 配置环境:
cc1调用链需要环境如下:
- jdk1.8.0_65
- commons-collections 版本号为
3.2.1 - jdk1.8_65源码
- 下载
jdk 8u65Oracle JDK 8u65 全平台安装包下载 - 码霸霸 (lupf.cn) - 导入坐标如下
1 | |
- 解压jdk1.8.0_65下是src文件,将jdk1.8.0_65源码中的
src/share/clasess/sun添加到你配置的1.8.0_65中的src目录中。
教程:
Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili
2. 思路:

从调用危险方法到调用顶层readObject实现反序列化
3. 探索:
3.1 简单找个危险函数:
- 突破口 transform方法:
找到一个接口,查看他的实现类

找到一个InvokerTranformer类的transform方法,接收参数是可控的,即获取了当前传入obejct类,然后调用反射去调用成员变量的函数名和函数参数,找到他的构造函数

第一个参数是方法名,第二个参数是参数类型,第三个参数是参数值。我们通过构造函数然后去调用transform方法即可调用我们传入类的方法了。
我们任意调用的方法是这样的Runtime.getRuntime().exec("calc");这里使用InvokerTransformer就是这样:
1 | |
- 解决Runtime不能反序列化
找到突破口之后,我们要想readObject方法是反序列化才会使用,但是Runtime没有实现SSerializable方法,因此不能实现反序列化。
如何解决?Class类都是可序列化的,因此我们序列化Runtime.class。
查看源码发现,Runtime类的构造方法都是私有的,只有一个静态方法getRuntime返回currentRuntime,用到的是单例模式。因此只能反射调用getRuntime。
1 | |
- 首先获取Runtime.class这个class对象
- 然后调用
getRuntime静态方法获取当前的runtimeClassMethod方法。 - 然后使用
runtimeClassMethod方法类去invoke,即调用getRuntime方法,invoke方法第一个参数是类的实例对象,第二个参数是这个方法执行的参数。因为需要Runtime类才能调用这个私有方法,因此第一个参数是我们上面获取到的对象。
3.2 TransformedMap类:
上面我们查找到了调用TransformedMap类中的checkSetValue方法,我们接着网上找,发现AbstractInputCheckedMapDecorator类中的内部类MapEntry的setValue方法调用了他,只要我们执行map的setValue将我们上面的Object Input传入进去就可以执行了,如下:

发现是AbstractInputCheckedMapDecorator中的MapEntry的setValue方法。

编写构造链如图所示:
1 | |
继续网上查询,查找谁调用了setValue()方法,发现存在许多方法,我们的目的是查找readObejct方法,在readObejct方法中调用setValue()方法既可以实现放序列化,查找到sun.reflect.annotation包中的AnnotationInnvocationHandler的readObejct方法调用了AbstractInputCheckedMapDecorator中的setVale()方法。

至此调用链就清楚了:

readObjetc中的判断如下:

- 将我们的map的key设置为一个注解中的成员变量,如
Target注解存在一个成员变量value。

然后创建一个代理对象,去调用了我们熟悉的AbstractInputCheckedMapDecorator的setValue方法。

因此思路在于如何修改这个parent对象的属性,可以没有办法。查找其他transform发现ConstantTransformer中transform不管输入什么都是调用他的成员变量,因此可以只要我们在创建的时候将Runtime.class赋值给他的成员变量,然后去调用transform方法就会返回Runtime.class
3.3 完整exp:
1 | |
4. 疑惑:
4.1 疑惑点一:为什么要加上new ConstantTransformer(Runtime.class)?
开始我们知道使用chainedTransformer中的transfomer方法可以方便我们去调用,我们传入多个对象就会不断循环去调用这些对象的transfomer方法。
如图:

我们只要将我们需要传入的Runtime.class就可以实现我们的链路了。
