DEV Community

Shobhit saini
Shobhit saini

Posted on

Casino (Medium) β€” Hack Smarter World walkthrough

Casino (Medium) β€” From Source Map to Root 🎰 | Hack Smarter World

Sometimes you start testing a login page expecting SQL injection, and end up finding something completely different. πŸ˜‚

This is my walkthrough of Casino (Medium), a Web and Linux challenge on Hack Smarter World. The objective was to identify vulnerabilities in a resort's guest Wi-Fi portal and escalate privileges to root.

The journey involved a forgotten JavaScript source map, an exposed API, Server-Side Template Injection (SSTI), SSH key exposure, credentials in Bash history, and finally, a root password sitting in a log file.

Let's get into it!

1. Port Scanning with Nmap

I started with an Nmap scan to identify the open ports and running services.

nmap -sC -sV -p- <TARGET_IP>
Enter fullscreen mode Exit fullscreen mode

The interesting results were:

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.18
80/tcp   open  http    Werkzeug httpd 3.1.8 (Python 3.10.18)
2222/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u7
Enter fullscreen mode Exit fullscreen mode

Three open ports, two SSH services, and a Python-based web application. Port 80 looked like the obvious starting point, so I opened the website and started exploring.

2. SQL Injection? Not Today.

The website presented a guest Wi-Fi login page asking for a room number and last name.

My first thought was SQL injection. I tried a few basic payloads against the last_name parameter:

curl -X POST "http://<TARGET_IP>/login" \
  -d "room_number=101&last_name='" -i

curl -X POST "http://<TARGET_IP>/login" \
  -d "room_number=101&last_name=xyz' OR '1'='1" -i

curl -X POST "http://<TARGET_IP>/login" \
  -d "room_number=101&last_name=xyz' UNION SELECT 1,2,3-- -" -i
Enter fullscreen mode Exit fullscreen mode

Every request returned the same authentication failure.

I also tried an injection payload against the room number, but the application returned an invalid-format message.

No useful behavior, no SQL errors, and no login bypass.

Lesson: Don't get emotionally attached to a vulnerability just because it looks promising. The login form wasn't cooperating, so I moved on.

3. Finding the JavaScript Source Map

While inspecting the login page's source, I noticed this line:

<script src="/static/js/app.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

I opened the JavaScript file and found:

function initPortal(){console.log("Hack Smarter World WiFi Gateway Active");}
document.addEventListener("DOMContentLoaded",initPortal);
//# sourceMappingURL=app.min.js.map
Enter fullscreen mode Exit fullscreen mode

That last line caught my attention:

//# sourceMappingURL=app.min.js.map
Enter fullscreen mode Exit fullscreen mode

A source map can reveal the original source code behind minified JavaScript. Naturally, I checked whether it was publicly accessible.

curl -s http://<TARGET_IP>/static/js/app.min.js.map
Enter fullscreen mode Exit fullscreen mode

The response contained:

{
  "version": 3,
  "file": "app.min.js",
  "sources": ["src/api/roomVerification.js"],
  "sourcesContent": [
    "// Front-Desk Kiosk API verification helper\nasync function checkRoomStatus(roomNum) {\n const res = await fetch('/api/v1/rooms/status?status=occupied');\n return await res.json();\n}"
  ]
}
Enter fullscreen mode Exit fullscreen mode

And there it was:

/api/v1/rooms/status?status=occupied
Enter fullscreen mode Exit fullscreen mode

A previously undiscovered API endpoint. Time to investigate. πŸ‘€

4. An Unauthenticated API

I visited the endpoint:

curl -s \
  "http://<TARGET_IP>/api/v1/rooms/status?status=occupied"
Enter fullscreen mode Exit fullscreen mode

The response contained multiple guest records, including room numbers, names, and checkout dates.

The interesting part? The endpoint didn't require authentication.

The login page was asking for guest details while another endpoint was exposing them. That's one way to make authentication unnecessarily difficult for legitimate users and unnecessarily easy for attackers. πŸ˜…

I examined the checkout dates to identify active reservations and used a valid room-number-and-last-name combination from the lab to log in.

The application issued a session cookie and redirected me to the dashboard.

Login bypass achieved. No SQL injection required.

5. SSTI β€” The Profile Field Was the Next Clue

Once inside the dashboard, I found an edit-profile option where I could change my preferred name.

The application was running Flask and Werkzeug, so I decided to test the field for Server-Side Template Injection.

I started with the classic payload:

{{7*7}}
Enter fullscreen mode Exit fullscreen mode

The application returned:

49
Enter fullscreen mode Exit fullscreen mode

Then I tried:

{{7*'7'}}
Enter fullscreen mode Exit fullscreen mode

The result was:

7777777
Enter fullscreen mode Exit fullscreen mode

This behavior is consistent with Jinja2, which uses Python expression semantics for these operations.

The field wasn't simply displaying my inputβ€”it was evaluating it as a template expression.

A preferred-name field should display your name, not start doing your maths homework. πŸ˜‚

6. From SSTI to a Shell

The successful template-expression tests suggested SSTI. Further testing in the lab confirmed that the vulnerability could be escalated to operating-system command execution.

I obtained a shell as:

www-data
Enter fullscreen mode Exit fullscreen mode

This gave me an initial foothold on the system.

The important point is that SSTI can become a serious vulnerability when untrusted input is interpreted as template code. In this case, the impact went beyond evaluating arithmetic expressions.

7. Finding George's SSH Private Key

With access as www-data, I began examining the filesystem and looking for files that the compromised account could read.

Inside George's home directory, I found SSH key material:

/home/george/.ssh/
Enter fullscreen mode Exit fullscreen mode

I checked the directory permissions and found that the private key, id_rsa, was readable by other users.

Its permissions were effectively 644:

-rw-r--r-- 1 george george ... id_rsa
Enter fullscreen mode Exit fullscreen mode

A private key that everyone can read isn't very private anymore. πŸ’€

I copied the key, restricted its permissions locally, and used it to connect through SSH on port 2222:

chmod 600 george_id_rsa

ssh -i george_id_rsa george@<TARGET_IP> -p 2222
Enter fullscreen mode Exit fullscreen mode

The connection succeeded, giving me access as George.

One careless file-permission setting had turned a low-privileged web-server compromise into another user's shell.

8. George to David β€” Bash History to the Rescue

Next, I checked George's privileges:

sudo -l
Enter fullscreen mode Exit fullscreen mode

No useful sudo access.

So I moved on to shell history:

cat ~/.bash_history
Enter fullscreen mode Exit fullscreen mode

Among the commands, I found a sequence involving a switch to the david account and a database connection.

The problem was that a password had been typed directly into the shell instead of being entered at the interactive password prompt.

That meant the credential was sitting in Bash history in plaintext.

There was also an attempt to clear the history:

history -c
Enter fullscreen mode Exit fullscreen mode

Unfortunately, clearing the current in-memory history doesn't guarantee that previously written history files or other session records are erased.

I used the recovered credential in the lab to switch to David.

Another account, another step forward.

Takeaway: Shell history is great for remembering commands, but terrible as a place to store passwords.

9. David to Root β€” The Provisioning Log

As David, I checked sudo privileges again:

sudo -l
Enter fullscreen mode Exit fullscreen mode

No direct route to root.

However, I noticed that David belonged to the adm group. On many Linux systems, this group can read certain system logs.

I inspected /var/log and found:

/var/log/provisioning.log
Enter fullscreen mode Exit fullscreen mode

The file permissions showed that it was owned by root:adm and readable by the group.

I opened it:

cat /var/log/provisioning.log
Enter fullscreen mode Exit fullscreen mode

The log contained a root password in plaintext.

At this point, the system had essentially left the final key in a file that my current user was allowed to read. Not exactly Fort Knox. πŸ˜‚

I used the recovered credential through the lab's local authentication mechanism and checked my privileges:

id
Enter fullscreen mode Exit fullscreen mode

The output confirmed root access:

uid=0(root) gid=0(root) groups=0(root)
Enter fullscreen mode Exit fullscreen mode

Root obtained! 🎯

10. Attack Chain Summary

Here's the entire path in one place:

  1. Nmap identified the available services.
  2. SQL injection attempts against the login form failed.
  3. The JavaScript source map revealed an undocumented API endpoint.
  4. The unauthenticated API exposed guest information.
  5. A valid guest record enabled login to the portal.
  6. SSTI in the profile field led to command execution as www-data.
  7. A world-readable SSH private key enabled access as George.
  8. George's Bash history exposed David's credentials.
  9. David's adm group membership allowed reading a provisioning log.
  10. The log exposed a root credential, leading to root access.

11. What Could Have Prevented This?

  • Source maps: Avoid exposing sensitive implementation details in production source maps.
  • API security: Enforce authentication and authorization on every sensitive endpoint.
  • SSTI: Never treat user-controlled input as template source code.
  • SSH keys: Restrict private keys to their intended owners.
  • Bash history: Avoid entering passwords directly into shell commands.
  • Logs: Never write reusable credentials into application or provisioning logs.
  • Least privilege: Restrict access to user directories and sensitive log files.

Final Thoughts

Casino was a good reminder that penetration testing isn't just about running tools and firing payloads at a login form.

My first instinct was SQL injection, but the real route started with a forgotten source map. That led to an exposed API, then SSTI, and eventually a chain of local privilege-escalation mistakes.

The same problem kept appearing in different forms: sensitive information was accessible where it shouldn't have been.

A source map exposed an API. A private key was readable by other users. A password survived in shell history. A root credential ended up in a log file.

Different mistakes, same result.

Sometimes you don't need a fancy exploit when the target is already leaking the keys to the kingdom. πŸ˜‚

References

CyberSecurity #CTF #HackSmarter #Writeup #PenetrationTesting #Linux #SSTI #Jinja2 #PrivilegeEscalation

Top comments (0)