CC1链(TransformedMap版)
红烧花园宝宝:https://lxu2n.github.io/posts/9e8a5add/
白日梦组长视频:https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1387.homepage.video_card.click
环境搭建
新建一个Java项目,使用JDK8u65、Maven3.6.3


在项目根目录中的pom.xml中添加对CC1链的依赖包
1 | |

然后打开maven下载源代码

成功之后可以在外部库中看到导入的依赖包

可以importCC包验证一下,没有报错就是成功了
1 | |

然后要修改 sun 包,将.class文件转化为.java文件
先下载openJDK8u65,然后放一边
然后找到之前下载的jdk8u65,将其中的src.zip解压缩

在下载的openJDK8u65中找到sun包复制到src目录,sun包的路径是:jdk-7fcf35286d52\jdk-7fcf35286d52\src\share\classes


打开项目结构中的SDK,在源路径中将src导入进去

可以跑个程序验证一下,没报错就是成功
1 | |

Common-Collections介绍
大佬文章:闪烁之狐
简单来说,Common-Collections 这个项目开发出来是为了给 Java 标准的 Collections API 提供了相当好的补充,在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充
包结构
org.apache.commons.collections– CommonsCollections自定义的一组公用的接口和工具类org.apache.commons.collections.bag– 实现Bag接口的一组类org.apache.commons.collections.bidimap– 实现BidiMap系列接口的一组类org.apache.commons.collections.buffer– 实现Buffer接口的一组类org.apache.commons.collections.collection–实现java.util.Collection接口的一组类org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类org.apache.commons.collections.functors–Commons Collections自定义的一组功能类org.apache.commons.collections.iterators– 实现java.util.Iterator接口的一组类org.apache.commons.collections.keyvalue– 实现集合和键/值映射相关的一组类org.apache.commons.collections.list– 实现java.util.List接口的一组类org.apache.commons.collections.map– 实现Map系列接口的一组类org.apache.commons.collections.set– 实现Set系列接口的一组类
攻击链分析
反序列化的攻击思路
在入口类需要一个readObject方法,在结尾需要一个能够命令执行的方法,中间通过链子引导过去,从尾部出发去寻找头
先找到一个可以利用的类(调用可以执行命令的方法A),然后后再去找调用了这个方法A的方法B,然后继续找调用了方法B的方法C,就这样一直找,直到找到了readObject方法,对其重写

寻找实现类
因为Runtime类是不能序列化的,所以要使用反射才能调用类中的getRuntime方法、exec方法

看Transformer接口,点击选中Transformer,快捷键ctrl+alt+B查看实现接口的类,可以发现:ChainedTransformer、ConstantTransformer、InvokerTransformer这三个类

InvokerTransformer类
刚好在InvokerTransformer类中存在一个反射调用任意类的transform方法,可以作为链子终点调用Runtime类中的方法

ChainedTransformer类
在ChainedTransformer类中也可以找到一个transform方法,有个数组iTransformers存储对象,可以依次调用每个对象的transform方法,将前一个的输出作为后一个的输入

ConstantTransformer类
在ConstantTransformer类中的transform方法会忽略输入的参数,始终返回预设的常量iConstant

可以通过构造函数设置iConstant的值,使transform方法返回指定的对象

攻击链构造
第一步
通过刚才分析已经明确了要使用InvokerTransformer的transform方法的反射机制调用Runtime类的方法来构造命令执行
回顾一下如何构造普通反射:
1 | |

先看看InvokerTransformer的构造函数:

有三个参数:
- **
methodName:**要调用的目标方法,这里可以传入getDeclaredMethod从而可以获得Runtime类的任意方法 - **
paramTypes:**目标方法的参数类型数组,这里就是getDeclaredMethod的参数类型数组- 查看
Class类中的getDeclaredMethod方法: 
- 所以这里填入
new Class[]{String.class,Class[].class}
- 查看
- **
args:**实际传给目标方法的参数,这里是要将getRuntime作为参数传给getDeclaredMethod方法,按照参数类型可以写为new Object[]{"getRuntime",null}
所以获取Runtime的实例代码:
1 | |
然后是调用执行方法:
查看Method方法的invoke方法

调用Method的invoke方法
1 | |
最后就是调用Runtime的exec方法执行命令
1 | |
完整的使用transform的代码:
1 | |

然后ChainedTransformer类的transform的方法就可以帮我们进行一个遍历,所以就可以写成:
1 | |

第二步
在第一步中是我自己直接调用的ChainedTransformer中的transform方法,所以现在要开始向上找
右击transform方法,点击查找用法

然后可以找到checkSetValue方法,在调用这个方法后可以调用valueTransformer的transform方法,如果令valueTransformer为chainedtransformer就可以往下衔接到第一步的chainedtransformer.transform(c);这个调用,就是valueTransformer.transform(c);

但是TransformedMap这个类的构造函数是protected

就要去找哪里可以实例化这个对象,可以找到decorate方法可以return一个实例化对象

所以可以将decorate方法返回的实例化对象传给checkSetValue方法
这里要传一个map,就先实例化一个
1 | |
这里就可以直接将valueTransformer参数部分传入chainedtransformer
第三步
继续向上找哪里调用了checkSetValue方法,可以发现在AbstractInputCheckedMapDecorator类中的MapEntry类中的setvalue方法中调用了

这里就可以使parent为TransformedMap,就可以调用到TransformedMap的checkSetValue
这里的MapEntry类是AbstractInputCheckedMapDecorator类的一个副类,然后要去找哪里有实例化的MapEntry,可以发现要使用entrySet方法

这里的this就是parent,意思是谁调用了entryset,this就是谁
然后看其所在的类是一个抽象类

这就要看它的子类了,使用快捷键ctrl+alt+B,可以看到有TransformedMap,这就很nice了,就可以在TransformedMap调用entryset,然后this就是TransformedMap,所以parent也就是TransformedMap了,可以衔接上了

这里可以写个demo来尝试触发一下,可以遍历decorate这个TransformedMap中的所有键值对条目,用entry.setValue(c)触发Transformer链执行
1 | |
用for循环遍历decorate.entrySet(),这里decorate是一个经过TransformedMap.decorate()包装的Map对象,entrySet方法会返回包含有所有键值对的Set集合,这里的map中只有一个键值对,就是map.put("key","value");,所以decorate也就包含这一个键值对,在遍历的时候就会获取到这个唯一的Map.Entry对象,通过entry.setValue(c)触发ChainedTransformer的执行

第四步
继续找谁调用了setValue,这里有个小问题卡了挺久的,就是之前安装的JDK包有点问题,少了点内容,导致在AnnotationInvocationHandler中一直没有找到想要的setValue,看文章对比一看发现少了点代码

我的缺少的:

正常的:

然后又重新去网上找了个sun包:https://github.com/frohoff/jdk8u-jdk/tree/master/src/share/classes/sun

重新配好环境后就可以看到在AnnotationInvocationHandler中调用了setValue方法,并且还是在readObject方法中的

这里有个for循环可以进行一个遍历,就像前面的demo一样,这里for循环就传入decorate即可
但是有两个问题
首先是在for循环中有两个if条件要满足的

其次,在之前的demo中,这里的setValue是要传Runtime的对象,但是在实际中的是AnnotationTypeMismatchExceptionProxy这个对象


这俩问题先放着,在后面解决,先去看这个类的构造函数

这里接收两个对象,一个是Class类的type参数,一个是Map类的memberValues参数,这里第二个参数就可以将构造好的decorate放进去,然后看第一个参数,是继承了Annotation的泛型,Annotation是个注解,注解就像是Override这样的

这里看Target这个注解,这里就有个值是value(不是函数)

然后开始尝试实例化这个类,这时注意到这个类不是public的,这里就要利用反射来获取
1 | |
然后回头去看前面遗留的两个问题,先下断点调试一遍,在开始readObject下一个,然后在第一个if下一个,看看能不能进入if里

开始这里的type就是传入注解Override


然后调试到下面这里时就会获取到Override的成员变量

不过之前看到了,实际上Override是没有成员变量的

然后memberValue就是键值对,String name = memberValue.getKey();就是获取键值对的key,然后Class<?> memberType = memberTypes.get(name);就是会在memberTypes中查找这个key,最后发现查找到的是空的,memberType就是null了,所以就进不去第一个if里,也就不能达到想要的setValue方法
这里就需要改一下,从上面调试可以发现,会先获取传入的注解的成员方法,然后会在memberTypes查找这个成员方法,所以首先是要找一个有成员方法的Class注解,之前看到了Target是有一个成员方法value的

所以把Override改成Target
1 | |
再重新调试一次

然后就会发现这里获取到的键值对的键是key,因为map.put("key","value");传入的就是key,而在Target注解中是没有key的,所以此时memberType还是null
所以map.put("key","value");这里也要改一下,其中的key要改成成员方法的名字value,这样才能查找到
1 | |
重新调试

可以看到此时获取到的键值对的键是value,memberType不再是null了,可以成功通过第一个if判断,第二个if是判断能不能强转,这里是肯定不能强转的,所以第二个if判断也可以通过
然后就到了想要的setValue方法了,F7步入跟进去


这里的valueTransformer.transform(value);实际上就是chainedtransformer.transform(Runtime.class);这个形式,这里就要使valueTransformer.transform(value);的value为Runtime.class,但是在调试中可以看到此时的value是下面这个代理

就是AnnotationTypeMismatchExceptionProxy

回想在攻击链分析时还看到一个类:ConstantTransformer,在这个类中的transform方法不管接收什么输入,都会返回预设的常量的值
所以在最后只要调用ConstantTransformer类的transform方法,就可以把AnnotationTypeMismatchExceptionProxy改成Runtime.class

调试一下

到这里后,接下来就会调用ConstantTransformer的transform方法,这里传入的是一个对象,就是AnnotationTypeMismatchExceptionProxy

然后就可以看到强制返回的常量iConstant是Runtime类

最后对象就变成了Runtime类,就很舒服了

最后加上序列化和反序列化的部分
1 | |
至此完整的攻击链就构造好了
完整代码
1 | |