Exploiting Authentication Weaknesses in DVWA: A Brute Force Attack Methodology

Low Security Level

The authentication endpoint lacks both input validation and rate-limiting mechanisms. Credentials transmit via insecure GET requests and concatenate directly into SQL queries without sanitization.

<?php
if (isset($_GET['Login'])) {
    $input_user = $_GET['username'];
    $input_pass = $_GET['password'];
    $hash_pass = md5($input_pass);
    
    $sql = "SELECT * FROM `users` WHERE user = '$input_user' AND password = '$hash_pass';";
    $response = mysqli_query($GLOBALS["___mysqli_ston"], $sql);
    
    if ($response && mysqli_num_rows($response) == 1) {
        $data = mysqli_fetch_assoc($response);
        $img = $data["avatar"];
        $html .= "<p>Welcome to the password protected area {$input_user}</p>";
        $html .= "<img src=\"{$img}\" />";
    } else {
        $html .= "<br />Username and/or password incorrect.";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

Intercept the login request using a proxy tool. Forward the request to the Intruder module and select Sniper attack mode. Designate the password parameter as the payload position. Configure a wordlist containing potential passwords and execute the attack. Successful authentication returns a distinct response length and redirects to the protected area.

Medium Security Level

This tier implements mysqli_real_escape_string to neutralize SQL injection attempts while introducing a fixed temporal delay upon validation failure.

<?php
if (isset($_GET['Login'])) {
    $input_user = $_GET['username'];
    $input_user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_user) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    
    $input_pass = $_GET['password'];
    $input_pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_pass) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    $input_pass = md5($input_pass);
    
    $sql = "SELECT * FROM `users` WHERE user = '$input_user' AND password = '$input_pass';";
    $response = mysqli_query($GLOBALS["___mysqli_ston"], $sql);
    
    if ($response && mysqli_num_rows($response) == 1) {
        $data = mysqli_fetch_assoc($response);
        $img = $data["avatar"];
        $html .= "<p>Welcome to the password protected area {$input_user}</p>";
        $html .= "<img src=\"{$img}\" />";
    } else {
        sleep(2);
        $html .= "<br />Username and/or password incorrect.";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

The two-second delay between failed attempts reduces attack throughput to thirty requests per minute. While this impedes rapid credential enumeration, the absence of account lockout mechanisms permits extended brute force campaigns to eventually succeed.

High Security Level

This implementation incorporates Anti-CSRF token validation and randomized response delays to complicate automated exploitation.

<?php
if (isset($_GET['Login'])) {
    checkToken($_REQUEST['user_token'], $_SESSION['session_token'], 'index.php');
    
    $input_user = $_GET['username'];
    $input_user = stripslashes($input_user);
    $input_user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_user) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    
    $input_pass = $_GET['password'];
    $input_pass = stripslashes($input_pass);
    $input_pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_pass) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    $input_pass = md5($input_pass);
    
    $sql = "SELECT * FROM `users` WHERE user = '$input_user' AND password = '$input_pass';";
    $response = mysqli_query($GLOBALS["___mysqli_ston"], $sql);
    
    if ($response && mysqli_num_rows($response) == 1) {
        $data = mysqli_fetch_assoc($response);
        $img = $data["avatar"];
        $html .= "<p>Welcome to the password protected area {$input_user}</p>";
        $html .= "<img src=\"{$img}\" />";
    } else {
        sleep(rand(0, 3));
        $html .= "<br />Username and/or password incorrect.";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
generateSessionToken();
?>

Configure the attack tool to use Pitchfork mode, targeting both the password and user_token parameters simultaneously. Navgiate to the Options module and enable Grep-Extract functionality. Define a regular expression to capture the user_token value from server responses. Set the thread pool to single-threaded execution to maintain session state integrity. Load the password dictionary in the first payload position and configure the second position to use the recursive grep extraction method, ensuring each request utilizes the token from the previous response.

Impossible Security Level

This configuration employs parameterized queries, account lockout mechanisms, and persistent failure tracking to mitigate brute force attacks.

<?php
if (isset($_POST['Login'])) {
    checkToken($_REQUEST['user_token'], $_SESSION['session_token'], 'index.php');
    
    $input_user = $_POST['username'];
    $input_user = stripslashes($input_user);
    $input_user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_user) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    
    $input_pass = $_POST['password'];
    $input_pass = stripslashes($input_pass);
    $input_pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) 
        ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $input_pass) 
        : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call!", E_USER_ERROR)) ? "" : ""));
    $input_pass = md5($input_pass);
    
    $threshold = 3;
    $lockout_minutes = 15;
    $locked = false;
    
    $stmt = $db->prepare('SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;');
    $stmt->bindParam(':user', $input_user, PDO::PARAM_STR);
    $stmt->execute();
    $row = $stmt->fetch();
    
    if ($stmt->rowCount() == 1 && $row['failed_login'] >= $threshold) {
        $last = strtotime($row['last_login']);
        $unlock = strtotime("+{$lockout_minutes} minutes", $last);
        if (time() < $unlock) {
            $locked = true;
        }
    }
    
    $stmt = $db->prepare('SELECT * FROM users WHERE user = (:user) AND password = (:pass) LIMIT 1;');
    $stmt->bindParam(':user', $input_user, PDO::PARAM_STR);
    $stmt->bindParam(':pass', $input_pass, PDO::PARAM_STR);
    $stmt->execute();
    $row = $stmt->fetch();
    
    if ($stmt->rowCount() == 1 && !$locked) {
        $img = $row['avatar'];
        $html .= "<p>Welcome to the password protected area <em>{$input_user}</em></p>";
        $html .= "<img src=\"{$img}\" />";
        
        $reset = $db->prepare('UPDATE users SET failed_login = 0 WHERE user = (:user) LIMIT 1;');
        $reset->bindParam(':user', $input_user, PDO::PARAM_STR);
        $reset->execute();
    } else {
        sleep(rand(2, 4));
        $html .= "<br />Username and/or password incorrect.<br /><br/>Account locked due to excessive failures. Retry in {$lockout_minutes} minutes.";
        
        $increment = $db->prepare('UPDATE users SET failed_login = failed_login + 1 WHERE user = (:user) LIMIT 1;');
        $increment->bindParam(':user', $input_user, PDO::PARAM_STR);
        $increment->execute();
    }
    
    $timestamp = $db->prepare('UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;');
    $timestamp->bindParam(':user', $input_user, PDO::PARAM_STR);
    $timestamp->execute();
}
generateSessionToken();
?>

The defense-in-depth strategy combines multiple protection layers: prepared statements eliminate SQL injection vectors, the account lockout triggers after three consecutive failures, and the fifteen-minute suspension window effectively neutralizes automated credential stuffing attacks.

Tags: DVWA brute force Web Security Authentication Bypass SQL Injection

Posted on Sun, 18 Oct 2026 16:24:53 +0000 by raccer