Ir ao contido

Informe Comparativo — Brute Force DVWA (Low → Medium → High → Impossible)

Data: 2026-04-26
Contorno: Laboratorio controlado — DVWA 2.x en http://10.0.2.100
Clasificación: Educativo / CTF


1. Táboa Resumo

Nivel Token CSRF Retardo fallo Bloqueo conta Consultas PDO Explotable Técnica usada
Low Hydra http-get-form directo
Medium 2 s fixos Hydra + -t 1 -w 3 adaptado ao retardo
High ✓ (por sesión) 2 s fixos Script Python: GET token → POST creds
Impossible ✓ (por petición) 2–4 s aleatorio ✓ (3 fallos / 15 min) Non explotable

2. Análise por Nivel Explotable

2.1 Nivel Low

Fallo principal: Ausencia total de proteccións anti-brute-force (CWE-307) e sen token CSRF (CWE-352).

Técnica: Hydra con http-get-form e cookie de sesión. O servidor responde instantaneamente a cada petición.

hydra -l admin -P wordlist.txt 10.0.2.100 \
  http-get-form '/dvwa/vulnerabilities/brute/: \
  username=^USER^&password=^PASS^&Login=Login: \
  H=Cookie\: PHPSESSID=...; security=low: \
  Username and/or password incorrect' -t 16

Resultado: Contrasinal password atopado en < 1 s.

CWEs relevantes:
- CWE-307 — Improper Restriction of Excessive Authentication Attempts
- CWE-352 — Cross-Site Request Forgery (Missing CSRF Token)
- CWE-521 — Weak Password Requirements (contrasinal trivial)

CVEs ilustrativos da mesma clase:

CVE Produto Descrición
CVE-2019-5420 Ruby on Rails (activestorage) Brute force sen límite en endpoint de autenticación
CVE-2021-22175 GitLab CE/EE Sen rate-limiting en API de autenticación
CVE-2020-15782 Siemens SCALANCE Autenticación HTTP sen bloqueo de conta

Puntuación CVSS v3.1:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Métrica Valor Razón
Attack Vector Network (N) Accesible vía HTTP
Attack Complexity Low (L) Sen condición especial
Privileges Required None (N) Non require conta previa
User Interaction None (N) Totalmente automatizable
Confidentiality High (H) Credenciais comprometidas
Integrity High (H) Toma de conta completa
Availability None (N) Sen impacto de dispoñibilidade

Base Score: 9.1 — CRÍTICO


2.2 Nivel Medium

Fallo principal: O sleep(2) reduce a velocidade do ataque pero non o impide. Segue sen CSRF nin bloqueo (CWE-307 + CWE-208).

Técnica: Hydra con -t 1 (un fío) para respectar o retardo fixo. O formulario acepta o mesmo token durante toda a sesión.

hydra -l admin -P wordlist.txt 10.0.2.100 \
  http-get-form '.../brute/: \
  username=^USER^&password=^PASS^&Login=Login: \
  H=Cookie\: PHPSESSID=...; security=medium: \
  Username and/or password incorrect' -t 1 -w 3

Resultado: Contrasinal password atopado en 8 s (4.º intento × ~2 s/intento).
Tempo máximo wordlist completa (197 entradas): ~394 s (~6,5 min).

CWEs relevantes:
- CWE-307 — Sin bloqueo de conta
- CWE-208 — Observable Timing Discrepancy (retardo fixo e predecible)
- CWE-352 — Sen token CSRF

CVEs ilustrativos:

CVE Produto Descrición
CVE-2019-7642 Mautic Brute force con retardo fixo contornable con fíos únicos
CVE-2022-22947 Spring Cloud Gateway Rate-limiting incompleto en rutas protexidas
CVE-2021-43798 Grafana Path traversal + auth sen lockout

Puntuación CVSS v3.1:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Base Score: 9.1 — CRÍTICO
Nota: O retardo de 2 s non reduce a puntuación base de CVSS (non é un control de seguridade efectivo); só incrementa o tempo de explotación.


2.3 Nivel High

Fallo principal: O token CSRF rénvase por sesión PHP, non por petición. Un atacante que obtén un token válido pode reutilizalo durante toda a sesión (CWE-352 parcial). Ademais non hai bloqueo de conta (CWE-307).

Técnica: Script Python con ciclo GET→POST: obtén un token fresco antes de cada intento de contrasinal, mantendo a mesma sesión PHP.

def attack_step(session, password):
    # 1. GET fresco para token actual da sesión
    token = get_token(session)          # extrae user_token do HTML
    # 2. POST con ese token válido
    return try_password(session, password, token)

Resultado: Contrasinal password atopado en 9 s (4.º intento × ~2.3 s/intento).

CWEs relevantes:
- CWE-307 — Sin bloqueo de conta
- CWE-352 — Token CSRF ligado á sesión, non á transacción
- CWE-613 — Insufficient Session Expiration

CVEs ilustrativos:

CVE Produto Descrición
CVE-2019-3396 Atlassian Confluence CSRF en formularios de autenticación sen token por-petición
CVE-2020-17519 Apache Flink Token de sesión reutilizable en operacións sensibles
CVE-2022-0540 Atlassian Jira Bypass de CSRF en determinados endpoints de auth

Puntuación CVSS v3.1:

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Métrica Valor Razón
Attack Complexity High (H) Require xestión de estado (token CSRF fresco por petición)
Outros igual Low/Medium

Base Score: 7.4 — ALTO


2.4 Nivel Impossible

Non explotable. As defensas en capas impiden calquera ataque automatizado:

  1. Token CSRF por transaccióngenerateSessionToken() tras cada POST invalida o token anterior
  2. Bloqueo tras 3 fallos → persistido en BD; 15 min de espera
  3. Bloqueo activo con clave correcta → nin o contrasinal real funciona mentres a conta está bloqueada
  4. Retardo aleatoriosleep(rand(2,4)) impide análise de timing
  5. Mensaxe de erro idéntica → "Username and/or password incorrect" tanto para fallo como para bloqueo (anti-enumeración)
  6. PDO con parámetros ligados → inmune a inxección SQL

Puntuación CVSS v3.1:

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N

Base Score: 0.0 — NINGÚN


3. Comparativa de Puntuacións CVSS v3.1

Nivel         CVSS Vector                                     Puntuación  Severidade
─────────────────────────────────────────────────────────────────────────────────────
Low           AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N           9.1         CRÍTICO
Medium        AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N           9.1         CRÍTICO
High          AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N           7.4         ALTO
Impossible    AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N           0.0         NINGÚN
  9 │ ██████████  ██████████
  8 │
  7 │                         ████████
  6 │
  5 │
  4 │
  3 │
  2 │
  1 │
  0 │                                    ──
    └────────────────────────────────────────
         Low      Medium     High    Impossible

4. Top 5 Recomendacións de Remediación (por impacto)

R1 — Implementar bloqueo de conta con backoff exponencial ⚡ CRÍTICO

Impacto: Elimina a viabilidade práctica de calquera ataque de forza bruta.

// PHP — exemplo de implementación
$max_intentos  = 5;
$bloqueo_base  = 30;   // segundos
$multiplicador = 2;    // backoff exponencial

$intentos = $cache->get("bf:{$ip}:{$usuario}") ?? 0;
if ($intentos >= $max_intentos) {
    $espera = $bloqueo_base * pow($multiplicador, $intentos - $max_intentos);
    http_response_code(429);
    header("Retry-After: {$espera}");
    exit("Too Many Requests");
}

Complementar con:
- Bloqueo por IP E por usuario (evitar lockout selectivo)
- Notificación ao usuario por email cando se bloquea a súa conta
- CAPTCHA progresivo (activar tras 2–3 fallos)


R2 — Token CSRF por transacción (non por sesión) ⚡ CRÍTICO

Impacto: Invalida todos os ataques automatizados que non xestionen estado HTTP completo.

// Xerar token novo ANTES de servir o formulario
function generateTransactionToken(): string {
    $token = bin2hex(random_bytes(32));
    $_SESSION['csrf_token']    = $token;
    $_SESSION['csrf_token_ts'] = time();   // TTL de 5 min
    return $token;
}

// Validar: token correcto + non caducado + consumir (one-time use)
function validateTransactionToken(string $token): bool {
    $valid = hash_equals($_SESSION['csrf_token'] ?? '', $token)
          && (time() - ($_SESSION['csrf_token_ts'] ?? 0)) < 300;
    unset($_SESSION['csrf_token'], $_SESSION['csrf_token_ts']); // one-time
    return $valid;
}

R3 — Cabeceiras HTTP de seguridade en todas as respostas 🔴 ALTO

Impacto: Mitiga clickjacking, XSS reflectido, sniffing de MIME e fixación de sesión.

Estado actual do servidor (confirmado por laboratorio):

# PRESENTES (ben configuradas)
Set-Cookie: PHPSESSID=...; HttpOnly; SameSite=Strict    ✓
Cache-Control: no-cache, must-revalidate                 ✓

# AUSENTES — deben engadirse en Apache /etc/apache2/conf-enabled/security.conf:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()

Configuración Apache recomendada:

# /etc/apache2/conf-enabled/security-headers.conf
<IfModule mod_headers.c>
    Header always set X-Frame-Options "DENY"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"
    Header always set Content-Security-Policy \
        "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; \
         img-src 'self' data:; frame-ancestors 'none'; form-action 'self'"
    # Ocultar versión do servidor
    ServerTokens Prod
    ServerSignature Off
</IfModule>

Set-Cookie óptimo para formulario de login:

Set-Cookie: PHPSESSID=<valor>; 
            Path=/; 
            HttpOnly; 
            Secure; 
            SameSite=Strict; 
            Max-Age=3600

⚠️ Actualmente falta Secure — debe engadirse cando se despregue con HTTPS.


R4 — Consultas parametrizadas con PDO en todos os niveis 🔴 ALTO

Impacto: Elimina SQL Injection en paralelo co brute force (niveles Low–High usan concatenación ou mysqli_real_escape_string).

// ✗ INSEGURO (Low/Medium): string concatenation
$query = "SELECT * FROM users WHERE user='$user' AND password='$pass'";

// ✓ SEGURO (Impossible): PDO preparado
$stmt = $db->prepare('SELECT * FROM users WHERE user = :user AND password = :pass LIMIT 1');
$stmt->bindParam(':user', $user, PDO::PARAM_STR);
$stmt->bindParam(':pass', $hash, PDO::PARAM_STR);
$stmt->execute();

R5 — Retardo aleatorio + log de eventos de autenticación 🟡 MEDIO

Impacto: Dificulta ataques de timing e permite detección e resposta (SIEM/alertas).

// Retardo aleatorio en fallo (igual que Impossible)
sleep(random_int(2, 5));

// Log estruturado para SIEM
error_log(json_encode([
    'event'    => 'auth_failure',
    'user'     => $usuario,
    'ip'       => $_SERVER['REMOTE_ADDR'],
    'ua'       => $_SERVER['HTTP_USER_AGENT'],
    'ts'       => date('c'),
    'attempt'  => $intentos_fallidos,
]));

Complementar con fail2ban:

# /etc/fail2ban/filter.d/dvwa-brute.conf
[Definition]
failregex = ^.*"event":"auth_failure".*"ip":"<HOST>".*$
ignoreregex =

# /etc/fail2ban/jail.d/dvwa.conf
[dvwa-brute]
enabled  = true
filter   = dvwa-brute
logpath  = /var/log/apache2/error.log
maxretry = 5
bantime  = 3600
findtime = 300

5. Exemplo Completo de Cabeceiras HTTP Seguras

5.1 Cabeceiras actuais do servidor (confirmadas)

HTTP/1.1 200 OK
Server: Apache/2.4.52 (Ubuntu)              ← ⚠️ Revela versión e SO
Set-Cookie: security=impossible; path=/; HttpOnly   ← falta Secure + SameSite
Set-Cookie: PHPSESSID=...; HttpOnly; SameSite=Strict ← ben, falta Secure
Cache-Control: no-cache, must-revalidate
Content-Type: text/html;charset=utf-8

5.2 Cabeceiras recomendadas (estado obxectivo)

HTTP/1.1 200 OK
Server: Apache                              ← sen versión nin SO
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; script-src 'self'; \
    style-src 'self' 'unsafe-inline'; img-src 'self' data:; \
    form-action 'self'; frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cache-Control: no-store, no-cache, must-revalidate, private
Pragma: no-cache
Set-Cookie: PHPSESSID=<opaco>; Path=/dvwa/; HttpOnly; Secure; \
    SameSite=Strict; Max-Age=1800
Set-Cookie: security=high; Path=/dvwa/; HttpOnly; Secure; SameSite=Strict
Content-Type: text/html; charset=UTF-8

5.3 Mapeo cabeceira → ataque que mitiga

Cabeceira Ataque mitigado
X-Frame-Options: DENY Clickjacking para roubo de credenciais
CSP: form-action 'self' Envío de formulario a servidor do atacante
CSP: frame-ancestors 'none' UI redressing / clickjacking
X-Content-Type-Options: nosniff MIME sniffing + XSS via upload
SameSite=Strict CSRF cross-site
HttpOnly Roubo de cookie via XSS
Secure Intercepción de cookie en HTTP
Strict-Transport-Security Downgrade a HTTP / SSL stripping
Referrer-Policy Fuga de token CSRF en cabeceira Referer
Cache-Control: no-store Caché de credenciais en proxy/navegador

6. Referencias

  • OWASP Top 10 2021 — A07: Identification and Authentication Failures
  • CWE-307: https://cwe.mitre.org/data/definitions/307.html
  • CWE-352: https://cwe.mitre.org/data/definitions/352.html
  • CWE-208: https://cwe.mitre.org/data/definitions/208.html
  • CVSS v3.1 Specification: https://www.first.org/cvss/v3.1/specification-document
  • OWASP Testing Guide v4.2 — OTG-AUTHN-003 (Testing for Weak Lock Out Mechanism)
  • OWASP CSRF Prevention Cheat Sheet
  • Mozilla Observatory: https://observatory.mozilla.org