一、CSRF漏洞的挖掘與利用
0x01 CSRF的攻擊原理
CSRF 百度上的意思是跨站請求僞造,其實最簡單的了解我們可以這麼講,假如一個微網誌關注使用者的一個功能,存在CSRF漏洞,那麼此時黑客隻需要僞造一個頁面讓受害者間接或者直接觸發,然後這個惡意頁面就可以使用受害者的微網誌權限去關注其他的人微網誌賬戶。CSRF隻要被批量化利用起來其危害還是比較大的。
舉個例子,比如筆者在新浪首頁執行了一次搜尋請求。

可以看到這裡有個Referer的資訊,Referer也就是請求來源位址,也就是說這個請求是從http://www.sina.com.cn這裡發起的。然後去掉Referer,去執行這個請求,發現這個請求仍然可以進行正常的搜尋,那麼就說明這個請求沒有判斷請求來源也就是Referer,在請求頭部也沒有發現存在token的迹象,那麼筆者可以判斷此處存在CSRF,這個CSRF是沒有任何危害的,但是換成其他請求呢?比如加關注,修改資料或者其他敏感操作呢?
0x02 CSRF漏洞成因及分類
1)第一種
請求直接是個GET請求,然後請求中沒有token驗證參數,然後還有一個固定的變量可以被控制。這種是比較常見的一種CSRF漏洞。這種漏洞的檢測方法很簡單:網頁操作某功能,抓包後,如果發現滿足上面條件,然後再去頁面測試下,基本就可以确定存在不存在CSRF漏洞了。
執行個體
某微網誌站曾存在的一個漏洞,關注使用者微網誌的請求如下(其中key的值是每一個使用者獨有唯一的):
http://***.****.com/reflow/follow?resType=json&isAjax=1&key=226216966
筆者根據上面的測試方法進行測試發現無token,但是對referer做了校驗,但是csrf的情況還是存在的,因為被攻擊者的referer往往是和CSRF漏洞的GET請求是同域的,是以除非驗證的相當的嚴格,一般情況下的驗證是無效的,此類的站點輸入微網誌類的站點,使用者可以随随便便在上面發送微網誌,微網誌中可以帶上該連結,隻要受害者點選即可關注黑客的微網誌了。
2)第二種
請求是個POST請求,post請求中沒有token參數,然後請求也沒有驗證referer資訊。這種是存在CSRF情況最多的一種。這種漏洞的檢測方法也很簡單:網頁操作某功能,抓包後,如果發現沒有token等參數,然後就将referer資訊設定為空,再次發包請求,如果請求成功了,就說明這裡有CSRF漏洞。
如果有token等參數,可以嘗試将token去掉,然後再将referer也去掉,進行驗證。這種CSRF漏洞的利用,是需要在自己伺服器構造一個form表單的,然後将伺服器form表單的URL作為CSRF攻擊進行利用的,或者用js代碼生成form表單,或者用ajax實作。
某微網誌首頁刷粉絲,利用poc如下:
請求是POST,post請求中沒有token參數,但是驗證了referer資訊。然而可以将post請求改寫為get請求,然後通過第一種情況下的那個方法利用。這種的檢測方法,就是先執行了第二種的驗證後,發現有對CSRF進行防禦。然後将post請求改寫為GET請求,發現仍然可以成功執行。漏洞成因是因為伺服器端接收請求參數的時候,沒有嚴格的用$_POST 而是用的類似于 $_REQUEST這種post,get請求的參數都可以接收的寫法。
二、CSRF漏洞的攻擊與防禦
CSRF概念:CSRF跨站點請求僞造(Cross—Site Request Forgery),跟XSS攻擊一樣,存在巨大的危害性,你可以這樣來了解:
攻擊者盜用了你的身份,以你的名義發送惡意請求,對伺服器來說這個請求是完全合法的,但是卻完成了攻擊者所期望的一個操作,比如以你的名義發送郵件、發消息,盜取你的賬号,添加系統管理者,甚至于購買商品、虛拟貨币轉賬等。 如下:其中Web A為存在CSRF漏洞的網站,Web B為攻擊者建構的惡意網站,User C為Web A網站的合法使用者。
CSRF攻擊攻擊原理及過程如下:1. 使用者C打開浏覽器,通路受信任網站A,輸入使用者名和密碼請求登入網站A;
2.在使用者資訊通過驗證後,網站A産生Cookie資訊并傳回給浏覽器,此時使用者登入網站A成功,可以正常發送請求到網站A;
3. 使用者未退出網站A之前,在同一浏覽器中,打開一個TAB頁通路網站B;
4. 網站B接收到使用者請求後,傳回一些攻擊性代碼,并發出一個請求要求通路第三方站點A;
5. 浏覽器在接收到這些攻擊性代碼後,根據網站B的請求,在使用者不知情的情況下攜帶Cookie資訊,向網站A送出請求。網站A并不知道該請求其實是由B發起的,是以會根據使用者C的Cookie資訊以C的權限處理該請求,導緻來自網站B的惡意代碼被執行。
CSRF攻擊執行個體受害者 Bob 在銀行有一筆存款,通過對銀行的網站發送請求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款轉到 bob2 的賬号下。通常情況下,該請求發送到網站後,伺服器會先驗證該請求是否來自一個合法的 session,并且該 session 的使用者 Bob 已經成功登陸。
黑客 Mallory 自己在該銀行也有賬戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己發送一個請求給銀行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是這個請求來自 Mallory 而非 Bob,他不能通過安全認證,是以該請求不會起作用。
這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下代碼: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通過廣告等誘使 Bob 來通路他的網站。當 Bob 通路該網站時,上述 url 就會從 Bob 的浏覽器發向銀行,而這個請求會附帶 Bob 浏覽器中的 cookie 一起發向銀行伺服器。大多數情況下,該請求會失敗,因為他要求 Bob 的認證資訊。但是,如果 Bob 當時恰巧剛通路他的銀行後不久,他的浏覽器與銀行網站之間的 session 尚未過期,浏覽器的 cookie 之中含有 Bob 的認證資訊。這時,悲劇發生了,這個 url 請求就會得到響應,錢将從 Bob 的賬号轉移到 Mallory 的賬号,而 Bob 當時毫不知情。等以後 Bob 發現賬戶錢少了,即使他去銀行查詢日志,他也隻能發現确實有一個來自于他本人的合法請求轉移了資金,沒有任何被攻擊的痕迹。而 Mallory 則可以拿到錢後逍遙法外。
CSRF漏洞檢測:檢測CSRF漏洞是一項比較繁瑣的工作,最簡單的方法就是抓取一個正常請求的資料包,去掉Referer字段後再重新送出,如果該送出還有效,那麼基本上可以确定存在CSRF漏洞。
随着對CSRF漏洞研究的不斷深入,不斷湧現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具為例,CSRF漏洞檢測工具的測試原理如下:使用CSRFTester進行測試時,首先需要抓取我們在浏覽器中通路過的所有連結以及所有的表單等資訊,然後通過在CSRFTester中修改相應的表單等資訊,重新送出,這相當于一次僞造用戶端請求。如果修改後的測試請求成功被網站伺服器接受,則說明存在CSRF漏洞,當然此款工具也可以被用來進行CSRF攻擊。
防禦CSRF攻擊:目前防禦 CSRF 攻擊主要有三種政策:驗證 HTTP Referer 字段;在請求位址中添加 token 并驗證;在 HTTP 頭中自定義屬性并驗證。
(1)驗證 HTTP Referer 字段根據 HTTP 協定,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP 請求的來源位址。在通常情況下,通路一個安全受限頁面的請求來自于同一個網站,比如需要通路 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,使用者必須先登陸 bank.example,然後通過點選頁面上的按鈕來觸發轉賬事件。這時,該轉帳請求的 Referer 值就會是轉賬按鈕所在的頁面的 URL,通常是以 bank.example 域名開頭的位址。而如果黑客要對銀行網站實施 CSRF 攻擊,他隻能在他自己的網站構造請求,當使用者通過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客自己的網站。是以,要防禦 CSRF 攻擊,銀行網站隻需要對于每一個轉賬請求驗證其 Referer 值,如果是以 bank.example 開頭的域名,則說明該請求是來自銀行網站自己的請求,是合法的。如果 Referer 是其他網站的話,則有可能是黑客的 CSRF 攻擊,拒絕該請求。
這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心 CSRF 的漏洞,隻需要在最後給所有安全敏感的請求統一增加一個攔截器來檢查 Referer 的值就可以。特别是對于目前現有的系統,不需要改變目前系統的任何已有代碼和邏輯,沒有風險,非常便捷。
然而,這種方法并非萬無一失。Referer 的值是由浏覽器提供的,雖然 HTTP 協定上有明确的要求,但是每個浏覽器對于 Referer 的具體實作可能有差别,并不能保證浏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴于第三方(即浏覽器)來保障,從理論上來講,這樣并不安全。事實上,對于某些浏覽器,比如 IE6 或 FF2,目前已經有一些方法可以篡改 Referer 值。如果 bank.example 網站支援 IE6 浏覽器,黑客完全可以把使用者浏覽器的 Referer 值設為以 bank.example 域名開頭的位址,這樣就可以通過驗證,進而進行 CSRF 攻擊。
即便是使用最新的浏覽器,黑客無法篡改 Referer 值,這種方法仍然有問題。因為 Referer 值會記錄下使用者的通路來源,有些使用者認為這樣會侵犯到他們自己的隐私權,特别是有些組織擔心 Referer 值會把組織内網中的某些資訊洩露到外網中。是以,使用者自己可以設定浏覽器使其在發送請求時不再提供 Referer。當他們正常通路銀行網站時,網站會因為請求沒有 Referer 值而認為是 CSRF 攻擊,拒絕合法使用者的通路。
(2)在請求位址中添加 token 并驗證CSRF 攻擊之是以能夠成功,是因為黑客可以完全僞造使用者的請求,該請求中所有的使用者驗證資訊都是存在于 cookie 中,是以黑客可以在不知道這些驗證資訊的情況下直接利用使用者自己的 cookie 來通過安全驗證。要抵禦 CSRF,關鍵在于在請求中放入黑客所不能僞造的資訊,并且該資訊不存在于 cookie 之中。可以在 HTTP 請求中以參數的形式加入一個随機産生的 token,并在伺服器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 内容不正确,則認為可能是 CSRF 攻擊而拒絕該請求。
這種方法要比檢查 Referer 要安全一些,token 可以在使用者登陸後産生并放于 session 之中,然後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在于如何把 token 以參數的形式加入請求。對于 GET 請求,token 将附在請求位址之後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對于 POST 請求來說,要在 form 的最後加上 ,這樣就把 token 以參數的形式加入請求了。但是,在一個網站中,可以接受請求的地方非常多,要對于每一個請求都加上 token 是很麻煩的,并且很容易漏掉,通常使用的方法就是在每次頁面加載時,使用 javascript 周遊整個 dom 樹,對于 dom 中所有的 a 和 form 标簽後加入 token。這樣可以解決大部分的請求,但是對于在頁面加載之後動态生成的 html 代碼,這種方法就沒有作用,還需要程式員在編碼時手動添加 token。
該方法還有一個缺點是難以保證 token 本身的安全。特别是在一些論壇之類支援使用者自己發表内容的網站,黑客可以在上面釋出自己個人網站的位址。由于系統也會在這個位址後面加上 token,黑客可以在自己的網站上得到這個 token,并馬上就可以發動 CSRF 攻擊。為了避免這一點,系統可以在添加 token 的時候增加一個判斷,如果這個連結是鍊到自己本站的,就在後面添加 token,如果是通向外網則不加。不過,即使這個 csrftoken 不以參數的形式附加在請求之中,黑客的網站也同樣可以通過 Referer 來得到這個 token 值以發動 CSRF 攻擊。這也是一些使用者喜歡手動關閉浏覽器 Referer 功能的原因。
(3)在 HTTP 頭中自定義屬性并驗證這種方法也是使用 token 并進行驗證,和上一種方法不同的是,這裡并不是把 token 以參數的形式置于 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裡。通過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上 csrftoken 這個 HTTP 頭屬性,并把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,通過 XMLHttpRequest 請求的位址不會被記錄到浏覽器的位址欄,也不用擔心 token 會透過 Referer 洩露到其他網站中去。
然而這種方法的局限性非常大。XMLHttpRequest 請求通常用于 Ajax 方法中對于頁面局部的異步重新整理,并非所有的請求都适合用這個類來發起,而且通過該類請求得到的頁面不能被浏覽器所記錄下,進而進行前進,後退,重新整理,收藏等操作,給使用者帶來不便。另外,對于沒有進行 CSRF 防護的遺留系統來說,要采用這種方法來進行防護,要把所有請求都改為 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。