We covered a demo of XML External Entity Injection along with privilege escalation through exploiting Python eval function. This was part of HackTheBox BountyHunter CREST CRT Track.
BountyHunter is an easy Linux machine that uses XML external entity injection to read system files. Being able to read a PHP file where credentials are leaked gives the opportunity to get a foothold on system as development user. A message from John mentions a contract with Skytrain Inc and states about a script that validates tickets. Auditing the source code of the python script reveals that it uses the eval function on ticket code, which can be injected, and as the python script can be run as root with sudo by the development user it is possible to get a root shell.
Initial Enumeration & Discovery
I started with an Nmap scan, which revealed two open ports: 22 (SSH) and 80 (HTTP). I then performed a Gobuster scan on the web server’s directory structure, identifying interesting files, including db.php
. The website had a “Portal” section that was under development but linked to a “Bounty tracker.” Submitting information through the Bounty tracker form resulted in an error message indicating the database wasn’t ready, and the input was returned as output. Navigating directly to db.php
showed a blank page.
Exploiting XML External Entity (XXE) Injection
I intercepted the form submission request using Burp Suite. The submitted data was base64 encoded and then URL encoded. Decoding it revealed the data was in XML format. This discovery led me to test for XML External Entity (XXE) injection.
I crafted an XXE payload to read the /etc/passwd
file and inserted it into the “title” field of the XML structure. The XML payload was base64 encoded and then URL encoded before being sent in the request. The server responded with the contents of the /etc/passwd
file, confirming the XXE vulnerability and revealing a user named “development.”
I then modified the XXE payload using a PHP filter to read the db.php
file. This successfully retrieved the database credentials (username and password).
Gaining Initial Access & Privilege Escalation
I used the retrieved database password to log in as the “development” user via SSH. I found the user flag in the development user’s home directory. A note (contracts
) in the user’s home directory mentioned an “internal tool” and issues with “tickets failing validation.”
The internal tool was located at /opt/SkyTerrainCorporation/ticket_validator
. The ticket_validator
script was owned by root and could only be run by the development user with sudo
privileges without a password, using a specific command format. Running the tool prompted for the path to a ticket file.
I examined the script’s code using nano /opt/SkyTerrainCorporation/ticket_validator
. The script checked for specific formatting in the ticket files:
- Must have an
.md
extension. - The first line must start with
# SkyTerrain
. - A line must contain
Ticket Code:
. - A specific number in the ticket code line, when divided by 7, must result in 4 (meaning the number should be 28, though the video later used 32, which the script accepted).
Crucially, the script used the eval()
function in Python, which is vulnerable to code injection.
I crafted a valid ticket file to meet the script’s criteria. I then injected a Python payload (__import__('os').system('id')
) into the ticket file. Running the ticket_validator
script with the crafted ticket executed the injected command as root, confirming the vulnerability. I then changed the payload to __import__('os').system('bash')
to get a root shell. This successfully escalated privileges to root, and I obtained the root flag.
Technical Commands
Here are the technical commands I used:
nmap <target_ip>
gobuster dir -u <target_url> -w <wordlist_path> -x php
base64 -w0 <filename>
ssh development@<target_ip>
pwd
cat user.txt
cat contracts
ls /opt
cd /opt/SkyTerrainCorporation
ls
./ticket_validator
(attempted, permission denied)ls -la
sudo -l
sudo /usr/bin/python3 /opt/SkyTerrainCorporation/ticket_validator
ls /opt/SkyTerrainCorporation/invalid_tickets
cat /opt/SkyTerrainCorporation/invalid_tickets/3.md
nano /opt/SkyTerrainCorporation/ticket_validator
cd /dev/shm
touch test.md
nano test.md
id
(as part of the python payload execution)bash
(as part of the python payload execution)id
(after getting root shell)ls /root
cat /root/root.txt