HTB Series – Bounty Hunter Writeup

Hello, Today were going to be going into an older retired box that I haven’t had time to upload until now. It is an easy Linux box. Lets get started!

Foothold

Well start off with an nmap scan and find two ports open, 22 and 80.

Port 80 has an apache 2.4.41 server running. 80 will probably net us the lowest hanging fruit so lets start taking a look at the service. I started fuzzing for pages and directories in the background while taking a look at the site, but could not find anything. After fuzzing the site, I found a few directories with files in them namely bountylog.js. After looking through this javascript code, it seems to make a POST request to tracker_diRbPr00f314.php with an xml document.

From here I have a really good feeling that we could exploit XXE. Through the fuzzing we also found portal.php, which then leads us to log_submit.php and submit a request. Using burpsuite to intercept the request, it looks like the data is base64 encoded, then url encoded. Before we send the POST request to tracker_diRbPr00f314.php, we modify the data to see if it is vulnerable to XXE…

Getting our response back, it looks like we have XXE on the machine!

With /etc/passwd in our hands, lets make note of the user named development, which is likely the unprivileged user on this box. Another file that looked interesting was db.php, however accessing it from the website itself did not prove bountiful. Using this XXE we can grab the file that is in the webroot (/var/www/html/db.php) and we get the database password.

From here, it looks to me like this password could be reused, on say maybe the unprivileged user account. This allows us to ssh in as development on the box.

Privilege Escalation

Usually the first thing I will check as a user on a machine are what privileges I have. In this search, the first thing I run is sudo -l, which gives me an idea of what I can run as a superuser. We come across sudo /usr/bin/python3.8 /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()

Reading through this ticketvalidator code, it looked like we needed to have a few things so that a well crafted file would work and make this possibly exploitable. It needed to end in .md, needed to have markdown formatting for Skytrain Inc, Ticket to, and Ticket Code. Finally the ticket code had to have a remainder of 4 and the validation number had to be bigger than 100. We created a successful working ticket, but couldn’t get root access with just the ticket. We could however, append python code to the end of the ticket that would be executed by python, giving us root.

and __import__('os').system('/bin/bash') == False

Now that we have root, we could grab the flag and submit this machine as owned! Thank you for reading through this post. I will be posting another older retired machine this week, and will be posting writeups for machines that I have owned currently as soon as they retire.