Simon McCabe

OSCP · OSWP · PWPP · PWPA · PAPA · EnCE · Linux+ · LPIC-1 · Network+ · Security+ · Pentest+ · eJPT · eWPT · BSc · PGCert

The Great Disappearing Act - TryHackMe Writeup


Jester
Jester

Unlocking Hopper’s Memories

Before you can tackle this hard-rated difficulty room, you must complete AoC Day #1 here. Once you do, you’ll get a “Memory Key”.

Memory Key
Memory Key

Entering the correct memory key gives a pop-up window. But…is that all? No ;-)

Asylum Escape Challenge — Flag #1

We play as Hopper, with one ultimate goal. Free ourselves from this wrongful imprisonment! Lucky for us, Hopper has a 5-step escape plan:

We start off in the Cells / Storage. And here, we run a (very basic) Nmap scan to see what we’re dealing with.

Nmap scan
Nmap scan

So our basic scan has discovered some open ports. I open the browser and check out 8000, 8080 and 9001. Note: the key to unlock hoppers memory has allowed these ports to show up. If you didn’t unlock hoppers memory earlier, you wont even see these ports.

I’m greeted with a “HopSec Security Console” residing on port 8080. But, I have no login.

HopSec Security Console
HopSec Security Console

Browsing to port 8000 shows fakebook. Interesting. I create an account and log into the application.

fakebook
fakebook

Before long, I find posts from Guard Hopkins revealing his email address:

Hopkin’s email
Hopkin’s email

The name of his dog..

Johnnyboy
Johnnyboy

and his DoB. It shouldn’t come as any surprise that the password is: Johnnyboy1982!

1982
1982

I use the newly aquired information to log in as Guard Hopkins:

Logging in as Guard Hopkins
Logging in as Guard Hopkins

I perform a slightly more in depth Nmap scan and find that there are actually more ports open than it first appeared, so I added these to my notes, as they’ll come in useful later on.

More open ports
More open ports

Now, the first flag is given to us for simply logging in and clicking on the door to exit the cell (Unlock Cell Door) for the flag. Simple, huh? Well it wont stay like this for long!

Flag #1
Flag #1

Asylum Escape Challenge — Flag #2 (sort of)

So now, we can browse back to <IP>:8080 and look at what’s next. We need a “Keycode” to escape the Psych Ward.

Psyche Ward
Psyche Ward

Port 13400 (from our 2nd Nmap scan) revealed a Facility Video Portal.

Port 13400
Port 13400

Sadly, it had been hijacked by…well, whatever this thing is!

Jestered
Jestered

Whilst clicking the “Lobby” and “Psych Ward Exit” cameras, I was capturing traffic in Caido (feel free to use Zap, Burpsuite or whatever proxy tool you use).

Traffic
Traffic

I used: req.method.eq:”OPTIONS” in Caido to show me requests that had been sent using the OPTIONS verb. There was A LOT of traffic in my history, so it made sense to work through it piece by piece.

options requests
options requests

I also ran: req.method.eq:”POST” AND req.host.eq:”10.81.164.78"

POST reqs
POST reqs

I sent one particular POST request to my replay tab (repeater, if you’re using Burpsuite). Here I could see a “camera_id” and a “tier”. This was why I couldn’t view the admin camera earlier. I was a mere “guard”, not an “admin”.

guard
guard

Hitting ‘Send’ gave a response which confirmed our effective tier and gave a “ticket_id”.

POST Response
POST Response

The “most obvious” thing to do was to try and change the camera_id and tier to admin. This didn’t work, however, when I appended the ?tier=admin parameter to the URL, it then responded to confirm I was now admin and gave a new ticket_id value.

admin ticket
admin ticket

Looking at main.js for the “ticket_id” parameter showed how it was made up.

API = base URL
/v1/streams/ = streaming API endpoint
e38f8604–8b96–4fa9–8de5–1a22dc42c5fa = stream UUID (unique camera/feed ID)
/manifest.m3u8 = HLS playlist (video segments list).

For reference, HLS is also known as HTTP Live Streaming. The m3u8 file is a UTF-8 text file serving as a playlist for the video player. It lists the locations of media segments with timing and sequencing metadata, but it doesn’t actually contain the video/audio data itself. Think of it like a pointer, that points to the correct video chunks on the server.

Anyway, I constructed my payload based on what was in the JavaScript file:

attachWithReconnect(API + ‘/v1/streams/’ + j.ticket_id’ + ‘/manifest.m3u8’);

and replaced j.ticket_id with my actual ticket id, which gave this:

attachWithReconnect(API + ‘/v1/streams/’ + ‘e38f8604–8b96–4fa9–8de5–1a22dc42c5fa’ + ‘/manifest.m3u8’);
main.js
main.js

From here, I simply entered this value into the browser’s console and forced the video to play.

Keycode entry
Keycode entry

I went back to the Psych Ward Exit and entered the Keycode:

This gave me the first half of flag #2.

Asylum Escape Challenge — Flag #2 (well, 2nd half of flag 2)

Going back to the GET requests I’d captured earlier, I could see some m3u8 files and the HTTP response showed a /v1/ingest/diagnostics endpoint along with an RTSP URL.

At this point, we should consider how RTSP and m3u8 files work together:

  1. Camera streams RTSP > vendor server
  2. Server segments > video1.ts, video2.ts…
  3. Server generates > manifest.m3u8
  4. Browser requests > /v1/streams/[ID]/manifest.m3u8
  5. Player downloads > ts files > live video
diagnostics endpoint
diagnostics endpoint

I tried a GET request but got a 405 Method Not Allowed error. Logic would state this method isn’t allowed, but other methods may be?

Method not allowed
Method not allowed

So, I changed to a POST request, but this time, I was “unauthorized”.

Unauthorized
Unauthorized

If you remember back when I got the “admin_ticket”, I was authorized and I had a bearer token. The content type was set to application/json and I had the following body:

{ “camera_id”:”cam=admin”}

So what happens if we authorize the POST request?

Well, I got an error, but this time, the error said:

{”error”:”invalid rtsp_url”}

So the server was expecting an object with a key:value pair. In other words, “rtsp_url:URL”. I create a new POST request and sent it:

object
object

This now gave a job_status URL, which I would use shortly.

Side-note / Tip: Remember, in both Caido and Burpsuite you can right-click and toggle GET/POST requests (very handy for testing…). Alas, we continue.

Toggle (context-menu)
Toggle (context-menu)

Performing a simple GET request provided a new token in the response.

Token Response
Token Response

I tried to be clever here and use nc’s verbose mode to connect to the server and it somewhat gave me what I was looking for. When I connected (and didn’t pass the token), it said I was “unauthorized”. However, when I connected and provided the token, I got dropped into a shell as the svc_vidops user.

nc login
nc login

from here, I went to /home (cd /home) and could simply read the .txt file for the 2nd half of flag #2.

flag #2 (second half)
flag #2 (second half)

Asylum Escape Challenge — Flag #3

Whilst still the svc_vidops user, I ran: find / -perm -4000 2>/dev/null

I was looking for suid binaries. I found a file located at /usr/local/bin/diag_shell (the last entry when I ran this command)

diag_shell
diag_shell

I saw the file could be executed by anybody, but was owned by dockermgr. So I did what anyone would do in this situation…and ran it!

la -la
la -la

I was now running as dockermgr. Yay! Well, I didn’t celebrate yet. I needed a 3rd flag. I ran docker ps, only to get an error:

docker error
docker error

The reason for this error was because when I became the dockermgr user, the shell hadn’t refreshed my permissions and wasn’t showing dockermgr as in the “docker” group.

So in the console (as dockermgr), I ran:

newgrp docker

newgrp (read about it here) will spawn/refresh your shell to appear as though the user had logged in, so now I could run commands as if I had logged into dockermgr with a username/password.

Newgrp
Newgrp

So now, I could run:

scada operator
scada operator

from here, (remember, I was looking for a passcode/unlock code), I ran: “cat scada_terminal.py | grep unlock” which found the UNLOCK_CODE

unlock_code
unlock_code

I entered the code from the scada python script:

SCADA Unlock Passcode
SCADA Unlock Passcode

And I had the 3rd flag. Room complete.

Final thoughts? Excellent room. Difficulty was most definitely “hard”. Enjoyable, but was a tough nut to crack considering there were zero hints allowed during the Advent of Cyber for this event.

Thanks for reading!


🍺 Quick message to readers: if my writeups help you, please consider a small donation to my buymeacoffee link here. This is not required but is very much appreciated! 🍺

LinkedIn X YouTube GitHub