本文首發(fā)于“合天智匯”公眾號 作者:Fortheone
前言
最近學習JAVA反序列化學到了weblogic部分,weblogic之前的兩個反序列化漏洞不涉及T3協(xié)議之類的,只是涉及到了XMLDecoder反序列化導致漏洞,但是網(wǎng)上大部分的文章都只講到了觸發(fā)XMLDecoder部分就結(jié)束了,并沒有講為什么XMLDecoder會觸發(fā)反序列化導致命令執(zhí)行。于是帶著好奇的我就跟著調(diào)了一下XMLDecoder的反序列化過程。
xml序列化
首先了解一下java中的XMLDecoder是什么。XMLDecoder就是jdk中一個用于處理xml數(shù)據(jù)的類,先看兩個例子。
這里引用一下淺藍表哥的(強推淺藍表哥的博客https://b1ue.cn/
import java.beans.XMLEncoder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* @author 淺藍
* @email [email protected]
* @since 2019/4/24 12:09
*/
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
HashMap<Object, Object> map = new HashMap<>();
map.put("123","aaaa");
map.put("321",new ArrayList<>());
XMLEncoder xmlEncoder = new XMLEncoder(System.out);
xmlEncoder.writeObject(map);
xmlEncoder.close();
}
}

這樣就把map對象變成了xml數(shù)據(jù),再使用XMLDecoder解析一下。
/**
* @author 淺藍
* @email [email protected]
* @since 2019/4/24 12:09
*/
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
String s = "<java version="1.8.0_131" class="java.beans.XMLDecoder">n" +
" <object class="java.util.HashMap">n" +
" <void method="put">n" +
" <string>123</string>n" +
" <string>aaaa</string>n" +
" </void>n" +
" <void method="put">n" +
" <string>321</string>n" +
" <object class="java.util.ArrayList"/>n" +
" </void>n" +
" </object>n" +
"</java>";
StringBufferInputStream stringBufferInputStream = new StringBufferInputStream(s);
XMLDecoder xmlDecoder = new XMLDecoder(stringBufferInputStream);
Object o = xmlDecoder.readObject();
System.out.println(o);
}
}

就可以把之前的xml數(shù)據(jù)反序列化回map對象,那么如果對xml數(shù)據(jù)進行修改,使其變成一個執(zhí)行命令的數(shù)據(jù)。比如說:
<java version="1.7.0_80" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0"><string>calc</string></void>
</array>
<void method="start"></void>
</object>
</java>
然后對其反序列化即可執(zhí)行命令彈出計算器。

現(xiàn)在我們知道了如果使用XMLDecoder去反序列化xml數(shù)據(jù),數(shù)據(jù)中包含的命令會被執(zhí)行。接下來就對其進行分析一下。
XMLDecoder反序列化漏洞成因
一、XML數(shù)據(jù)解析前的函數(shù)處理

在readObject處打上斷點開始debug

進入了parsingComplete方法,跟進。

其中使用XMLDecoder的handler屬性DocumentHandler的parse方法,并且傳入了我們輸入的xml數(shù)據(jù),跟進。

這里調(diào)用了SAXParserImpl類的parse方法。

然后又進了xmlReader的parse方法。

這里又調(diào)用了xmlReader父類AbstractSAXParser的parser方法。

最后進入了XML11Configuration類的parse方法。
二、XML數(shù)據(jù)的處理

在XML11Configuration中進行了很多解析XML之前的操作,我們不去仔細研究,看到處理XML數(shù)據(jù)的函數(shù)scanDocument。跟進查看

這個函數(shù)通過迭代的方式對XML數(shù)據(jù)的標簽進行解析,網(wǎng)上有些文章寫道“解析至END_ELEMENT時跟進調(diào)試”,但是我看了一下我這里的END_ELEMENT。

里面沒有函數(shù)可以跟進啊,然后搜了一些其他的文章,是因為jdk版本的問題,處理的邏輯放在了next函數(shù)里。在do while循環(huán)里跳了大概十次,就開始解析了xml的標簽。

跳到XMLDocumentScannerImpl中的next方法

跳到XMLDocumentFragmentScannerImpl中的next方法,解析到endtag時會走到scanEndElement方法里。
然后就到了網(wǎng)上說的endElement方法里,跟進。

這一部分的解析可以參考下圖:

也就是說解析時會按照標簽一個一個解析。

這里調(diào)用了DocumentHandler的endElement方法。接下來就是很重要的部分


這里的handler是StringElementHandler,但是這個類沒有重寫endElement方法,所以調(diào)用的是父類ElementHandler的endElement方法,其中調(diào)用了getValueObject來獲取標簽中的value值,這里的標簽是string標簽,所以獲取到的值是calc。


然后將其添加到其父類標簽VoidElementHandler的Argument屬性中。

然后將handler指向其父類VoidElementHandler。

繼續(xù)解析到void標簽,此時的handler就是VoidElementHandler,接著調(diào)用getValueObject。但是因為沒有重寫該方法,所以調(diào)用父類NewElementHandler的getValueObject。


繼續(xù)跟進發(fā)現(xiàn)實現(xiàn)了反射調(diào)用invoke方法,也就是執(zhí)行了set方法。接著再解析Array標簽,按照上面的步驟解析,就完成了這一部分參數(shù)的解析。
<array class="java.lang.String"length="1">
<void index="0">
<string>calc</string>
</void>
</array>

那么再按照上面的步驟解析object標簽,然后調(diào)用new 方法實例化 ProcessBuilder類。

然后解析到void標簽獲取到start方法,然后通過調(diào)用start方法實現(xiàn)了命令執(zhí)行,彈出計算器。
也就相當于最后拼接了 new java.lang.ProcessBuilder(new String[]{"calc"}).start();

文章有說的不對的地方請師傅們指點,剛開始學java,大佬們輕噴。。。
參考文章
https://b1ue.cn/archives/239.html
https://zhuanlan.zhihu.com/p/108754274
https://blog.csdn.net/SKI_12/article/details/85058040
相關(guān)實驗
Java反序列漏洞
https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001
(本實驗通過Apache Commons Collections 3為例,分析并復現(xiàn)JAVA反序列化漏洞。)