HackTheBox “BountyHunter” Walkthrough
BountyHunter, an easy-level Linux OS machine on HackTheBox, we will discover the intricacies of a straightforward yet potent XXE vulnerability nestled within a webpage, offering a gateway to accessing crucial files on the host. Unravel the secrets within PHP scripts, extracting user credentials and paving the path to SSH access. Delve deeper into the privilege escalation, where a ticket validation script, running with root privileges, succumbs to the prowess of Python eval injection.
Let’s get started! 🚀
Recon & Enumeration
Let’s use nmap to full scan for open ports and services:
Let’s check the port 80.
Check the portal subdirectory.
It indicates the ongoing development phase and offers a URL for testing the lab environment.
We found a data-entry form that saves input externally, resembling a beta report submission system. Let’s examine the page source.
This resembles a typical HTML page. Time to inspect the bountylog JavaScript file.
It’s a form allowing value input, with data storage elsewhere. Upon inspecting the page source, we noted XML structure. This suggests vulnerability to XXE (XML External Entity). To gather more insights, we employed dirsearch.
Exploitation:
Open BurpSuite and navigate to the portal link then proceed to the Bounty Report System.
Submit a test report and monitor the request in Burp.
The field above are encoded in base64 to construct an XML structure, enabling a vulnerability check for XXE.
<?xml version="1.0" encoding="ISO-8859-1"?>
<bugreport>
<title>test</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Transfer the request to the repeater and insert the encoded payload below into the data field after encoding it to Base64 and ensuring URL compatibility.
In the repeater section, we substitute our request’s data field with our payload before sending it out.
Let’s attempt to extract the contents of the db.php
file obtained through dirsearch tool above.
Since it's a PHP file, direct viewing isn't possible as it gets executed.
To access its content, we'll utilize the PHP wrapper php://filter/convert.base64-encode/resource
, converting the PHP file's content into base64 and displaying it. For more information on PHP wrappers, refer to this article.
Let's adjust our payload to retrieve the content of the db.php
file.
In the repeater section, we substitute our request’s data field with our payload again before sending it out.
After decoding the information from the db.php file, we obtain the following data.
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>
Verify SSH access. Couldn’t log in with the admin username but examining the extracted file /etc/passwd above reveals a user named “development”.
Privilege Escalation:
Let’s check what privileges we have.
The $sudo -l
command indicates that the user development
has root permission to execute ticketValidator.py
. Let's inspect its contents.
development@bountyhunter:~$ 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 eval()
function in Python is powerful but potentially dangerous if not used carefully. It allows execution of any valid Python expression or code, making it susceptible to Python Code Injection attacks if malicious input is provided. To safeguard against this, rigorous data validation must precede its use dynamically in applications. For further insights into this vulnerability, refer to this article.
We found four invalid tickets “md files” in the python script directory:
The script indicates the ticket is invalid.
The script’s use of the eval()
function without sanitization allows for execution of arbitrary Python expressions, presenting an opportunity for OS command injection. By crafting expressions like __import__('os').system('/bin/bash')**
and concatenating them with other input strings using the and
operator, we can exploit this vulnerability to escalate privileges.
I gained root shell access by creating a file named “root.md” in the “development” user’s home directory depending on the structure of the invalid tickets found above.
Then, I invoked root.md while running “ticketValidator.py” with root privileges.
Cheers.