[Network Security] DVWA's Insecure CAPTCHA attack posture and problem-solving detailed analysis collection

[Network Security] DVWA's Insecure CAPTCHA attack posture and problem-solving detailed analysis collection

Insecure CAPTCHA

CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart, fully automated Turing test to distinguish between computers and humans) is a commonly used human-machine verification mechanism designed to prevent malicious robots or automated programs from abusing or attacking websites.

The reCAPTCHA verification process is as follows:

  1. Website Integration: The webmaster integrates the reCAPTCHA service on the website. This usually involves inserting reCAPTCHA's JavaScript code into the pages of the website and configuring relevant parameters.

  2. Verification request: When a user tries to access a protected feature or submit a form, a website sends a verification request to the reCAPTCHA server.

  3. Client-side verification: The reCAPTCHA JavaScript library loads and displays a captcha on the user's browser. The verification code can be picture verification, sound verification or inverted text verification, etc.

  4. User operation: The user needs to correctly input the characters they see or the words they hear in the corresponding input box according to the displayed verification code image or sound. Sometimes, users may also need to complete additional interactive tasks, such as dragging a slider for puzzle verification.

  5. Verification result: Once the user is verified, the reCAPTCHA JavaScript library generates a token.

  6. Token Verification: The website communicates the token with the reCAPTCHA server to verify the validity and authenticity of the token. The website sends the token and receives a response to confirm whether the user successfully passed the reCAPTCHA verification.

Insecure CAPTCHA (Insecure CAPTCHA) refers to a verification code system that has security holes or is vulnerable to attack.

Low level

insert image description here

source code

<?php

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'],
        $_POST['g-recaptcha-response']
    );

    // Did the CAPTCHA fail?
    if( !$resp ) {
    
    
        // 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;
        }
    }
}

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 = ((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 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 end user
        echo "<pre>Password Changed.</pre>";
    }
    else {
    
    
        // Issue with the passwords matching
        echo "<pre>Passwords did not match.</pre>";
        $hide_form = false;
    }

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

?> 

code audit

  1. If the user clicks the submit button (named "Change") and step is 1, then do the following:

    • Hide captcha form (hide_form=true)
    • Obtain the new password (pass_new) and confirm password (pass_conf) entered by the user
    • Call the recaptcha_check_answer() function to verify whether the verification code entered by the user is correct
    • If the captcha verification fails, display an error message and show the captcha form ($hide_form = false)
    • If the verification code verification is successful, check whether the new password entered twice is the same
      • If the passwords are the same, show the form for the next stage, prompting the user to click a button to confirm the change
      • If the passwords are not the same, show an error message, and show a captcha form ($hide_form = false)
  2. If the user clicks the submit button (named "Change") and step is 2, do the following:

    • Hide captcha form (hide_form=true)
    • Obtain the new password (pass_new) and confirm password (pass_conf) entered by the user
    • Check if the new password entered twice is the same
      • If the passwords are the same, secure the new password and update the password field in the database
      • If the passwords are not the same, show an error message, and show a captcha form ($hide_form = false)

posture

According to the code audit, you only need to set step=2, and the new password entered twice is the same, you can bypass the verification code and update the database.

Since the application defaults to step=1 every time the user submits, it is enough to capture the package and modify it

Capture packets:
insert image description here

Modify the step parameter and send the package:

insert image description here
The output shows that the password has been changed successfully.

Medium level

source code

<?php

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' ],
        $_POST['g-recaptcha-response']
    );

    // Did the CAPTCHA fail?
    if( !$resp ) {
    
    
        // 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=\"hidden\" name=\"passed_captcha\" value=\"true\" />
                    <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;
        }
    }
}

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 = ((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 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 end user
        echo "<pre>Password Changed.</pre>";
    }
    else {
    
    
        // Issue with the passwords matching
        echo "<pre>Passwords did not match.</pre>";
        $hide_form = false;
    }

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

?> 

code audit

First, when the user hits the submit button and step is 1, do the following:

  1. Hide captcha form ($hide_form = true)
  2. Obtain the new password (pass_new) and confirm password (pass_conf) entered by the user
  3. Use the recaptcha_check_answer() function to check whether the verification code entered by the user is correct
  4. If the captcha verification fails, display an error message and show the captcha form ($hide_form = false)
  5. If the verification code verification is successful, check whether the new password entered twice is the same
    • If the passwords are the same, show the form for the next stage, prompting the user to click a button to confirm the change
    • If the passwords are not the same, show an error message, and show a captcha form ($hide_form = false)

Then, when the user hits the submit button and step is 2, do the following:

  1. Hide captcha form ($hide_form = true)

  2. Obtain the new password (pass_new) and confirm password (pass_conf) entered by the user

  3. Check whether the verification code verification of the first step is passed

// 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;
    }
  1. Check if the new password entered twice is the same
    • If the passwords are the same, secure the new password and update the password field in the database
    • If the passwords are not the same, show an error message, and show a captcha form ($hide_form = false)

posture

Just bypass the program's captcha verification for the first step

Capture packets:

insert image description here
Modify the parameters and send the package:

insert image description here
The output shows that the password has been changed successfully.

High level

source code

<?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' ],
        $_POST['g-recaptcha-response']
    );

    if (
        $resp || 
        (
            $_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
            && $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
        )
    ){
    
    
        // CAPTCHA was correct. Do both new passwords match?
        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 );

            // Update database
            $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>' );

            // Feedback for user
            echo "<pre>Password Changed.</pre>";

        } else {
    
    
            // Ops. Password mismatch
            $html     .= "<pre>Both passwords must match.</pre>";
            $hide_form = false;
        }

    } else {
    
    
        // What happens when the CAPTCHA was entered incorrectly
        $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);
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

code audit

  • First, set the flag to true to hide the captcha form, then get the new password and confirmation password entered by the user.

  • Next, call the recaptcha_check_answer() function to verify whether the verification code entered by the user is correct. If the verification code is verified, or judged to be verified according to certain conditions (the hidden field value is 'hidd3n_valu3' and the user agent is 'reCAPTCHA'), the following operations are performed:

  1. Verify that the new password entered twice is the same.
    If the passwords are the same, the new password is secured by escaping it using the mysqli_real_escape_string() function and encrypting it using the md5() function.
    Update the password field of the current user in the database by executing the UPDATE statement.
    Feedback the prompt message that the user's password has been changed.

  2. If the two entered passwords are not the same, assign the error message to the $html variable and set the flag to hide the captcha form to false to redisplay the captcha form.

  • If the captcha validation fails, assign the error message to the $html variable and set the flag to hide the captcha form to false to redisplay the captcha form. At the same time, use the return keyword to end the execution of the script.

  • Finally, call the generateSessionToken() function to generate a CSRF-resistant token.

posture

Passed due to certain conditions (hidd3n_valu3' and user-agent 'reCAPTCHA')

$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'

Therefore, you can modify HTTP_USER_AGENT and add == g-recaptcha-response to bypass

insert image description here
As can be seen from the echo in the preceding figure, the password has been changed successfully.

Impossible level

source code

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

    // Check CAPTCHA from 3rd party
    $resp = recaptcha_check_answer(
        $_DVWA[ 'recaptcha_private_key' ],
        $_POST['g-recaptcha-response']
    );

    // Did the CAPTCHA fail?
    if( !$resp ) {
    
    
        // What happens when the CAPTCHA was entered incorrectly
        echo "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
        $hide_form = false;
    }
    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();

?> 

code audit

  1. First, determine if the user clicked the submit button by checking for a POST request named "Change".

  2. Next, call the checkToken() function to verify the Anti-CSRF (Cross-Site Request Forgery) token.

  3. Sets the flag to hide the captcha form to true.

  4. Get the new password, confirmation password and current password entered by the user, and do some processing:

    • Use the stripslashes() function to strip backslashes from the input.
    • Use the mysqli_real_escape_string() function to escape passwords to prevent SQL injection attacks.
    • The password is encrypted using the md5() function for enhanced security.
  5. Call the recaptcha_check_answer() function to verify whether the reCAPTCHA verification code provided by the third party is correct.

  6. If captcha validation fails, output an error message and set the flag to hide the captcha form to false so that the captcha form will be redisplayed.

  7. If the verification code is verified, proceed to the following operations:

    • Query the database to check if the current password is correct.
    • Check that the new and confirm passwords are the same, and that the current password is correct.
    • If the conditions are met, update the password field in the database to the new password.
    • Output a prompt message indicating that the password has been changed successfully.
  8. If the password change fails, output an appropriate error message and set the flag to hide the captcha form to false so that the captcha form is redisplayed.

  9. Finally, call the generateSessionToken() function to generate a new CSRF-resistant token.

Summarize

The above is a collection of [Network Security] DVWA's Insecure CAPTCHA attack posture and detailed analysis of problem solving , investigation PHP代码审计, Burp基本use and other related knowledge.

I am Qiu said , see you next time.

Guess you like

Origin blog.csdn.net/2301_77485708/article/details/131248920