CSRF

Cross Site Request Forgery (CSRF)

跨站请求伪造

0x00 LOW Level

最初想直接尝试SQL注入,然后发现大部分特殊字符被转义了,原因在于函数mysqli_real_escape_string

源码:

<?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
        $current_user = dvwaCurrentUser();
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
        $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);
}

?>

问题:什么是跨站

浏览器打开的一个页面其中信息被其他的网站修改(不同域之间相互请求资源,就算跨域)

比如说你现在打开了支付宝,登陆了自己的用户账号,看了一圈花呗,然后果断下线,如果此时你的Cookie等啥身份验证还没有过期,那么攻击者就可以伪造一个页面来诱惑你点击,在你神不知鬼不觉的情况下修改你的密码,让你无法再登陆自己的支付宝账号。

关于同源策略:

一个网页设置的cookie,另一个网页不能打开,除非这两个网页同源。

同源又是指:

  1. 协议相同
  2. 域名相同
  3. 端口相同

但是CSRF就是可以做到违反同源策略,仍可以依靠尚未过期的身份打开页面进行操作(就比如在接下来的操作中,我们Burpsuite生成的html文件是存放在攻击机的,因此通过file协议读取:file:///C:/Users/xxxx/Desktop/CRSF.html首先协议就不相同了)

重点

我们可以通过Burpsuite进行抓包

将HTML文件保存在本地,构造CSRF url(这个需要相同的浏览器打开,因为不同的浏览器cookie不同,没办法进行身份的盗用)

这里最开始测试的时候我们,可以在首部url中看到相关的GET方式得到的数据,

http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=1&password_conf=1&Change=Change#

然后我们可以修改password的值然后再打开一个新的页面,就会发现之前的密码不管用了,这个可以作为一个初步的尝试,但是我觉得无法很好的展示其违反的同源的特性,尤其是都在同一个设备上测试的时候。

0x01 Medium_Level

源码:

<?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
            $current_user = dvwaCurrentUser();
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
            $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);
}

?>

我们注意到多了一个判断

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

什么是HTTP_REFERER

关于Referer:表明了请求的来源。我们学习计网的过程中了解过了,关于网络上访问浏览器的请求会传输数据包,然后在应用层采用http协议中Referer是其Header的一部分,你访问的网站也是通过它来统计网站的流量的。

目的:它存在的作用一般是为了防盗链接,就是我要访问的网站只允许自己的域名访问,不被允许就会被拦截

就比如此时我们在打开一个新的页面更改url是不被允许的,因为我们通过F12可以看到新的网页不含referer。

解决

因此,我们可以抓包后,手动加入Referer。

0x02 High_Level

源码:

<?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
        $current_user = dvwaCurrentUser();
        $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';";
        $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 {
        echo "<pre>" . $return_message . "</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

发现此时多出来了许多判断,

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

多出来了一个token,

http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=1&password_conf=1&Change=Change&user_token=2361956e672e361c3a9a3436a47105d1#

什么是token

:Anti-CSRF token机制,用户每次访问修改密码的页面时,服务器都会返回一个随机的token,当浏览器向服务器发起请求,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

因此它比referer更安全

这里Burpsuite可以帮我们直接完成token的获取

安装好插件后修改,就可以进行修改密码。

重点

  1. 什么是跨站(不同域的资源请求)| 什么是同源(三个特性相同即为同源)
  2. Referer的作用:用于指出域名来源
  3. token作用:安全性更近一步

热门相关:赠我深爱如长风   首辅娇娘   帝少宠妻有点甜   绝色符师:龙皇的狂傲妃   惊悚乐园