Bandit: nivel 23 → 24
Cómo resolver el nivel 23 → 24 de OverTheWire Bandit colocando tu propio script en un directorio que ejecuta un cron como bandit24, una escalada de privilegios real.
Este es el nivel de cron más interesante hasta ahora, y el primero en el que de verdad escalamos privilegios. Hasta aquí nos limitábamos a leer lo que dejaba una tarea programada. Ahora el cron ejecuta scripts de un directorio en el que nosotros podemos escribir, así que la jugada es colocar nuestro propio script para que bandit24 lo ejecute por nosotros. Es exactamente el patrón de “cron escribible” que explotarás en máquinas reales.
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ú.
El enunciado
El enunciado del nivel nos manda otra vez a /etc/cron.d/, pero avisa de que esta vez el cron hace algo distinto. La configuración mantiene el patrón de siempre:
1
2
3
bandit23@bandit:~$ cat /etc/cron.d/cronjob_bandit24
@reboot bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
Un script que corre como bandit24 cada minuto. La diferencia, como siempre, está dentro.
Leer el script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bandit23@bandit:~$ cat /usr/bin/cronjob_bandit24.sh
#!/bin/bash
shopt -s nullglob
myname=$(whoami)
cd /var/spool/"$myname"/foo || exit
echo "Executing and deleting all scripts in /var/spool/$myname/foo:"
for i in * .*;
do
if [ "$i" != "." ] && [ "$i" != ".." ];
then
echo "Handling $i"
owner="$(stat --format "%U" "./$i")"
if [ "${owner}" = "bandit23" ] && [ -f "$i" ]; then
timeout -s 9 60 "./$i"
fi
rm -rf "./$i"
fi
done
Esto es lo que hace, ejecutándose como bandit24:
shopt -s nullglob: si un patrón como*no encuentra ficheros, en vez de dejar el literal*lo expande a nada. Evita errores en el bucle cuando el directorio está vacío.cd /var/spool/bandit24/foo(porquemynameesbandit24): entra en ese directorio. Si no existe o no puede entrar,|| exitaborta.- El bucle
for i in * .*: recorre todos los ficheros del directorio, incluidos los ocultos (.*), saltándose.y... owner="$(stat --format "%U" "./$i")": obtiene el propietario de cada fichero.if [ "${owner}" = "bandit23" ] && [ -f "$i" ]: la condición clave. Solo ejecuta el fichero si su propietario es bandit23 (¡nosotros!) y es un fichero regular.timeout -s 9 60 "./$i": lo ejecuta, matándolo conSIGKILLsi tarda más de 60 segundos.rm -rf "./$i": borra el fichero después, pase lo que pase.
Traducido: cada minuto, bandit24 entra en /var/spool/bandit24/foo y ejecuta todo lo que haya ahí que sea propiedad de bandit23. Nos está invitando, literalmente, a dejarle un script nuestro para que lo ejecute con sus permisos.
El plan
La idea de la escalada:
- Escribir un script que lea la contraseña de bandit24 (
/etc/bandit_pass/bandit24) y la copie a un sitio que yo pueda leer después. - Dejar ese script en
/var/spool/bandit24/foo, siendo yo (bandit23) el propietario. - Esperar a que el cron lo ejecute como bandit24, que sí puede leer esa contraseña.
- Leer el resultado.
El detalle fino: el cron ejecuta el script como bandit24, pero el fichero de salida lo escribe bandit24, no yo. Así que ese fichero (y la carpeta donde cae) tienen que tener permisos que me permitan leerlo a mí después. Por eso vamos a ser generosos con los permisos.
La solución paso a paso
1. Un directorio de trabajo con mktemp
/tmp es escribible por todos, así que creo ahí una carpeta temporal única con mktemp -d y le doy permisos totales para que bandit24 pueda escribir dentro:
1
2
3
4
bandit23@bandit:~$ mktemp -d
/tmp/tmp.d4iKxOa5lF
bandit23@bandit:~$ cd /tmp/tmp.d4iKxOa5lF
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ chmod 777 /tmp/tmp.d4iKxOa5lF
mktemp -dcrea un directorio con un nombre aleatorio garantizado como único. Es buena costumbre trabajar así en/tmppara no pisar ficheros de otros usuarios ni dejar rastros con nombres predecibles. El tuyo tendrá otro nombre: usa el que te salga a ti.
2. Crear el fichero de salida y el script
Creo dos ficheros: password (donde caerá la contraseña) y script.sh (lo que ejecutará bandit24), y les doy permisos totales:
1
2
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ touch password script.sh
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ chmod 777 password script.sh
El chmod 777 del fichero password es importante: lo va a escribir bandit24, pero lo tengo que leer yo. Con permisos abiertos, ambos pueden.
El contenido de script.sh:
1
2
#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/tmp.d4iKxOa5lF/password
Cuando bandit24 ejecute esto, leerá su propia contraseña (puede, es suya) y la volcará en mi fichero password. Fíjate en que uso la ruta absoluta al fichero de salida: el cron ejecuta el script desde /var/spool/bandit24/foo, no desde mi carpeta, así que una ruta relativa no valdría.
3. Dejar el script en el buzón del cron
Copio el script al directorio que vigila el cron:
1
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ cp script.sh /var/spool/bandit24/foo/script.sh
Lo copio (cp) en vez de moverlo para que el fichero en destino lo cree bandit23, es decir, que su propietario sea yo, que es justo lo que exige la condición del script (owner = bandit23).
El script tiene que ser ejecutable. Como le di
chmod 777antes de copiarlo, ya lo es. Si se te olvida, el cron no lo ejecutará (la condición-fpasa, perotimeout ./script.shfallaría sin permiso de ejecución).
4. Esperar y leer
El cron corre cada minuto. Espero uno o dos minutos y leo el fichero password:
1
2
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ cat password
[REDACTED]
Ahí está la contraseña de bandit24. (Si lo lees demasiado pronto, el fichero estará vacío: espera a que el cron pase.)
Por qué esto importa en seguridad
Este nivel es una escalada de privilegios de libro. El fallo no es tuyo, es del diseño: un proceso privilegiado (bandit24) ejecuta ficheros de un directorio en el que un usuario de menos privilegios (bandit23) puede escribir. En cuanto puedes meter un ejecutable en la ruta que corre un usuario con más permisos, puedes ejecutar lo que quieras con esos permisos: leer sus secretos, abrirte una shell como él, etc.
En una máquina real buscarías justo esto:
- Crons que ejecutan scripts en directorios escribibles (
/tmp,/var/spool, rutas con permisos flojos). - Scripts de cron que no usan rutas absolutas (se pueden secuestrar vía
PATH). - Directorios con
777o de tu propiedad en el flujo de un proceso de root.
La diferencia entre el nivel anterior y este es la que separa “leer una fuga” de “ejecutar código como otro usuario”. El segundo es el que de verdad te lleva a
rooten un pentest.
Pasar al nivel siguiente
1
bandit23@bandit:/tmp/tmp.d4iKxOa5lF$ ssh bandit24@bandit.labs.overthewire.org -p 2220
Entra y usa la contraseña de bandit24 que acabas de recuperar.
El siguiente nivel cambia por completo de registro: toca un daemon que pide un PIN de 4 dígitos, y tendrás que hacer fuerza bruta con un script. Prepárate para escribir tu primer bucle de ataque.
Resumen
| Comando / concepto | Para qué sirve |
|---|---|
| Cron escribible | Un proceso privilegiado ejecuta ficheros de un directorio donde tú escribes |
shopt -s nullglob | Que un patrón sin coincidencias se expanda a nada |
stat --format "%U" fichero | Obtener el propietario de un fichero |
timeout -s 9 60 cmd | Ejecutar un comando matándolo a los 60 s (SIGKILL) |
mktemp -d | Crear un directorio temporal con nombre único |
chmod 777 | Permisos totales: necesario para que otro usuario escriba/leas tú |
cp (no mv) | Que el fichero en destino sea propiedad del que copia |
| Ruta absoluta en el script | El cron ejecuta desde otro directorio distinto al tuyo |