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

CSRF

CSRF (Cross-Site Request Forgery, cross-site request forgery) is a common web application security vulnerability, which takes advantage of the user's identity in an authenticated website and deceives the user to initiate an unexpected request.The attacker will construct a malicious webpage, so that when the user visits the webpage in the browser, a request that is not authorized by the user is automatically sent to the target website.

The principle of CSRF attack is to use the web application's trust in user requests, the attacker constructs a malicious request and induces the user to trigger it, so as to achieve the purpose of the attack.

Common CSRF attacks include modifying user passwords, sending emails, and transferring funds.

Low level

insert image description here

source code

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    
    
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

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

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

?> 

code audit

  1. $_GET['Change']Existence is first checked to see if there is a password change request.
  2. The code gets the new password entered and the confirmation password.
  3. Determine whether the new password and the confirmation password match.
  4. If it matches, the new password is processed:
    • mysqli_real_escape_stringFunction used to database escape new passwords to prevent SQL injection attacks.
    • md5The function is used to encrypt the new password with MD5 hash.
  5. A statement to update a user's password in the database is constructed, and the update operation is performed.
  6. Provide corresponding feedback information to the user according to the operation result.

dvwaCurrentUserAdditionally, a function called in the code returns the username of the current user.

posture

Enter 1 respectively, and the echo is as follows:

insert image description here
Discovery parameters are submitted in GET mode

So the parameter can be modified as password_new=2&password_conf=2

insert image description here
Open the link again:

insert image description here
It can be seen from the page echo that if the password is changed successfully,
it means that CSRF has been successfully implemented.

Medium level

source code

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    
    
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
    
    
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords 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 the 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 user
            echo "<pre>Password Changed.</pre>";
        }
        else {
    
    
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
    
    
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }

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

?> 

code audit

  1. First, the code checks to see if a GET parameter named is set Changeto see if there is a password change request.
  2. The code uses stripos()a function to check that the origin of the request is on the same server to ensure that the request is from a trusted source.
  3. If the source of the request is legitimate, get the input of the new password and confirm the password.
  4. Check that the new and confirmed passwords match.
  5. If the passwords match, the new password is processed:
    • First, use mysqli_real_escape_string()a function to database-escape the new password to prevent SQL injection attacks.
    • Then, use md5()the function to MD5 hash the new password. Note that MD5 is no longer considered a secure hashing algorithm.
  6. Construct the SQL statement to update the user's password in the database, and execute the update operation.
  7. Provide corresponding feedback information to the user according to the operation result.

dvwaCurrentUser()Additionally, a function called in the code returns the username of the current user.

posture

Different from the Low level, this statement is added to the source code of the Medium level

stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) 

即判断 HTTP_REFERER中是否包含SERVER_NAME,即检查请求来源是否在同一服务器上。

HTTP_REFERER is the Referer parameter value, that is, the source address
SERVER_NAME is the host parameter and the host ip name


Idea: dvwa的www目录下Write an html file that contains a CSRF link, capture the packet so that the request packet includes the file


So write an html file and name it host address.html

The content is:

<img src=“http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=2&password_conf=2&Change=Change#” border=“0” style=“display:none;”/>

insert image description here
The host address can be entered cmdin ipconfigto get

Modify parameters after packet capture:

insert image description here

After sending the package, you can see from the echo on the right that the password has been changed successfully.

High level

source code

<?php

$change = false;
$request_type = "html";
$return_message = "Request Failed";

if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
    
    
    $data = json_decode(file_get_contents('php://input'), true);
    $request_type = "json";
    if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
        array_key_exists("password_new", $data) &&
        array_key_exists("password_conf", $data) &&
        array_key_exists("Change", $data)) {
    
    
        $token = $_SERVER['HTTP_USER_TOKEN'];
        $pass_new = $data["password_new"];
        $pass_conf = $data["password_conf"];
        $change = true;
    }
} else {
    
    
    if (array_key_exists("user_token", $_REQUEST) &&
        array_key_exists("password_new", $_REQUEST) &&
        array_key_exists("password_conf", $_REQUEST) &&
        array_key_exists("Change", $_REQUEST)) {
    
    
        $token = $_REQUEST["user_token"];
        $pass_new = $_REQUEST["password_new"];
        $pass_conf = $_REQUEST["password_conf"];
        $change = true;
    }
}

if ($change) {
    
    
    // Check Anti-CSRF token
    checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
    
    
        // They do!
        $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert );

        // Feedback for the user
        $return_message = "Password Changed.";
    }
    else {
    
    
        // Issue with passwords matching
        $return_message = "Passwords did not match.";
    }

    mysqli_close($GLOBALS["___mysqli_ston"]);

    if ($request_type == "json") {
    
    
        generateSessionToken();
        header ("Content-Type: application/json");
        print json_encode (array("Message" =>$return_message));
        exit;
    } else {
    
    
        echo "<pre>" . $return_message . "</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

code audit

  1. First, the code checks that the request is a POST method and has a content type of application/json. If the conditions are met, the request will be parsed into JSON format and the corresponding parameters will be obtained.
  2. If the JSON request condition is not met, the parameters of the non-JSON request are checked.
  3. If the conditions for password change are met, do the following:
    • Check for Anti-Cross-Site Request Forgery (Anti-CSRF) tokens.
    • Check that the new and confirmed passwords match.
    • Database escape and MD5 hash encryption of new passwords.
    • Construct the SQL statement to update the user's password in the database, and execute the update operation.
    • Provide corresponding feedback information to the user according to the operation result.
  4. Close the database connection.
  5. Based on the request type, a new Anti-CSRF token is generated and a response is returned.

posture

From the code audit:

The generateSessionToken() function is used to generate a random token and store it in the session (session), ensuring that each user has a unique token.

The checkToken() function is used to verify that the token passed to the server matches the token stored in the session. This function is called before handling password change requests to ensure only legitimate requests are processed.

Through these two steps, the server will first verify the validity of the token, and only after the verification is successful will the user's password change request be processed.

Therefore, you should obtain the user_token returned by the server before initiating the request, and then use the user_token to bypass the verification.

method 1

Install in BurpCSRF Token Tracker

insert image description here

Add the host name, the token name obtained by capturing the packet is the token value
insert image description here
, and then capture the packet again, replay the packet, and modify the password arbitrarily, and the token value in the plug-in will be automatically updated.

method 2

Use DVWA xss(stored)to realize token pop-up.

Enter 1, 1 in the xss(stored) page and capture the package

insert image description here
Modify the textName parameter to<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>

At this time, put the package and close the interception, and the token will pop up on the page:80edcbac43cd9594f29999a0692a608f3

insert image description here
Then grab the package of the CSRF page:

insert image description here
Modify the password and token, and release the package:

insert image description here

The page echo shows that the password has been changed successfully.

Impossible level

source code

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    
    
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Sanitise current password input
    $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 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 passwords match and does the current password match the user?
    if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
    
    
        // It does!
        $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 );

        // Update database with new password
        $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 user
        echo "<pre>Password Changed.</pre>";
    }
    else {
    
    
        // Issue with passwords matching
        echo "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

code audit

  1. First, the code checks $_GET['Change']to see if there is a password change request.
  2. Next, the code calls checkTokenthe function to validate the Anti-CSRF token. checkTokenThe function will compare the match between the request user_tokenand the session session_tokento confirm the legitimacy of the request.
  3. The code obtains the current password, new password and confirmation password entered, and performs a series of processing on the current password:
    • stripslashesFunction to remove backslashes from the current password.
    • mysqli_real_escape_stringThe function is used to perform database escape on the current password to prevent SQL injection attacks.
    • md5The function is used to perform MD5 hash encryption on the current password.
  4. The code performs a database query to check that the current password entered by the user is correct.
  5. If the new password and the confirmed password match, and the current password is correct, update the password in the database with the new password.
  6. Finally, output corresponding feedback information according to the operation result.

Additionally, two additional functions are called in the code:

  • generateSessionTokenFunction to generate and set the Anti-CSRF token to ensure that every time the form is rendered a new token is generated and stored in the session.
  • dvwaCurrentUserThe function returns the username of the current user.

Summarize

The above is[Network Security] DVWA's CSRF attack posture and problem-solving detailed analysis collection, Investigation CSRF, Burp使用and PHP代码审计other relevant knowledge.

I am Qiu said , see you next time.

Guess you like

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