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();

?>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM