Ir ao contido

Práctica 1 — Ataques de Forza Bruta sobre DVWA

Sección

Sección: Prácticas Taller
Módulo: O hacking ético nas aplicacións web
Práctica: 1 — DVWA Brute Force

Aviso legal

Todo o contido desta práctica é para uso exclusivamente educativo nun contorno de laboratorio controlado e legal. Está terminantemente prohibido aplicar estas técnicas en sistemas reais sen autorización explícita e por escrito do propietario do sistema. O uso non autorizado destas técnicas pode constituír un delito penal.


Estrutura da práctica

Esta práctica divídese en dúas partes complementarias:

Ficheiro Contido
1a — Ataque manual con hydra Resolución nos catro niveis usando hydra desde o terminal
1b — Ataque con HexStrike AI Resolución automatizada usando Claude Code + HexStrike MCP

Orde recomendada: fai primeiro a Práctica 1a para entender o mecanismo técnico, e despois a Práctica 1b para ver como a IA automatiza o mesmo proceso.


Parámetros do laboratorio

Parámetro Valor
URL DVWA http://10.0.2.100/dvwa/
Credenciais DVWA admin / password
IP Atacante (Kali) 10.0.2.250
IP Obxectivo (DVWA) 10.0.2.100
Wordlist /usr/share/wordlists/seclists/Passwords/Common-Credentials/2020-200_most_used_passwords.txt

Obxectivos de aprendizaxe

Ao rematar esta práctica o alumnado será capaz de:

  • Comprender o concepto de ataque de forza bruta contra formularios de autenticación web.
  • Entender por que é necesario autenticarse en DVWA antes de atacar a sección Brute Force.
  • Executar ataques de forza bruta con hydra nos catro niveis de DVWA.
  • Analizar o código fonte PHP vulnerable e identificar os fallos de implementación en cada nivel.
  • Comprender por que certas proteccións son insuficientes e como evolucionan de nivel en nivel.
  • Comparar o enfoque manual co enfoque automatizado mediante HexStrike AI.
  • Propoñer medidas de mitigación axeitadas para un sistema de autenticación robusto.

Conceptos previos

Que é un ataque de Forza Bruta?

Un ataque de forza bruta (brute force) contra un formulario de autenticación consiste en probar sistematicamente pares usuario/contrasinal ata atopar a combinación correcta. Este proceso repetitivo automatízase mediante ferramentas específicas e dicionarios (wordlists) con miles ou millóns de entradas, como os de SecLists ou rockyou.txt.

Tipos de estratexia de ataque

Estratexia Descrición Cando usar
Sniper Proba un dicionario nunha única posición (ex: contrasinal coñecendo o usuario) Usuario coñecido, ataque de contrasinal
Battering Ram Introduce a mesma palabra en todos os campos á vez Probar o mesmo valor en varios parámetros
Pitchfork Ataca varias posicións con dicionarios diferentes en paralelo Credential stuffing con pares coñecidos
Cluster Bomb Proba todas as combinacións posibles de varios dicionarios Enumerar usuario e contrasinal ao mesmo tempo

Nesta práctica traballaremos coa estratexia Sniper: usuario coñecido (admin) e ataque ao campo contrasinal.

Por que é necesario o PHPSESSID?

A sección de Brute Force de DVWA está dentro da zona protexida da aplicación. Calquera petición sen sesión válida é redirixida automaticamente ao login:

Sen sesión:
  GET /dvwa/vulnerabilities/brute/  →  302 Redirect → /dvwa/login.php  ✗

Con sesión válida:
  GET /dvwa/vulnerabilities/brute/
  Cookie: PHPSESSID=abc123; security=low  →  200 OK + formulario  ✓

Tanto hydra como HexStrike AI necesitan incluír a cookie de sesión PHPSESSID en cada petición para que o servidor as trate como se proviñesen dun usuario autenticado.


Login en DVWA e obtención da sesión

O login en DVWA require token CSRF

O formulario de login de DVWA inclúe un token anti-CSRF que cambia en cada petición. Sen enviar este token, o servidor ignora o POST e non inicia sesión. O mesmo ocorre co formulario de cambio de nivel en security.php. Por iso o proceso require catro pasos.

Proceso completo verificado

# Crear un ficheiro de cookies temporal
COOKIEJAR=$(mktemp /tmp/dvwa_cookies_XXXXXX.txt)

# Paso 1: GET login.php para obter o token CSRF do formulario de login
TOKEN=$(curl -s -c "$COOKIEJAR" \
  http://10.0.2.100/dvwa/login.php \
  | grep -oP "user_token.*?value='\\K[^']+")
echo "Token login: $TOKEN"

# Paso 2: POST login con credenciais + token CSRF
curl -s -b "$COOKIEJAR" -c "$COOKIEJAR" -X POST \
  http://10.0.2.100/dvwa/login.php \
  -d "username=admin&password=password&Login=Login&user_token=$TOKEN" \
  | grep -o "You have logged in as '[^']*'"
echo "PHPSESSID: $(grep PHPSESSID $COOKIEJAR | awk '{print $7}')"

# Paso 3: GET security.php para obter o token CSRF do formulario de nivel
SEC_TOKEN=$(curl -s -b "$COOKIEJAR" -c "$COOKIEJAR" \
  http://10.0.2.100/dvwa/security.php \
  | grep -oP "user_token.*?value='\\K[^']+")
echo "Token security: $SEC_TOKEN"

# Paso 4: POST security.php para cambiar o nivel (substitúe "low" polo nivel desexado)
curl -s -b "$COOKIEJAR" -c "$COOKIEJAR" -X POST \
  http://10.0.2.100/dvwa/security.php \
  -d "security=low&seclev_submit=Submit&user_token=$SEC_TOKEN" \
  | grep -oP "Security level is currently: <em>\\K[^<]+"
echo "Ficheiro de cookies: $COOKIEJAR"

Resultado esperado

Token login: 782f8ee73c56a3d0ca112a469eff9343
PHPSESSID: qr70dpmtcv5p2s1btstl0odle6
Token security: 767d507b88de00b0fb814bde12fc4c37
Ficheiro de cookies: /tmp/dvwa_cookies_ffPTsm.txt

Variable COOKIEJAR

O ficheiro de cookies só existe na sesión actual do terminal. Se abres un terminal novo debes repetir o proceso ou exportar a variable:

export COOKIEJAR="/tmp/dvwa_cookies_ffPTsm.txt"

Verificar o acceso á sección Brute Force

curl -s -b "$COOKIEJAR" \
  -o /dev/null \
  -w "HTTP Status Brute Force: %{http_code}\n" \
  http://10.0.2.100/dvwa/vulnerabilities/brute/

Resultado esperado

HTTP Status Brute Force: 200
Un 302 indica que a sesión non é válida ou que o nivel non se cambiou correctamente. Repite o proceso completo.

Cambiar o nivel entre seccións

Para cambiar de nivel entre niveis da práctica, repite os pasos 3 e 4:

# Obter token CSRF de security.php
SEC_TOKEN=$(curl -s -b "$COOKIEJAR" -c "$COOKIEJAR" \
  http://10.0.2.100/dvwa/security.php \
  | grep -oP "user_token.*?value='\\K[^']+")

# Cambiar o nivel (substitúe "medium" polo nivel desexado)
NIVEL="medium"
curl -s -b "$COOKIEJAR" -c "$COOKIEJAR" -X POST \
  http://10.0.2.100/dvwa/security.php \
  -d "security=${NIVEL}&seclev_submit=Submit&user_token=$SEC_TOKEN" \
  | grep -oP "Security level is currently: <em>\\K[^<]+"

Análise dos niveis de seguridade

NIVEL LOW — Sen ningunha medida de seguridade

O formulario usa o método GET sen protección ningunha:

GET /dvwa/vulnerabilities/brute/?username=admin&password=abc123&Login=Login HTTP/1.1
Cookie: security=low; PHPSESSID=ou50vbil75pgsn9qgrj7q929sp8

Código PHP:

<?php
if( isset( $_GET[ 'Login' ] ) ) {
    $user = $_GET[ 'username' ];    // Sen sanitización
    $pass = $_GET[ 'password' ];
    $pass = md5( $pass );

    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query);

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        echo "<p>Welcome to the password protected area {$user}</p>";
    } else {
        echo "<pre>Username and/or password incorrect.</pre>";
        // Sen retardo, sen bloqueo, sen ningunha protección
    }
}
?>

Fallos: sen sanitización, sen retardo, sen límite de intentos, sen token anti-CSRF, consulta SQL inxectable.


NIVEL MEDIUM — Retardo fixo de 2 segundos

O programador engade sleep(2) en cada intento fallido. Non impide o ataque, pero aumenta o tempo necesario. Con 200 contrasinais e 2 segundos por intento, o ataque pode tardar ata ~400 segundos no peor caso.

Código PHP:

<?php
if( isset( $_GET[ 'Login' ] ) ) {
    $user = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $_GET['username']);
    $pass = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $_GET['password']);
    $pass = md5( $pass );

    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query);

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        echo "<p>Welcome to the password protected area {$user}</p>";
    } else {
        sleep( 2 );   // ← Retardo fixo de 2 segundos
        echo "<pre>Username and/or password incorrect.</pre>";
    }
}
?>

Melloras: sanitización básica, retardo de 2s.
Problemas que persisten: retardo fixo e coñecible, sen límite de intentos, sen bloqueo, sen token anti-CSRF.


NIVEL HIGH — Token anti-CSRF por petición

O formulario pasa a usar POST e inclúe un user_token único que cambia en cada petición. Os ataques simples con hydra fallan porque non poden obter un token fresco antes de cada intento. O retardo pasa a ser aleatorio entre 0 e 3 segundos.

<!-- Token visible no código fonte da páxina -->
<input type='hidden' name='user_token' value='a3f9b2c1d4e5...' />

Código PHP:

<?php
if( isset( $_GET[ 'Login' ] ) ) {
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    $user = stripslashes( $_GET[ 'username' ] );
    $user = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user);
    $pass = stripslashes( $_GET[ 'password' ] );
    $pass = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass);
    $pass = md5( $pass );

    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query);

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        echo "<p>Welcome to the password protected area {$user}</p>";
    } else {
        sleep( rand(0, 3) );   // ← Retardo aleatorio 0-3s
        echo "<pre>Username and/or password incorrect.</pre>";
    }
}
generateSessionToken();   // ← Novo token para a seguinte petición
?>

Melloras: token anti-CSRF por petición, retardo aleatorio, mellor sanitización.
Problemas que persisten: sen límite de intentos, sen bloqueo de conta. O token pode extraerse automaticamente cun script Python.


NIVEL IMPOSSIBLE — Sen vulnerabilidade explotable

Implementa todas as capas de protección necesarias. O bloqueo de conta tras 3 intentos fallidos fai o ataque de forza bruta impracticable.

Código PHP:

<?php
if( isset( $_POST[ 'Login' ] ) ) {
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    $user = stripslashes( $_POST[ 'username' ] );
    $user = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user);
    $pass = stripslashes( $_POST[ 'password' ] );
    $pass = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass);
    $pass = md5( $pass );

    // Prepared statement — elimina inxección SQL
    $data = $db->prepare('SELECT failed_login, last_login FROM users
                          WHERE user = (:user) LIMIT 1;');
    $data->bindParam(':user', $user, PDO::PARAM_STR);
    $data->execute();
    $row = $data->fetch();

    // Bloqueo de conta: 3 fallos → 15 minutos
    $account_locked = false;
    if( $row['failed_login'] >= 3 &&
        $row['last_login'] + (15 * 60) > time() ) {
        $account_locked = true;
    }

    $data = $db->prepare('SELECT * FROM users WHERE user = (:user)
                          AND password = (:password) LIMIT 1;');
    $data->bindParam(':user',     $user, PDO::PARAM_STR);
    $data->bindParam(':password', $pass, PDO::PARAM_STR);
    $data->execute();

    if( !$account_locked && $data->rowCount() == 1 ) {
        $db->prepare('UPDATE users SET failed_login = "0"
                      WHERE user = (:user) LIMIT 1;')
           ->execute([':user' => $user]);
        echo "<p>Welcome to the password protected area {$user}</p>";
    } else {
        sleep( rand(2, 4) );   // ← Retardo aleatorio 2-4s
        echo "<pre>Username and/or password incorrect.</pre>";
        $db->prepare('UPDATE users SET failed_login = (failed_login + 1)
                      WHERE user = (:user) LIMIT 1;')
           ->execute([':user' => $user]);
    }
}
generateSessionToken();
?>

Medidas de seguridade implementadas:

Medida Implementación
Token anti-CSRF checkToken() — token único por sesión e petición
Prepared statements PDO::prepare() + bindParam() — elimina SQLi
Sanitización completa stripslashes() + mysqli_real_escape_string()
Retardo aleatorio sleep(rand(2,4)) — dificulta ataques temporais
Bloqueo de conta 3 fallos → bloqueo de 15 minutos
Contador de fallos Persistente en base de datos
Método POST Sen parámetros visibles na URL

Comparativa de niveis

Nivel Retardo Bloqueo Token CSRF Prepared Stmt Explotable?
Low ❌ Non ❌ Non ❌ Non ❌ Non ✅ Si (trivial)
Medium ⚠️ 2s fixos ❌ Non ❌ Non ❌ Non ✅ Si (lento)
High ⚠️ 0-3s aleatorio ❌ Non ✅ Si ❌ Non ✅ Si (script)
Impossible ✅ 2-4s aleatorio ✅ Si (3/15min) ✅ Si ✅ Si ❌ Non

Medidas de mitigación recomendadas

1. Bloqueo de contas — Tras 3-5 fallos, bloquear temporalmente a conta.

2. Rate limiting — Retardos aleatorios e límite de intentos por IP nun período de tempo.

3. Política de contrasinais — Contrasinais longos e complexos verificados contra bases de datos de contrasinais comprometidos (HaveIBeenPwned).

4. MFA — Segundo factor de autenticación (TOTP, FIDO2, SMS).

5. CAPTCHA — Retos para distinguir humanos de bots, especialmente tras varios fallos.

6. Tokens anti-CSRF — Token único por sesión e petición.

7. Prepared Statements — PDO con bindParam() para eliminar completamente a inxección SQL.

8. Logging e monitorización — Rexistrar fallos e alertar ante patróns anómalos.

9. WAF — Firewall de aplicación web que detecte e bloquee patróns de brute force.


Glosario

Termo Definición
Brute Force Ataque que proba sistematicamente combinacións de credenciais ata atopar a correcta
Wordlist Dicionario de contrasinais usados en ataques de forza bruta
PHPSESSID Identificador de sesión PHP gardado nunha cookie do navegador
CSRF Cross-Site Request Forgery — ataque que fai que un usuario autenticado execute accións non desexadas nun sitio web. O token anti-CSRF prevén este ataque verificando que cada petición provén do formulario lexítimo da aplicación
Token anti-CSRF Valor único e secreto incluído en formularios para previr ataques automatizados
Rate Limiting Limitación do número de peticións permitidas nun período de tempo
Prepared Statement Consulta SQL parametrizada que separa código de datos, prevenindo inxección SQL
MFA Multi-Factor Authentication — autenticación con dous ou máis factores independentes
hydra Cracker de login en rede con módulos para HTTP, SSH, FTP e outros protocolos
http-get-form Módulo de hydra para atacar formularios web con método GET
WAF Web Application Firewall — firewall específico para protexer aplicacións web
Credential stuffing Ataque que usa pares usuario/contrasinal filtrados de brechas anteriores
MCP Model Context Protocol — protocolo que conecta Claude con ferramentas externas
HexStrike AI Servidor MCP con 150+ ferramentas de ciberseguridade para Kali Linux

Referencias