zoukankan      html  css  js  c++  java
  • DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA

    之前在 CSRF 攻击 的那篇文章的最后,我觉得可以用验证码提高攻击的难度。

    若有验证码的话,就比较难被攻击者利用 XSS 漏洞进行的 CSRF 攻击了,因为要识别验证码起码要调用api,跨域会被浏览器拦截,再者一些验证码很难被识别,比如知乎点击倒立的汉字,拖动拼图、百度的汉字验证码,谷歌的神奇的勾勾。。。 觉得这篇文章像是 CSRF 攻击 的一种补充(更像是谷歌验证码的使用教程,而且正常人的逻辑也不会犯这个问题的,其实可以跳过这篇文章)

    话说回来,你会发现页面是这样的


    其实也不用科学上网,直接在/var/www/html/config/config.inc.php 中 乱写个字符串就行了。

    之后就会变成这样。

    反正验证码加载不出来,要加载出来服务端也要科学上网,而我们需要做的是,绕过验证

    而科学上网后,验证码是这样的,这肯定是破解不出来的。

    初级

    看代码,这里修改密码是分成二个部分的,一个部分是用来判断验证码的正确性,如果正确了就再返回密码的界面,这个界面就不再需要输入验证码的,按提交就可以修改密码了。这两个部分的用 form 表单的 step 字段区分。。。(大家都应该能猜到如何攻击了) 代码如下

    <?php
    //步骤1 
    if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) {
        // Hide the CAPTCHA form
        $hide_form = true;
    
        // Get input
        $pass_new  = $_POST[ 'password_new' ];
        $pass_conf = $_POST[ 'password_conf' ];
    
        // Check CAPTCHA from 3rd party
        $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ],
            $_SERVER[ 'REMOTE_ADDR' ],
            $_POST[ 'recaptcha_challenge_field' ],
            $_POST[ 'recaptcha_response_field' ] );
    
        // Did the CAPTCHA fail?
        if( !$resp->is_valid ) {
            // What happens when the CAPTCHA was entered incorrectly
            $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
            $hide_form = false;
            return;
        }
        else {
            //CAPTCHA was correct. Do both new passwords match?
            if( $pass_new == $pass_conf ) {
                // Show next stage for the user
                echo "
                    <pre><br />You passed the CAPTCHA! Click the button to confirm your changes.<br /></pre>
                    <form action="#" method="POST">
                        <input type="hidden" name="step" value="2" />
                        <input type="hidden" name="password_new" value="{$pass_new}" />
                        <input type="hidden" name="password_conf" value="{$pass_conf}" />
                        <input type="submit" name="Change" value="Change" />
                    </form>";
            }
            else {
                // Both new passwords do not match.
                $html     .= "<pre>Both passwords must match.</pre>";
                $hide_form = false;
            }
        }
    }
    //步骤2 
    if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
        // Hide the CAPTCHA form
        $hide_form = true;
    
        // Get input
        $pass_new  = $_POST[ 'password_new' ];
        $pass_conf = $_POST[ 'password_conf' ];
    
        // Check to see if both password match
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );
    
            // Update database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
    
            // Feedback for the end user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with the passwords matching
            echo "<pre>Passwords did not match.</pre>";
            $hide_form = false;
        }
    
        mysql_close();
    }
    
    ?>
    

    攻击方式也很简单,用 BurpSuite 直接改请求

    或者打开火狐的审查元素(F12)直接改 form 表单

    所以还是会有 CSRF 攻击漏洞的。

    中级

    中级篇主要区别是如果第一步验证成功了,会有一个 passed_captcha 字段

    ...
    if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
        // Hide the CAPTCHA form
        $hide_form = true;
    
        // Get input
        $pass_new  = $_POST[ 'password_new' ];
        $pass_conf = $_POST[ 'password_conf' ];
    
        //如果有做第一步  Check to see if they did stage 1
        if( !$_POST[ 'passed_captcha' ] ) {
            $html     .= "<pre><br />You have not passed the CAPTCHA.</pre>";
            $hide_form = false;
            return;
        }
    
        // Check to see if both password match
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );
    
            // Update database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
    
            // Feedback for the end user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with the passwords matching
            echo "<pre>Passwords did not match.</pre>";
            $hide_form = false;
        }
    
        mysql_close();
    }
    
    ?>
    

    所以,同样可以用 burp suite 或者火狐添加一个参数吧。用火狐先打开审查元素,发一个失败的请求,然后修改请求如下。

    高级

    中级、高级不一样,不分步骤了。。。都放在一块了,关注点在请求失败的条件,代码如下。

    <?php
    
    if( isset( $_POST[ 'Change' ] ) ) {
        // Hide the CAPTCHA form
        $hide_form = true;
    
        // Get input
        $pass_new  = $_POST[ 'password_new' ];
        $pass_conf = $_POST[ 'password_conf' ];
    
        // Check CAPTCHA from 3rd party
        $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ],
            $_SERVER[ 'REMOTE_ADDR' ],
            $_POST[ 'recaptcha_challenge_field' ],
            $_POST[ 'recaptcha_response_field' ] );
    
        // Did the CAPTCHA fail?
        if( !$resp->is_valid && ( $_POST[ 'recaptcha_response_field' ] != 'hidd3n_valu3' 
                               || $_SERVER[ 'HTTP_USER_AGENT' ] != 'reCAPTCHA' ) ) {
            // What happens when the CAPTCHA was entered incorrectly
            $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
            $hide_form = false;
            return;
        }
        else {
            // CAPTCHA was correct. Do both new passwords match?
            if( $pass_new == $pass_conf ) {
                $pass_new = mysql_real_escape_string( $pass_new );
                $pass_new = md5( $pass_new );
    
                // Update database
                $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";
                $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
    
                // Feedback for user
                echo "<pre>Password Changed.</pre>";
            }
            else {
                // Ops. Password mismatch
                $html     .= "<pre>Both passwords must match.</pre>";
                $hide_form = false;
            }
        }
    
        mysql_close();
    }
    
    //神·生成token,服务器又不验证 Generate Anti-CSRF token 
    generateSessionToken();
    
    ?>
    

    关键是判断验证码是否失败的那句

    // Did the CAPTCHA fail?
    if( !$resp->is_valid && ( $_POST[ 'recaptcha_response_field' ] != 'hidd3n_valu3'
                           || $_SERVER[ 'HTTP_USER_AGENT' ] != 'reCAPTCHA' ) ) {
        // What happens when the CAPTCHA was entered incorrectly
        $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
        return;
    }
    

    而验证是否失败的条件是,谷歌服务器返回的结果是 false ,而且请求属性 recaptcha_response_field 的值不能是 hidd3n_valu3 或者* 请求头部的 HTTP_USER_AGENT 不能是 reCAPTCHA

    也就是当 recaptcha_response_field 会是 hidd3n_valu3,头部的 HTTP_USER_AGENT 是 reCAPTCHA ,就能绕过了。

    应该是逻辑错误的,估计是如果验证成功 recaptcha_response_field 会是 hidd3n_valu3,头部的 HTTP_USER_AGENT 会变成 reCAPTCHA 吧。 所以应该是

    !($resp->is_valid && $_POST[ 'recaptcha_response_field' ] == 'hidd3n_valu3' && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA' )
    

    再换算

    !($resp->is_valid && ($_POST[ 'recaptcha_response_field' ] == 'hidd3n_valu3' && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA' ))
    

    然后在换算

    !($resp->is_valid) || !($_POST[ 'recaptcha_response_field' ] == 'hidd3n_valu3' && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA' )
    

    不可能

    与前面的相比

    • 判断验证码的方式只有 $resp->is_valid
    • 有验证生成的 token
    • 使用 prepare 预防 sql 注入
    • 要输入原密码
    Insecure CAPTCHA Source
    <?php
    
    if( isset( $_POST[ 'Change' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Hide the CAPTCHA form
        $hide_form = true;
    
        // Get input
        $pass_new  = $_POST[ 'password_new' ];
        $pass_new  = stripslashes( $pass_new );
        $pass_new  = mysql_real_escape_string( $pass_new );
        $pass_new  = md5( $pass_new );
    
        $pass_conf = $_POST[ 'password_conf' ];
        $pass_conf = stripslashes( $pass_conf );
        $pass_conf = mysql_real_escape_string( $pass_conf );
        $pass_conf = md5( $pass_conf );
    
        $pass_curr = $_POST[ 'password_current' ];
        $pass_curr = stripslashes( $pass_curr );
        $pass_curr = mysql_real_escape_string( $pass_curr );
        $pass_curr = md5( $pass_curr );
    
        // Check CAPTCHA from 3rd party
        $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ],
            $_SERVER[ 'REMOTE_ADDR' ],
            $_POST[ 'recaptcha_challenge_field' ],
            $_POST[ 'recaptcha_response_field' ] );
    
        // Did the CAPTCHA fail?
        if( !$resp->is_valid ) {
            // What happens when the CAPTCHA was entered incorrectly
            echo "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
            $hide_form = false;
            return;
        }
        else {
            // 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 password match and was the current password correct?
            if( ( $pass_new == $pass_conf) && ( $data->rowCount() == 1 ) ) {
                // Update the database
                $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 end user - success!
                echo "<pre>Password Changed.</pre>";
            }
            else {
                // Feedback for the end user - failed!
                echo "<pre>Either your current password is incorrect or the new passwords did not match.<br />Please try again.</pre>";
                $hide_form = false;
            }
        }
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?>
    
  • 相关阅读:
    Android 使用MediaPlayer 播放 视频
    Android加载asset的db
    MAC SVN 基本设置 终端命令
    AFNetWork 简单实用demo
    IntelliJ IDEA导出Java 可执行Jar包
    Xcode快速排错
    Listview多tab上滑悬浮
    N最短路径分词
    进程监控工具supervisor
    nginx配置指南
  • 原文地址:https://www.cnblogs.com/jojo-feed/p/10173225.html
Copyright © 2011-2022 走看看