漏洞名稱:Host頭攻擊
風險等級:低
問題類型:管理員設置問題
漏洞描述:
Host首部字段是HTTP/1.1新增的,旨在告訴服務器,客戶端請求的主機名和端口號,主要用來實現虛擬主機技術。運用虛擬主機技術,單個主機可以運行多個站點。
例如:hacker和usagidesign兩個站點都運行在同一服務器A上,不管我們請求哪個域名,最終都會被解析成服務器A的IP地址,這個時候服務器就不知道該將請求交給哪個站點處理,因此需要Host字段指定請求的主機名。
我們訪問hacker域名,經DNS解析,變成了服務器A的IP,請求傳達到服務器A,A接收到請求后,發現請求報文中的Host字段值為hacker,進而將請求交給hacker站點處理。
這個時候,問題就出現了。為了方便獲取網站域名,開發人員一般依賴於請求包中的Host首部字段。例如,在php里用_SERVER["HTTP_HOST"],但是這個Host字段值是不可信賴的(可通過HTTP代理工具篡改)。
漏洞危害:
如果應用程序沒有對Host字段值進行處理,就有可能造成惡意代碼的傳入。
為了方便獲取網站域名,開發人員一般依賴於請求包中的Host首部字段。例如,在php里用_SERVER["HTTP_HOST"]
。但是這個Host字段值是不可信賴的(可通過HTTP代理工具篡改),如果應用程序沒有對Host字段值進行處理,就有可能造成惡意代碼的傳入。
很多應用是直接把這個值不做HTML編碼便輸出到了頁面中,比如:
<link href="http://_SERVER['HOST']" (Joomla)
還有的地方還包含有secret key和token:
<a href="http://_SERVER['HOST']?token=topsecret"> (Django, Gallery, others)
這樣處理問題一般會很容易遭遇到兩種常見的攻擊:緩存污染和密碼重置。緩存污染是指攻擊者通過控制一個緩存系統來將一個惡意站點的頁面返回給用戶。密碼重置這種攻擊主要是因為發送給用戶的內容是可以污染的,也就是說可以間接的劫持郵件發送內容。
密碼重置
拿 Gallery 這個站來做例子。當我們進行密碼重置的時候,網站會給我們發送一個隨機的key:
$user -> hash = random::hash() ; $message -> confirm_url = url::abs_site("password/do_reset?key=$user->hash") ;
當用戶點擊重置密碼的鏈接時,肯定可以說明點的是自己的賬戶。
這個地方的漏洞是: url::abs_site
這一部分使用的Host header是來自用戶重置密碼的請求,那么攻擊者可以通過一個受他控制的鏈接來污染密碼重置的郵件。
> POST /password/reset HTTP/1.1 > Host: evil.com > ... > csrf=1e8d5c9bceb16667b1b330cc5fd48663&name=admin
當然這種攻擊方式一定要能騙取用戶點擊訪問這個受污染的鏈接,如果用戶警覺了沒有點擊,那么攻擊就會失敗。當然你自己也可以配合一些社會工程學的方法來保證攻擊的成功率。
緩存污染
通過Host header來污染緩存的攻擊方法最初是Carlos Beuno 在2008年提出來的。但是在現在的網絡架構中,這種攻擊還是比較困難的,因為現在的緩存設備都能夠識別Host。比如對於下面的這兩種情況他們絕對不會弄混淆:
> GET /index.html HTTP/1.1 > GET /index.html HTTP/1.1 > Host: example.com > Host: evil.com
因此為了能使緩存能將污染后的response返回給用戶,我們還必須讓緩存服務器看到的host header 和應用看到的host header 不一樣。
比如說對於Varnish(一個很有名的緩存服務軟件),可以使用一個復制的Host header。Varnish是通過最先到達的請求的host header來辨別host的,而Apache則是看所有請求的host,Nginx則只是看最后一個請求的host。這就意味着你可以通過下面這個請求來欺騙Varnish達到污染的目的:
> GET / HTTP/1.1
> Host: example.com
> Host: evil.com
應用本身的緩存也可能受到污染。比如Joomla就將取得的host值不經html編碼便寫進任意頁面,而它的緩存則對這些沒有任何處理。比如可以通過下面的請求來寫入一個存儲型的xss:
> GET / HTTP/1.1 > Host: cow"onerror='alert(1)'rel='stylesheet'
響應其實已經受到污染:
<link href="http://cow"onerror='alert(1)'rel='stylesheet'/" rel="canonical"/>
這時只需要瀏覽首頁看是否有彈窗就知道緩存是否已經被污染了。
漏洞檢測
Host頭攻擊漏洞的檢測比較簡單,只需要抓包,修改Host字段值,提交,查看響應中是否包含修改后的Host字段值即可。下面我分三個場景,介紹一下Host頭攻擊漏洞存在的表現。
1、跳轉
【場景一】正常請求,響應302,Location首部字段指明跳轉的地址,其中Location字段值為Host字段指定的地址。
將請求包的Host字段值修改為www.baidu.com提交,響應包中的Location地址也被更改為www.baidu.com。
2、拼接
【場景二】正常請求,正常響應,將Host字段值拼接到標簽屬性值中。
將請求包的Host字段值修改為www.baidu.com提交,發現服務器將www.baidu.com拼接到了script標簽的src屬性值中。
3、代碼注入
【場景三】這其實也屬於拼接,只不過在場景二的基礎上寫入了惡意代碼。
利用Host字段寫入script標簽,彈出警告框。
修復建議
對Host字段進行檢測
Nginx,修改ngnix.conf文件,在server中指定一個server_name名單,並添加檢測。
Apache,修改httpd.conf文件,指定ServerName,並開啟UseCanonicalName選項。
Tomcat,修改server.xml文件,配置Host的name屬性。
Web應用程序應該使用SERVER_NAME
而不是host header
。在Apache和Nginx里可以通過設置一個虛擬機來記錄所有的非法host header。在Nginx里還可以通過指定一個SERVER_NAME名單,Apache也可以通過指定一個SERVER_NAME名單並開啟UseCanonicalName選項。
不要使用類似JSP中request.getServerName( )
方法引用客戶端輸入的hostname值。拼接生成URL時引用靜態變量定義的服務器域名,或者使用相對路徑生成URL。