BountyHunter is an easy rated machine on HackTheBox created by ejedev. For the user part we will abuse a XXE vulnerability in a Bounty Report System
to read the source of the website containing credentials for ssh access. Once on the machine we are able to run a python script as root which passes some of our input to an eval statement, thus allowing for arbitratry code execution as the root user.
User
Nmap
As always we begin our enumeration on the machine with a nmap scan against all ports, followed by the script and version detection scan for the open ones to gain an initial picture of the attack surface.
All ports scan
1 |
|
Script and version scan
1 |
|
XXE
Only two ports are open, with http being the more likely succesfull entry, so we will start there. Browsing to it we see a Bounty Hunters
home page.
Going over to portal it states that this part is under development and we can test the bountry tracker. Applications being under development are always quite interesting due to a higher chance of flaws in their code.
In the bountry tracker we can add values for Exploit Title
, CWE
, CVSS Score
and Bounty Reward ($)
.
Intercepting the request and looking at it in Burp repeater we see it is base64 encoded data, however the X-Requested-With
already gives away what it is decoded.
Decoding it indeed reveals the data in XML structure.
Testing for basic forms of XXE we add our own DTD with an entity consisting of the /etc/passwd
file. Since the output of our XML request mirrors the input, it should display the passwd
file if we replace a field with the earlier defined entity.
Base64 and URL-encoding the data again we can see that it is indeed vulnerable after sending the request. This also reveals the user account development
. All we might need now is either a password or an ssh key.
Since we can read local files with the XXE we have to figure out which ones we want to read. The user does not seem to have a private ssh key, but another interesting thing to take a look at is the source code of the web app. To discover most of the pages we can run a gobuster scan against it with the php
extension.
1 |
|
The db.php
looks interesting but since php code get’s executed and not displayed we cannot use the file://
wrapper to include it. There exists however a wrapper that is commonly used in LFI scenarios to circumvent this problem. With php://filter/convert.base64-encode/resource
we can base64 encode the source code so it does not get run by the server on including it.
Decoding it again the source reveals the password for the database access.
1 |
|
This password is also reused for the development account which gives us ssh access to the machine and we can grab the user flag.
1 |
|
Root
Looking at sudo permission we can see that development can run /opt/skytrain_inc/ticketValidator.py
with python3.8 as root.
1 |
|
This script takes a filepath, opens the file at this location, performs some checks on it and eventually passes a piece of it to the eval function. The interesting part in this python script is the portion where eval get’s called. So we need to create a file that passes the check up to this point and place some code at the correct place for it to get evaled.
/opt/skytrain_inc/ticketValidator.py
1 |
|
First the script calls load_file
which is defined earlier. All this does is check if the file ends with .md
and exits if it doesn’t. Next evalute
is called which has multiple checks in it. Basically it goes through it line by line and has a new check for each line. After going through the code we can break down how our file needs to look.
- First line starts with
# Skytrain Inc
- Second line starts with
## Ticket to
and needs another space followed by something arbitratry. - Third line starts with
__Ticket Code:__
- Fourth line has to start with
**
there also has to be a+
in it, where the part before it get’s parsed byint()
to a number that results in 4 if taken mod 7. The rest of this line needs to be that code which we want to get evaluated.
A possible way to achive root from here is to just import os with the __import__()
function and then call bash with system
.
root.md
1 |
|
With everything prepared, running the script with sudo and specifying our file, we dropp into a root shell and are able to read the flag.
1 |
|