為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來執行命令。