Bandit: nivel 24 → 25
Cómo resolver el nivel 24 → 25 de OverTheWire Bandit haciendo fuerza bruta a un PIN de 4 dígitos con un script de bash y netcat, y filtrando la respuesta con grep y awk.
Primer nivel de fuerza bruta. Hay un servicio que pide la contraseña de bandit24 (que ya tenemos) más un PIN secreto de 4 dígitos que no tenemos. Como solo hay 10000 PINs posibles, en vez de adivinarlo los probamos todos de golpe con un script. Es tu primer bucle de ataque, y el concepto (automatizar lo que a mano sería inviable) es el corazón de medio pentesting.
Las contraseñas de los niveles aparecen como [REDACTED]. Siguiendo las normas de OverTheWire, no publico credenciales: la gracia es que llegues a ellas tú.
Conexión
1
2
$ sshpass -p '[REDACTED]' ssh bandit24@bandit.labs.overthewire.org -p 2220
bandit24@bandit:~$ export TERM=xterm
El enunciado
El enunciado del nivel dice que hay un daemon escuchando en el puerto 30002 de localhost. Para darte la contraseña de bandit25 necesita que le mandes, en una sola línea separadas por un espacio, la contraseña de bandit24 y un PIN secreto de 4 dígitos. No hay forma de deducir el PIN: hay que probar.
Primer acercamiento: hablar con el servicio a mano
Antes de automatizar nada, conviene ver qué responde el servicio:
1
2
3
4
bandit24@bandit:~$ nc localhost 30002
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
[REDACTED] 0000
Wrong! Please enter the correct current password and pincode. Try again.
Le mando la contraseña de bandit24 y un PIN de prueba (0000) y me responde Wrong!. Perfecto: ya sé el formato que espera (contraseña PIN) y qué contesta cuando fallo. Con eso puedo construir el ataque: generar las 10000 líneas posibles y mandárselas todas.
Fíjate en que el servicio no cierra la conexión tras un fallo: sigue pidiendo. Eso es lo que nos permite mandarle los 10000 intentos seguidos por la misma conexión en lugar de reconectar cada vez.
El script de fuerza bruta
Creo un directorio de trabajo con mktemp -d (buena costumbre, como en el nivel anterior) y dentro monto el script:
1
2
3
4
5
6
7
#!/usr/bin/env bash
for pin in {0000..9999}; do
echo "$(cat /etc/bandit_pass/bandit24) ${pin}" >> probando.txt
done
nc localhost 30002 < probando.txt > resultados.txt
Qué hace, por partes:
for pin in {0000..9999}: expansión de llaves que genera todos los números de 0000 a 9999. El truco está en escribir0000(con los ceros): bash mantiene el relleno de ceros y genera0000,0001, …,9999, todos con 4 dígitos, que es justo el formato de PIN que pide el servicio.echo "$(cat /etc/bandit_pass/bandit24) ${pin}" >> probando.txt: por cada PIN, escribe una línea con la contraseña de bandit24, un espacio y el PIN, y la va añadiendo (>>) al ficheroprobando.txt. Al acabar el bucle, ese fichero tiene las 10000 combinaciones.nc localhost 30002 < probando.txt > resultados.txt: conecta al daemon y le vuelca todo el fichero por la entrada estándar. El servicio va leyendo línea a línea, probando cada combinación, y toda su respuesta se guarda enresultados.txt.
Uso
$(cat /etc/bandit_pass/bandit24)en lugar de escribir la contraseña a pelo. Es más limpio (no dejas la contraseña escrita en el script) y evita erratas. Tú puedes leer ese fichero porque eres bandit24.
Lo ejecuto y compruebo que me ha creado los ficheros. Un vistazo rápido al tamaño de la respuesta:
1
2
bandit24@bandit:/tmp/tmp.XXXX$ bash script.sh
bandit24@bandit:/tmp/tmp.XXXX$ wc -l resultados.txt
Filtrar la respuesta
resultados.txt está lleno de líneas Wrong!, una por cada PIN fallido, más la buena perdida entre ellas. Para encontrarla, filtro quitando las líneas de error con grep -v (el -v invierte: muestra lo que no coincide):
1
2
3
4
bandit24@bandit:/tmp/tmp.XXXX$ cat resultados.txt | grep -v "Wrong!"
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
Correct!
The password of user bandit25 is [REDACTED]
Ahí está. Con eso ya tienes la contraseña, pero como la idea es coger soltura, vamos a afinar el filtro para que escupa solo la contraseña:
1
2
bandit24@bandit:/tmp/tmp.XXXX$ cat resultados.txt | grep -v "Wrong!" | tail -n 2 | head -n 1 | awk '{print $NF}'
[REDACTED]
Desmontando la tubería:
| Trozo | Qué hace |
|---|---|
grep -v "Wrong!" | Quita todas las líneas de fallo |
tail -n 2 | Se queda con las 2 últimas líneas |
head -n 1 | De esas 2, la primera (la del The password... is ...) |
awk '{print $NF}' | Imprime el último campo de la línea: $NF = number of fields, o sea, la última palabra |
El resultado es la contraseña pelada, lista para copiar. Encadenar filtros así (grep → tail/head → awk) es un patrón que vas a repetir mil veces para extraer justo el dato que quieres de una salida larga.
Por qué esto importa en seguridad
Esto es fuerza bruta en su forma más pura: el espacio de PINs es diminuto (10⁴ = 10000) y el servicio no limita los intentos ni mete retardos. Las dos cosas son el fallo. En un sistema bien hecho verías:
- PINs/contraseñas más largos (el espacio de búsqueda crece exponencialmente y la fuerza bruta deja de ser viable).
- Rate limiting: bloqueo o retardo tras X intentos fallidos.
- Bloqueo de cuenta o captchas.
La herramienta “de verdad” para esto es hydra, que automatiza la fuerza bruta contra multitud de servicios (SSH, HTTP, FTP…). Aquí lo hacemos a mano con bash para entender el mecanismo, pero el concepto es idéntico.
Un PIN de 4 dígitos sin límite de intentos se rompe en segundos. Es el recordatorio de por qué la longitud y el rate limiting importan tanto en el mundo real.
Pasar al nivel siguiente
1
2
bandit24@bandit:/tmp/tmp.XXXX$ exit
$ sshpass -p '[REDACTED]' ssh bandit25@bandit.labs.overthewire.org -p 2220
Resumen
| Comando / concepto | Para qué sirve |
|---|---|
{0000..9999} | Expansión de llaves con relleno de ceros: genera 0000-9999 |
>> | Añadir al final de un fichero (sin sobreescribir) |
| `nc localhost 30002 |