【DVWA】 CSRF

【DVWA】 CSRF



Uno, nivel bajo


1. Proceso de prueba

imagen-20210303170012898

Como se muestra en la figura, la contraseña se puede cambiar ingresando la nueva contraseña una vez y confirmando la nueva contraseña una vez, sin pedir la contraseña anterior para su verificación.

Usa eructos para capturar el paquete y echa un vistazo:

imagen-20210303170320927

Se encuentra que es una solicitud de obtención y no hay token, por lo que obviamente existe una vulnerabilidad CSRF.

Forjar un enlace de solicitud:

http://222.24.28.118/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change

Cambie los valores de los parámetros password_new y password_conf a 123456, y luego envíe este enlace al objetivo del ataque. Si el objetivo ha iniciado sesión en este sitio web y hace clic en este enlace, la contraseña del usuario se restablecerá a 123456

Los usuarios simulados hacen clic en el enlace:

Inicie sesión en DVWA en la máquina virtual y configure el nivel de seguridad en bajo, luego visite:

http://222.24.28.118/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change

imagen-20210303171451671

Como se muestra en la figura, se ha cambiado la contraseña.

Vale la pena señalar que el usuario debe acceder al enlace con el navegador conectado a este sitio web para que el ataque tenga éxito. Debido a que el backend verificará el valor de la cookie, si cambia el navegador para solicitar el paquete de datos, no se obtendrá la cookie y no se podrá verificar la cookie del backend, por lo que el salto a la página de inicio de sesión no será válido. Parece que la clave de un ataque CSRF es utilizar la cookie de la víctima para enviar una solicitud falsificada al servidor.

Sin embargo, este método de ataque es muy simple y el usuario puede ver directamente que el enlace es un enlace para cambiar la contraseña y estar preparado.

En realidad, los atacantes a menudo crean un sitio primero y luego cargan un documento html que contiene enlaces maliciosos. Después de enviar la dirección de este documento html al usuario, una vez que el usuario haga clic, el enlace malicioso se cargará automáticamente para completar el ataque.

imagen-20210303181038355 imagen-20210303181010231

2. Análisis del código fuente

<?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)) ? "" : ""));  //防SQL注入
        $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);
}

?> 

Se puede ver que el backend solo verifica si el parámetro de cambio está establecido y si password_new y password_conf son iguales para emitir un juicio, pero no toma ninguna medida para evitar CSRF.



En segundo lugar, el nivel medio


1. Proceso de prueba

De acuerdo con el método anterior, hicimos una prueba y descubrimos que se informó un error:

imagen-20210303173032730

Veamos directamente el código fuente.

2. Análisis del código fuente

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    
    
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
    
     //检测Referer中是否有host
        // 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);
}

?> 

stripos(strs, str)Función: Devuelve la posición del carácter str en la cadena strs

HTTP_REFERER representa el campo de referencia en el paquete de datos (que representa el enlace de origen del paquete de datos), y SERVER_NAME representa el host en el paquete de datos (la dirección del host al que se accede), por lo que el backend verificará si hay un host en el referer, y pasará si hay uno.

Omita la idea : cambie directamente el nombre del documento html producido en el nivel bajo a [host] .html, de modo que haya un host en el referente, y una vez que el usuario haga clic en él, el ataque se omitirá y el ataque ¡es exitoso!

imagen-20210303184312319

Tres, alto nivel


### 1. Proceso de prueba

Visite el enlace malicioso e informe que el token CSRF de error es incorrecto.

Parece que el token se usa para prevenir

imagen-20210303184934522

2. Análisis del código fuente

<?php

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

    // 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);
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

El mecanismo de token Anti-csrf se agrega al nivel alto, que es implementado por la función checkToken. Cada vez que el usuario accede a la página de cambio de contraseña, el servidor devolverá un token aleatorio. Después de eso, debe obtener el user_token devuelto por el servidor antes de realizar una solicitud al servidor. User_token omite la verificación.

Puede utilizar un rastreador de tokens CSRF de eructo:

imagen-20210303215241378

Establecer host y nombre

imagen-20210303215406694

Luego, solo toma el paquete y reprodúcelo.


Cuatro, nivel imposible


Análisis de código fuente

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

?> 

El nivel imposible también utiliza el mecanismo de token Anti-CSRF, que previene eficazmente los ataques CSRF, y utiliza técnicas de preprocesamiento y parametrización de PDO para evitar la inyección de SQL.


Cinco, sugerencias de prevención


  • Verifique el valor de Referer de la solicitud . Si Referer es el nombre de dominio que comienza con su propio sitio web, significa que la solicitud proviene del sitio web en sí y es legal. Si Referer es el nombre de dominio de otro sitio web o está en blanco, puede ser un ataque CSRF y el servidor debe rechazar la solicitud. Solicitud, pero este método puede omitirse.
  • La configuración de un mecanismo de token dificulta la falsificación de solicitudes.

Supongo que te gusta

Origin blog.csdn.net/qq_43665434/article/details/114338347
Recomendado
Clasificación