XMLDecoder解析流程分析

前言

經過了Weblogic的幾個XMLDecoder相關的CVE(CVE-2017-3506、CVE-2017-10352、CVE-2019-2725),好好看了一下XMLDecoder的分析流程。

本文以jdk7版本的XMLDecoder進行分析,jdk6的XMLDecoder流程都寫在了一個類裏面(com.sun.beans.ObjectHandler)

此處只分析XMLDecoder的解析流程,具體Weblogic的漏洞請看其它幾位師傅寫的Paper。

WebLogic RCE(CVE-2019-2725)漏洞之旅-Badcode

Weblogic CVE-2019-2725 分析報告-廖新喜

不喜歡看代碼的也可以看官方關於XMLDecoder的文檔:
Long Term Persistence of JavaBeans Components: XML Schema

XMLDecoder的幾個關鍵類

XMLDecoder的整體解析過程是基於Java自帶的SAX XML解析進行的。

以下所有類都在com.sun.beans.decoder包中

DocumentHandler

DocumentHandler繼承自DefaultHandler,DefaultHandler是使用SAX進行XML解析的默認Handler,所以Weblogic在對XML對象進行validate的時候也使用了SAX,保證過程的一致性。

DefaultHandler實現了EntityResolver, DTDHandler, ContentHandler, ErrorHandler四個接口。

DocumentHandler主要改寫了ContentHandler中的幾個接口,畢竟主要是針對內容進行解析的,其它的保留默認就好。

ElementHandler及相關繼承類

XMLDecoder對每種支持的標籤都實現了一個繼承與ElementHandler的類,具體可以在DocumentHandler的構造函數中看到:

在這裏插入圖片描述

所以XMLDecoder只能使用如上標籤。

其中繼承關係與函數重寫關係如下(很大,可以看大圖或者自己用idea生成再看):

在這裏插入圖片描述
如上的繼承關係也是object標籤可以用void標籤替代用的原因,後面詳說。

ValueObject及其相關繼承類

ValueObject是一個包裝類接口,包裹了實際解析過程中產生的對象(包括null)

繼承關係:

在這裏插入圖片描述
一般的對像由ValueObjectImpl進行包裹,而null\true\false(非boolean標籤)則直接由自身Handler進行代表,實現相關接口。

XMLDecoder過程中的幾個關鍵函數

DocumentHandler的XML解析相關函數的詳細內容可以參考Java Sax的ContentHandler的文檔

ElementHandler相關函數可以參考ElementHandler的文檔

DocumentHandler創建各個標籤對應的ElementHandler並進行調用。

startElement

處理開始標籤,包括屬性的添加
DocumentHandler:。XML解析處理過程中參數包含命名空間URL、標籤名、完整標籤名、屬性列表。根據完整標籤名創建對應的ElementHandler並添加相關屬性,繼續調用其startElement。

ElementHandler: 除了array標籤以外,都無操作。

endElement

結束標籤處理函數
DocumentHandler: 調用對應ElementHandler的endElement函數,並將當前ElementHandler回溯到上一級的ElementHandler。

ElementHandler: 沒看有重寫的,都是調用抽象類ElementHandler的endElement函數,判斷是否需要向parent寫入參數和是否需要註冊標籤對象ID。

characters

DocumentHandler: 標籤包裹的文本內容處理函數,比如處理<string>java.lang.ProcessBuilder</string>包裹的文本內容就會從這個函數走。函數中最終調用了對應ElementHandler的addCharacter函數。

addCharacter

ElementHandler: ElementHandler裏的addCharacter只接受接種空白字符(空格\n\t\r),其餘的會拋異常,而StringElementHandler中則進行了重寫,會記錄完整的字符串值。

addAttribute

ElementHandler: 添加屬性,每種標籤支持的相應的屬性,出現其餘屬性會報錯。

getContextBean

ElementHandler: 獲取操作對象,比如method標籤在執行方法時,要從獲取上級object/void/new標籤Handler所創建的對象。該方法一般會觸發上一級的getValueObject方法。

getValueObject

ElementHandler: 獲取當前標籤所產生的對象對應的ValueObject實例。具體實現需要看每個ElementHandler類。

isArgument

ElementHandler: 判斷是否爲上一級標籤Handler的參數。

addArgument

ElementHandler: 爲當前級標籤Handler添加參數。

XMLDecoder相關的其它

兩個成員變量,在類的實例化之前,通過對parent的調用進行增加參數。

parent

最外層標籤的ElementHandler的parent爲null,而後依次爲上一級標籤對應的ElementHandler。

owner

ElementHandler: 固定owner爲所屬DocumentHandler對象。

DocumentHandler: owner固定爲所屬XMLDecoder對象。

簡易版解析流程圖

PPT畫的:-D
在這裏插入圖片描述

跟着漏洞來波跟蹤(Weblogic)

來一份簡單的代碼:

public static void main(String[] args) throws FileNotFoundException {
	String filename = "1.xml";
	XMLDecoder XD =new XMLDecoder(new FileInputStream(filename));
	Object o = XD.readObject();
	System.out.println(o);
}

Level1:什麼過濾都沒有

<java>
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="1">
            <void index="0">
                <string>calc</string>
            </void>
        </array>
        <void method="start"/>
  	</object>
</java>

首先看下DocumentHandler的startElement:
在這裏插入圖片描述

  1. 創建對應Handler,設置owner與parent
  2. 爲Handler添加屬性
  3. 調用Handler的startElement

(後面DocumentHandler的部分忽略,直接從ElementHandler開始)
下面從object標籤對應的ObjectElementHandler開始看:
進入obejct標籤,object標籤帶有class屬性,進入:
在這裏插入圖片描述

可以看到判斷的列表裏沒有class標籤,會調用父類(NewElementHandler)的addAttribute方法。
在這裏插入圖片描述
給type賦值爲java.lang.ProcessBuilder對應的Class對象。

中間創建array參數的部分略過,有興趣的同學可以自己跟一下。

進入void標籤,設置好method參數,由於繼承關係,看上面那張addAttribute圖就好。

退出void標籤,進入elementHandler的endElement函數:
在這裏插入圖片描述
由於繼承關係,調用NewElementHandler的getValueObject函數:
在這裏插入圖片描述
繼續進入進入ObjectElementHandler的帶參數getValueObject函數:
在這裏插入圖片描述
此處的getContextBean會調用上一級也就是Object標籤的getValueObject來獲取操作對象。

略過中間步驟,再次進入ObjectElementHandler的getValueObject方法:

最終通過Expression創建了對象:
在這裏插入圖片描述

(可以看出此處的Expression的首個參數是來自於上面getContextBean獲取的Class對象,先記住,後面會用)

再次回到Void標籤對應的getValueObject函數:
最終通過Expression調用了start函數:
在這裏插入圖片描述
如果對繼承關係感覺比較蒙的話,可以看下一節的繼承關係圖。

PS: 雖然ObjectElementHandler繼承自NewElementHandler,但是其重寫了getValueObject函數,兩者是使用不同方法創建類的實例的。
再PS: 其實不加java標籤也能用,但是沒法包含多個對象了。

Level2:只過濾了object標籤

把上面的object標籤替換爲void即可。

VoidElementHandler的繼承關係:

在這裏插入圖片描述
可以看到只改寫了isArgument,而在整個觸發過程中並無影響,所以此處使用void標籤與object標籤完全沒有區別。

Level3:過濾一堆

過濾了object/new/method標籤,void標籤只允許用index,array的class只能用byte,並限制了長度。

CNVD-2018-2725(CVE-2019-2725)最初的poc使用了UnitOfWorkChangeSet這個類,這個類的構造方法如下(從Badcode師傅的Paper裏盜的圖):

在這裏插入圖片描述

最初的poc主要利用UnitOfWorkChangeSet類在構造函數中,會將輸入的byte數組的內容進行反序列化,所以說剛開始說是反序列化漏洞。

其實這個洞是利用了存在問題的類的構造函數,因爲沒法用調用method了,就取了這種比較折中的方法。(其實還是有部分方法可以調用的:-D)。

在做這個實驗時需要導入weblogic 10.3.6的modules目錄下com.oracle.toplink_1.1.0.0_11-1-1-6-0.jar文件。

<java>
    <class><string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string><void>
            <array class="byte" length="2">
                <void index="0">
                    <byte>-84</byte>
                </void>
                <void index="0">
                    <byte>-19</byte>
                </void>
            </array>
        </void>
    </class>
</java> 

由於class標籤繼承了繼承了string標籤的addCharacter函數,導致其會將標籤中包裹的空白字符(空格\r\n\t)也加入到classname中,導致找class失敗,所以至少要將<class>到<void>之間的空白字符刪除。

PS: 其實這裏不加string標籤也沒問題。

Level1中說到:

Expression的首個參數是來自於上面getContextBean獲取的Class對象

也就是說,如果能夠找到替代上面object/void+class屬性的方法令getContextBean可以獲取到Class對象,也久可以調用構造函數進行對象的創建。

我們來看下此處調用的getContextBean的實現:
在這裏插入圖片描述
Level1/2中由於Object(Void)設置了class屬性,那麼type是有值的,所以直接返回type。

而父類的getContextBean是調用parent的getValueObject函數,來獲取上一級對象,所以此時我們令上一級獲取到的對象爲Class即可,所以此處使用了class標籤令void的上一級獲取的對象爲Class對象。

因爲void標籤只允許使用index屬性,所以此處無法使用method屬性來調用具體函數,所以只能寄期望於構造方法,就有了上面利用UnitOfWorkChangeSet類的構造方法來利用反序列化漏洞的手法。

同樣可利用的類還有之前jackson rce用的FileSystemXmlApplicationContext類。

總結

XMLDecoder的流程還是蠻有意思的,具體各標籤的功能、詳細解析流程,還需要大家自己看一下。

順便重要的事情說三遍:
一定要自己跟一下!
一定要自己跟一下!
一定要自己跟一下!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章