7 minutes
Bookstore - TryHackMe Writeup
You're gonna want to read this one!Well hello everyone! After a (long) break, I’m finally back with my first ever writeup for TryHackMe’s Bookstore! This box focuses on web enumeration, API fuzzing, and binary decompiling in order to gain a reverse shell and escalate privileges. Without further ado, let’s get started.
Scanning
As with any box, I’ll start by performing a port scan using a custom script that combines two scanning tools: masscan
and nmap
. Once the scan is finished, I found ports 22, 80, and 5000 to be open on the machine:
One thing of note is the /api
found in robots.txt
of the server on port 5000. I’ll come back to this once I do recon on the port.
Recon - Port 80
The first thing I’ll look at is the web server hosted on port 80. Navigating to the IP address of the web server gives me the following homepage:
As with most CTFs, I’ll browse through each page to see if there any information of interest. For this box, there wasn’t anything of much use on the immediate pages. However, diving into the source of each page using Developer Tools revealed a good amount of useful data.
On the landing page, the website’s HTML and JavaScript files don’t provide any telling information. Moving to the “Books” page, I find an interesting JavaScript file titled api.js
. The script conatains the following code:
After reading through the script, I am able to determine that the webpage is making API calls to port 5000, which is expected due to the robots.txt
entry for that server containing a page called /api
. I also find an interesting comment that notes that an earlier version of the API had a parameter that was vulnerable to Local File Inclusion (LFI). I’ll come back to this later.
The final page, “Login”, also contains an interesting comment in it’s source:
This comment points out that there is a “debugger pin” in a user’s .bash_history
file. While I don’t immediately know what “debugger” the comment is refering to, the note on the .bash_history
file does provide us with a target for the LFI-vulnerable API. This brings about the conclusion of the port 80 recon, so we’ll move on to port 5000.
Recon - Port 5000
To begin, I decide to look up what exactly “Werkzeug” is. Reading through the Werkzeug documentation, I find that Werkzeug has a debugger that can be accessed through the /console
path. The documentation also notes that the debugger can have a pin set. This all but confirms the intersting file in the user’s .bash_history
file contains a pin to give us access to the Werkzeug debugger. To confirm that this console is set up like the documentation lays it out, I navigate to the console path and was greeted with a lock screen.
With this confirmed, I’ll now move to the api part of the site. Travelling to the /api
page I discovered earlier reveals the API landing page with a variety of different API calls that can be made:
I know that this API is vulnerable to LFI, but only on an earlier version. One thing I notice about the URLs for the API calls is that each one has v2
in it. Assuming this is denoting the version, I decide to try to use v1
in its place. Surely enough, navigating to one of the listed URLs with v1
instead of v2
yields a successful API call. With all of this information, I now believe I have a clear path to exploitation.
Exploitation
In order to exploit the LFI issue, I need to find the parameter that is vulnerable to LFI. To do this, I’ll use the wfuzz
application to fuzz valid parameter names and see which ones return a valid response. The target for wfuzz will be http://<BOOKSTORE_IP>:5000/api/v1/books?FUZZ=../../../../../etc/passwd
and I’ll be using the directory-list-2.3-medium.txt
wordlist. With these parameters, I end up finding an interesting parameter that is not used by items returned by the API.
Navigating to that page in a web browser, I can see that the parameter is vulnerable to LFI as it prints out the contents of /etc/passwd/
to the page. Unfortunately, /etc/passwd
does not have any useful information. However, I do know from earlier that the pin for the Werkzeug debugger is in a user’s .bash_history
file. By using this file as the path on the parameter that is vulnerable to LFI, I get the Werkzeug pin.
Now that the pin has been acquired, I can navigate to /console
and access the Werkzeug debugger. In the debugger, I have access to a Python console. This console provides the perfect opportunity to create a reverse shell, so useing a python “one-liner”, I am able to successfully spawn a reverse shell and read user.txt
!
Privilege Escalation
Now that I have a reverse shell, it’s time to look for some privilege escalation opportunities. Luckily for me, the first promising privilige escalation is sitting right in the user’s home directory.
From running the ls -l
and file
commands, I can see that there is a Linux ELF binary called try-harder
that has the SUID bit set and is owned by the root user. Running the program, I’m asked for a “magic number”. Seeing this, I’m thinking that providing the correct number to the program will put me into a root shell. To determine if my theory is correct, I’m going to decompile this program using Ghidra - the NSA’s reversing engineering suite.
After starting a Python web server on the target machine to transfer the binary over, I opened it up in Ghidra and navigated to the “main” function within the “Functions” folder. I can now see the decompiled code written in C.
After looking over the code, I can see that the following happens:
- The UID is set to 0 (root)
- Variable
local_18
is set to a hex value of 0x5db3 - The “magic number” entered by the user is stored in the variable
local_1c
- A mathematicaly function is performed involving
local_18
,local_1c
, and the hex value 0x1116 and is stored in variablelocal_14
- If the the value of
local_14
is equal to the hex value 0x5dcd21f4, then run the command/bin/bash -p
With this in mind, I now need to figure out what the caret (^) operator does and then convert the hex values to decimal. After doing some Google-fu, I found that the caret operator is used for bitwise XOR. This is extremely convenient as the inverse of bitwise XOR is just bitwise XOR! So what does this mean? This means that given the equation:
c = x ^ a ^ b
(where x is the “magic number”)
we can solve for x through the equation:
x = a ^ b ^ c
(order doesn’t matter, so it could be b ^ a ^ c and so on)
After quickly converting the hex values to decimal, I can use the following values to determine the “magic number”:
Now that I know the “magic number”, I can run the binary, enter the number, and gain root access to the machine!
Remediation
Just for kicks, I thought it would be interesting to go over a few things that could be done in order to prevent this machine from getting rooted. The goal here is to provide recommendations that would have minimal to no impact on assumed business processes. With this in mind, heres a quick list of things that need to be done to further secure this machine:
- Remove needless comments about the code/vulnerabilities
- Disable the vulnerable version of the API
- If the vulnerable version must be used, at least disable the vulnerable parameter
- Disable the Werkzeug debugger console
- Werkzeug documentation notes that the debugger should never be enabled in production (link)
- Ensure that no binaries with SUID/SGID bits set can be exploited to escalate privileges.
Conclusion
This box was great - it rewarded thorough investigation and provided me with the opportunity to use wfuzz for the first time. I’m definitely looking forward to the next box I get to root!