當前位置:成語大全網 - 書法字典 - xmldecoder反序列化漏洞分析

xmldecoder反序列化漏洞分析

Java提供了很多xml文檔解析的庫,包括dom4j、domj、SAX等等,都可以解析xml文檔。對這些庫的不當使用將導致XXE漏洞。但是在這些庫中,SAX庫允許自己定義整個xml文檔處理的handler,可以在xml文檔解析的過程中對解析的節點進行壹些操作。

為java反序列化定義的處理程序會導致壹些java反序列化問題。

概念驗證:

獲取文件內容後,用XMLDecoder解析它,並調用下面的readObject方法。

進入readObject方法。

文檔在這裏被解析,XMLDecoder.this.handler實際上是

文檔處理程序

這個處理程序非常重要,所有的反序列化操作都在這個類中進行。

類通過SAXParserFactory實例化SAXParser的壹個實例,並調用其中的parse方法。

重現過java XXE漏洞的高手應該見過SAXParser的使用,它允許用戶定義自己的處理程序來處理文檔。

如您所見,用戶可以定義自己的處理程序。只要這個類繼承了DefaultHandler,就可以看壹下官網的例子:

您自己的處理程序可以在解析的不同階段執行不同的操作,這使得xml文檔成為java序列化的載體。DocumentHandler處理xml文檔的反序列化。

接下來,調用SAXParser來解析xml文檔。設置壹些屬性後,真正的解析過程從XML11Configuration類中的parse方法開始。

首先是對實體的分析:

因為我們的文檔中沒有xml實體,所以我們不需要註意這壹步。

然後解析文檔。

進入scanDocument函數。

這裏,文檔由下壹個函數解析,並返回當前的解析狀態。整個文檔的具體解析過程在XMLDocumentFragmentScannerImpl類的ContentDriver類中,裏面的解析過程非常復雜。不要太關註具體的解析過程,而要關註解析結果的處理。

當發現當前文檔是根節點時,調用fContentDriver的下壹個方法來解析這裏的根節點。

直到XMLDocumentFragmentScannerImpl的scanStartElement方法。

解析類中的文檔,並調用scanName解析出第壹個名為java的節點。

然後找出java節點的結束字符在哪裏,解析出中間的所有屬性。

解析後,該節點中的所有屬性都將添加到屬性中。

最後,在startElement函數中,我們可以認為fElementQName是我們節點的名稱,attributes是這個節點中所有屬性組成的字典。

最後,調用DocumentHandler的startElement是真正的反序列化位置。

此時,壹個節點的解析就算結束了,頭也會被放大。首先附加壹個調用棧,然後轉到DocumentHandler查看具體的反序列化過程。

最後,調用DocumentHandler.startElement的函數。讓我們來看看。

這裏,根據不同節點實例化不同的節點處理程序。

在this.handlers中找到java節點對應的處理程序,就可以看看this.handlers中所有的處理程序都是什麽了。這是壹個散列表,也是私有的最終地圖

處理程序在構造函數中被賦值。

這是XMLDecoder支持的所有節點類型。不同的節點會調用不同的ElementHandler進行處理。我們的java節點應該由JavaElementHandler來處理。

回到startElement的方法

實例化JavaElementHandler類後,調用setParent保存最後壹個處理程序,相當於把每個節點的處理類串成壹個鏈。

在這壹步中,獲取所有節點屬性,並調用由處理程序本身實現的addAttribute方法。可以看看JavaElementHandler的addAttribute。

這裏通過我們設置的class屬性的內容,得到對應的類,也就是java.beans.XMLDecoder類的類。

然後調用處理程序對應的startElement,java處理程序沒有操作,於是解析下壹個節點。

下壹個節點是object,具體解析過程就不分析了。

對象對應的處理程序是ObjectElementHandler。可以發現startElement中的關鍵點其實是每個處理程序的addAttribute方法和startElement方法。

調用父類的addAttribute方法。

此步驟獲取ProcessBuilder的類。

處理處理程序:ArrayElementHandler

addAttribute:

數組元素的類型和數組大小在這裏定義。

startElement:

這裏實例化壹個數組元素,並返回壹個ValueObject對象。

空節點是特殊的。void node實際上是object node的子類,它沒有定義任何方法。

我們不必太在意void節點的處理規則,只需要明白它的作用是聲明壹個變量,可以這樣理解:

處理完壹個節點後,會從內向外依次調用每個節點的endElement方法。

比如我們的poc。

touch的string節點解析後,觸發string的endElement,主要是將值設置為StringElementHandler的屬性。

然後將該字符串添加到ArrayElementHandler中的數組,並為每個元素調用getValueObject方法,其中

void節點的endElement很有趣。

看壹看getContextBean方法。

這裏我們將獲得最後壹個處理程序的ValueObject的值,這個ValueObject的值就是我們在屬性中定義的對象。void node的最後壹個節點是壹個數組節點,我們在數組節點中定義了壹個大小為2的字符串數組,那麽得到的ValueObject就是這個數組。

因為我們只將void的屬性設置為index="0 ",所以我們將輸入var4=set的條件。

最後壹個Express類相當於在var3的對象上調用var4的方法,參數為var2,這樣就把這個字符串數組的第壹個值賦為touch。

來到第二個void標簽。

當提到…時

輸入getContextBean

這裏調用的是Parent的getValueObject方法,也就是Object標簽。

獲取ObjectElementHandler中的ProcessBuilder對象。

最後,調用start來執行命令。