Reconnaissance
nmap/TCP
Nmap found the port 22 and port 80 to be open.
croc@hacker$ rustscan -a 10.129.37.152 --ulimit 5000 -- -A -Pn
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
Real hackers hack time ⌛
[~] The config file is expected to be at "/home/croc/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.129.37.152:22
Open 10.129.37.152:80
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} -{{ipversion}} {{ip}} -A -Pn" on ip 10.129.37.152
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-21 03:55 -0400
Nmap scan report for 10.129.37.152
Host is up, received user-set (0.23s latency).
Scanned at 2026-04-21 03:55:55 EDT for 19s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN9Ju3bTZsFozwXY1B2KIlEY4BA+RcNM57w4C5EjOw1QegUUyCJoO4TVOKfzy/9kd3WrPEj/FYKT2agja9/PM44=
| 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9qI0OvMyp03dAGXR0UPdxw7hjSwMR773Yb9Sne+7vD
80/tcp open http syn-ack ttl 63 nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://silentium.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.24.0 (Ubuntu)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19
TCP/IP fingerprint:
OS:SCAN(V=7.98%E=4%D=4/21%OT=22%CT=%CU=36596%PV=Y%DS=2%DC=T%G=N%TM=69E72D9E
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=100%GCD=1%ISR=103%TI=Z%CI=Z%II=I%TS=A)OPS(
OS:O1=M552ST11NW7%O2=M552ST11NW7%O3=M552NNT11NW7%O4=M552ST11NW7%O5=M552ST11
OS:NW7%O6=M552ST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(
OS:R=Y%DF=Y%T=40%W=FAF0%O=M552NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS
OS:%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=
OS:Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=
OS:R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T
OS:=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=
OS:S)
Uptime guess: 3.964 days (since Fri Apr 17 04:47:49 2026)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=256 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 228.02 ms 10.10.14.1
2 228.20 ms 10.129.37.152
Nmap done: 1 IP address (1 host up) scanned in 19.95 seconds
Raw packets sent: 34 (2.306KB) | Rcvd: 27 (1.858KB)As the port 80 redirects to the http://silentium.htb/, we need to have an entry in the /etc/hosts file pointing to that:
croc@hacker:~/$ sudo sed -i '$a10.129.37.152\tsilentium.htb' /etc/hostsHTTP - 80/TCP
Business Logic
Let's start with the Happy Path Testing of the website while keeping burp suite running in the background.

We have this functionality of a calculator labelled under "Institutional Loan Structuring".

This calculator is purely client-side JavaScript with all calculations happening in the browser using the amortization formula with no server requests made whatsoever.
You can verify it yourself: Open DevTools → Network tab → move the sliders. You'll see zero new network requests firing. Everything is already loaded on page load.
We also found the following employee names on the site, which could serve as potential usernames.

I will go ahead and note these in my notepad.

Moreover, we also see a .js file in the footer of the main page which can be accessed at assets/app.js.
Directory Busting
Directory busting revealed the directory of /assets which gives a 403 upon access.
croc@hacker:~/HTB$ sudo dirsearch -u http://silentium.htb/ -w /usr/share/wordlists/dirb/common.txt
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 4613
Output File: /home/croc/HTB/reports/http_silentium.htb/__26-04-21_04-17-45.txt
Target: http://silentium.htb/
[04:17:45] Starting:
[04:17:53] 301 - 178B - /assets -> http://silentium.htb/assets/
Task Completed
Subdomain Enumeration
Moving on to subdomain enumeration, we managed to find a staging subdomain.
croc@hacker:~/HTB$ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u http://silentium.htb \
-H "Host: FUZZ.silentium.htb" \
-fs 178
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://silentium.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.silentium.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 178
________________________________________________
staging [Status: 200, Size: 3142, Words: 789, Lines: 70, Duration: 241ms]
:: Progress: [4989/4989] :: Job [1/1] :: 179 req/sec :: Duration: [0:00:29] :: Errors: 0 ::Added the staging.silentium.htb inside of /etc/hosts and opened up in the browser. It shows a login portal for a platform called Flowise, which is meant to be used for building AI agents.

Username Enumeration
On the login portal, the API returns different responses depending on whether a user exists or not.

For example, using croc@croclius.com returned a 404 User Not Found, while ben@silentium.htb returned a 401 Incorrect Email or Password.

This confirms that Ben is a valid user, meaning we can enumerate valid users simply by observing the response codes and messages.
Valid Usernames
Saved all of the following combinations into the emails.txt file:
marcus@silentium.htb
ben@silentium.htb
elena@silentium.htb
marcus.thorne@silentium.htb
elena.rossi@silentium.htbAfter that, used ffuf to enumerate all valid usernames. Note that it is filtering the 404 automatically.
croc@hacker:~/HTB/selentium$ ffuf -w emails.txt \
-u http://staging.silentium.htb/api/v1/auth/login \
-X POST \
-H "Content-Type: application/json" \
-d '{"email":"FUZZ","password":"dummy"}'
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://staging.silentium.htb/api/v1/auth/login
:: Wordlist : FUZZ: /home/croc/HTB/selentium/emails.txt
:: Header : Content-Type: application/json
:: Data : {"email":"FUZZ","password":"dummy"}
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
ben@silentium.htb [Status: 401, Size: 85, Words: 4, Lines: 1, Duration: 245ms]
:: Progress: [5/5] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::Forgot Password
Next, I explored the forgot password functionality using ben@silentium.htb.

Out of curiosity, I also clicked on that link above to change my password if I got a reset code:

Inside of burp, I see this response where we have this tempToken in the response which seems to be the password reset token and we can try using it above to update the password.

The "credential":"$2a$05$6o1ngPjXiRj.EbTK33PhyuzNBn2CLo8.b0lyys3Uht9Bfuos2pWhG" is the bcrypt password hash. It is Ben's actual hashed password stored in the database and the API is leaking it directly in the response. Note that Ben is also the admin of the web app.
Updating the Ben's Password
We can use this token to try to reset the user's password:

Using the tempToken returned in the response, we were able to reset Ben's password directly without any email verification, and the application returned a success response.
Now, we can log in into the application.

Shell as
Finding the Version
You can see the version of the web app in the top right corner under the settings icon:

You can also use the API endpoint of /version inside of Burp.

Looking for CVEs
I asked Claude to find all the CVEs for this version and it provided me three.
Understanding CVE-2025-59528
The following article from SentinelOne provides a clear breakdown of the attack flow.


In a nutshell, the user input as inputString is passed to the Function() constructor without being properly sanitized in the convertToValidJSONString function.
function convertToValidJSONString(inputString: string) {
try {
const jsObject = Function('return ' + inputString)()
return JSON.stringify(jsObject, null, 2)
} catch (error) {
console.error('Error converting to JSON:', error)
return ''
}
}This makes it equivalent to the eval() function. When user-controlled data is passed directly to this constructor without proper validation, it creates a direct path for arbitrary code execution.
Since Flowise runs as a Node.js application, executed code has access to the full Node.js runtime environment, including dangerous built-in modules such as child_process which can be used to execute system commands.
The following is the attack path from here which we will perform step-by-step now:
[Attacker]
|
| Identifies exposed Flowise v3.0.5 instance
v
[Target Flowise Instance]
|
| Accesses CustomMCP node configuration interface
v
[CustomMCP Node]
|
| Submits crafted mcpServerConfig string
| containing malicious JavaScript payload
v
[Server-Side Processing]
|
| Passes unsanitized input to Function() constructor
v
[Function() Constructor]
|
| Evaluates and executes malicious JavaScript
v
[Node.js Runtime - Full Privileges]
|
_________________|_________________
| | |
v v v
child_process fs net
Execute system Read / Write Establish network
commands filesystem connectionsExploiting CVE-2025-59528







0 Comments