DVWA-Insecure CAPTCHA(不安全的验证码)

Insecure CAPTCHA,意思为不安全的验证码
全称为Completely Automated Public Turing Test to Tell Computers and Humans Apart,意思是全自动公共图灵测试,区分计算机和人类
顾名思义,想通过验证码拦截我们的操作,绕过即可
在进行实验前,可能又出现报错信息,只需要修改dvwa目录下得配置文件dvwa/config/config.inc.php
将这两个的值改为统一内容即可

LOW

审计源码

<?php
// 判断 POST 传参 Change ,和 step 为 1
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) {
    // 隐藏验证码表单
    $hide_form = true;

    // 获取输入的新密码和二次密码
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // 检查第三方验证码
    $resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key'],
        $_POST['g-recaptcha-response']
    );

    // 查看验证码正确
   
    if( !$resp ) {
        // 判断 $reps 进行了 ! 取反,代表 $resp 返回的错误是一个 True 值
        // 验证码错误
        $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
        return;
    }
    else {
        // 验证码正确
        // 判断两次输入密码是否想通过
        if( $pass_new == $pass_conf ) {
            // 进入下一阶段
            // 你通过了验证码!单击按钮确认更改。
            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 {
            // 两次输入密码不匹配
            $html     .= "<pre>Both passwords must match.</pre>";
            $hide_form = false;
        }
    }
}

// 判断传参 Change,并判断 step 是否为 2
// 这里很明显有一个漏洞,这个if语句不在上一个 step 2的嵌套中,直接传参 step == 2 就可以绕过验证码
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
    // 隐藏验证码表单
    $hide_form = true;

    // 获取两次输入密码
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // 检查两次输入密码是否相同
    if( $pass_new == $pass_conf ) {
        // 相同
        // 这里也是用 mysqli_real_escape_string 进行了一个转移,所以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)) ? "" : ""));
        // 使用md5加密输入的新密码
        $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>";
        $hide_form = false;
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

这里的验证码是调用的Google的,所以需要访问外网就可以调用成功。不能访问可无妨。
一个很明显的错误,step = 2的判断应该在step = 1的嵌套中,所以直接传参step=2,输入两次一样的密码就可以绕过验证码
HackBar测试,当然也可以使用burpsuite

可以看到密码更改成功,更改成功后,我使用更改的密码发现登录失败,使用空密码更改成功
后开看了源码发现,这里传参的两次密码应该是password_newpassword_conf,眼花了,不过没什么问题


所以这里使用burpsuite更好,可以直接获取数据包头

Medium

审计源码

<?php
// 判断传入的 Change 和 step 
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) {
    // 隐藏验证码
    $hide_form = true;

    // 获取两次输入密码
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // 检查引入的 Goolge 验证码
    $resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key' ],
        $_POST['g-recaptcha-response']
    );

    // 检测验证码是否正确
    if( !$resp ) {
        // 验证码错误
        $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
        return;
    }
    else {
        // 验证码正确
        if( $pass_new == $pass_conf ) {
            // 进入下一阶段
            // 你通过了验证码!单击按钮确认更改。
            // 这里于上次不同,多加了一个 passed_captcha 传参,值为 true
            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=\"hidden\" name=\"passed_captcha\" value=\"true\" />
                    <input type=\"submit\" name=\"Change\" value=\"Change\" />
                </form>";
        }
        else {
            // 两次输入密码不一致
            $html     .= "<pre>Both passwords must match.</pre>";
            $hide_form = false;
        }
    }
}
// 判断传入 Change 和 step == 2 
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
    // 隐藏验证码
    $hide_form = true;

    // 获取两次输入密码
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // 检查是否完成了一阶段,判断的方式是检查passed_captcha值是否为空
    // !取反,如果为空,打印 你没有通过验证码
    if( !$_POST[ 'passed_captcha' ] ) {
        $html     .= "<pre><br />You have not passed the CAPTCHA.</pre>";
        $hide_form = false;
        return;
    }

    // 检查两次输入密码是否一致
    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_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // 打印密码更改成功
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // 两次输入密码不一致
        echo "<pre>Passwords did not match.</pre>";
        $hide_form = false;
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

这里加入了一个passed_captcha来检测是否通过了step==1阶段,判断的方式是值是否为空,问题很突出,只需要传参任意值
就可以绕过验证码
HackBar测试,这一次我没有输错

由于截图没有完整,这里我输入的是Change=Change&password_new=Admin123&password_conf=Admin123&step=2&passed_captcha=Junglezt
密码更改成功
其实前面这两题可以进行CSRF攻击,直接复制源代码中的HTLM代码就可以
实际步骤这里就不演示了

High

审计源码

<?php
// 获取传入的 Change
if( isset( $_POST[ 'Change' ] ) ) {
    // 隐藏验证码
    $hide_form = true;

    // 后去两次输入的密码
    $pass_new  = $_POST[ 'password_new' ];
    $pass_conf = $_POST[ 'password_conf' ];

    // 进入验证码
    $resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key' ],
        $_POST['g-recaptcha-response']
    );
    // 判断两种成立条件 resp 验证码返回正常 或者 
    // POST g-recaptcha-response的值为hidd3n_valu3 和 $_SERVER获取 HTTP_USER_AGENT 值为 reCAPTCHA
    // $_SERVER HTTP_USER_AGENT 获取 请求头中的 User-Agent: 值
    if (
        $resp || 
        (
            $_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
            && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
        )
    ){
        // 验证码正确,判断两次输入密码是否一直
        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)) ? "" : ""));
            // 使用 md5 加密密码
            $pass_new = md5( $pass_new );

            // 更新数据库密码
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";
            $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 {
            // 两次输入密码不一致
            $html     .= "<pre>Both passwords must match.</pre>";
            $hide_form = false;
        }

    } else {
        // 验证码错误
        $html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
        return;
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// 生成反CSRF令牌,并没有看到验证user_token的地方
generateSessionToken();

?>

通过审计代码,通过验证码验证只有两种方式,验证码正常或者获取POST传参g-recaptcha-response值为hidd3n_valu3User-Agent值为reCAPTCHA
就可以绕过,所以这次我们使用burpsuite进行抓包测试

如果出现上述错误服务器返回 500 错误的话,是我们刚开始配置dvwa/config/config.inc.php文件中的两个值没有配置一样

配置一样后,或者是user_token值得问题,你需要再次抓包
再次测试


密码更改成功

Impossible

审计源码

<?php
// 判断传入的 Change
if( isset( $_POST[ 'Change' ] ) ) {
    // 检查user_token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // 隐藏二维码
    $hide_form = true;

    // 获取输入的新密码
    $pass_new  = $_POST[ 'password_new' ];
    // 去除左右两边的空
    $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 );

    // 获取第二次输入的密码
    $pass_conf = $_POST[ 'password_conf' ];
    // 去除左右两边的空
    $pass_conf = stripslashes( $pass_conf );
    $pass_conf = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_conf ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass_conf = md5( $pass_conf );

    // 获取输入的当前密码
    $pass_curr = $_POST[ 'password_current' ];
    // 去除左右两边的空
    $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 );

    // 引入 Goolge 验证码进行验证
    $resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key' ],
        $_POST['g-recaptcha-response']
    );

    // 判断验证码是否正确
    if( !$resp ) {
        // 验证码错误发生什么
        echo "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
    }
    else {
        // 使用PDO方法查看当前密码
        $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 ) ) {
            // 更新数据库密码
            $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>Either your current password is incorrect or the new passwords did not match.<br />Please try again.</pre>";
            $hide_form = false;
        }
    }
}

// 生成 CSRF user_token
generateSessionToken();

?>

验证码检测已经写死了,不能进行绕过,而且还必须输入当前的密码才可以修改密码,引入了user_token防止CSRF攻击

posted @ 2022-05-30 09:00  Junglezt  阅读(299)  评论(0编辑  收藏  举报