Hack the Box Machine: Timing
A fun, medium difficulty machine, that requires us to identify an LFI, read and understand PHP source code, and get code execution after uploading files to the server.
Initial nmap scanning
Our initial scan showed only two ports open. SSH on port 22 and HTTP on port 80. We also see that the server is running Apache and using PHP.
Initial review of the site
The initial page of the site was a log in page for a web app. We did a quick look at the page, source, and tried some common credentials. We didn’t get anywhere with the site, so we started fuzzing directories and files.
Ffuf was used for the initial fuzzing with a medium word list.
Filtering our output, and just looking at the status code 200s, showed 3 files. Login we know, footer displays the footer, but doesn’t leak any useful information, and image.php returns a blank page.
A quick review of our options
We only have a single web application to focus on. We could try to brute force logins, but we don’t know the valid usernames. The site also doesn’t identify, if the username or the password is incorrect, when we try to log in.
Since we have access to the image.php file, we can try to fuzz it, and see if we can find a parameter that changes the response.
Fuzzing with wfuzz
When we fuzz for parameters that provide some form of different response, we get it with the payload “img”.
Browsing to the site to see what response we are getting, indicates that we have some bad character in our payload, that is identified as hacking.
We can test multiple payloads to determine what character is causing the detection.
Test by itself doesn’t cause a warning.
If we add “/” to it, then we again get the warning.
With this knowledge we can try to access a file, that we know exists, in the local directory. By placing a PHP filter, we bypass the hacking warning.
When we decode the base64, we get a database connection string. We don’t have any other usernames yet, but SSH using this password for root doesn’t work.
We can use the same vulnerability to review the /etc/passwd file, and see, the only other user on the system is aaron. The credentials also do not work for logging in with him.
We could now move back to trying to brute force the web app login with the username Aaron, but there is another vulnerability worth mentioning.
Remember, the LFI we have access to, allows us to get source code from any of the web app files. If we review the login.php file, we can see a way to detect valid usernames.
Identifying the time delay for users that exist.
We see in the source, that if a user exists, the app runs the function createTimeChannel(). So, any user that exists, should return a response 1 second slower than a non-existent user.
Verifying the vulnerability in Burp
If we send two requests, one for admin, and another for a user that shouldn’t exit, we get two different response times. 1.263 seconds vs 115 milliseconds. This should verify that admin is a valid user, but johnsmith is not. The same technique will show that aaron is a valid user.
Automating the check with python and a word list to brute force usernames.
We use the first argument on the command line to identify a text file with one name per line. These names are split into a list based on the newline character. Then, each name is past to a for loop, where we send a login request and verify the response times. I found that existing names reliably returned in over 250000 microsec. Our if statement then identifies the valid usernames, or identifies quick responses as invalid.
Running the script verifies that the admin and aaron accounts are valid.
After identifying the users, we can brute force credentials and log in with username aaron and password aaron.
There is only one page in the web interface that we can interact with. Edit profile allows us to set 4 options, but doesn’t move us forward yet.
From our earlier fuzzing we know that there is also an upload file. We can try to access it, but don’t have the required permissions.
To track down how the app is determining our permissions, we can use the LFI to review more source code.
Source Code for Upload.php and the Auth Check
The upload.php file starts with including another file for the auth check.
We can see in this auth_check.php file, that we need a role of 1 to access the upload functionality.
Setting our own role to 1
If we look more in depth at the edit profile page, we see that the response shows more items than we can update from the web interface. One of these is the role, and it is set to 0.
If we test sending the role in the http post request, we can set our own role for the session.
If we refresh after setting the role to 1, we will gain access to an admin panel. The admin panel lets us select and upload a file. If we try to upload a file with a .php extension, it is not allowed, but we can always return to reading the source to see what to do.
There are 3 things that stood out to me in the source code.
First, we can only upload a jpg file, second the file will be stored in the images/uploads directory, and third because of the way the file is named, we should be able to calculate the filename.
We are able to upload php webshell code in a file with a .jpg extension on it.
Now we identify how the file will be named. In the md5 function, the ‘$file_hash’ is actually just a string, not a variable, and time is just the time function when the file was uploaded.
We can use php interactive mode to calculate the time when we uploaded the file. This is based on the time in the Burp response.
Now we combine that with the md5 function.
Now combine the hash with the file name, and we can verify that we can reach the file.
We can’t get code execution like this. The file ends in .jpg, so we need another way to execute code. Once again, back to the LFI. The code for the image.php file (Used for the LFI) is using the include function. This is a vulnerable function in PHP and if we can pass code to it we should get execution.
Verifying Code Execution with a browser
By using the LFI and calling the webshell that we uploaded, we can execute code. I attempted to use the code execution to get a callback with a nc reverse shell, a bash reverse shell, tried using curl or wget to execute code with bash, and tried to put a new file on the remote system without any luck.
I decided instead to have a look at the box using only the webshell and Burp. This allowed me to find a backup.zip file in the /opt directory. We were then able to use wget to download the .zip file to our machine for review.
I renamed the file and unzipped it, to see what information could be found.
The zip file contained the the code for the web app, but also included previous git revisions of the code.
Finding credentials that work for SSH
Looking at the older version differential we can see they changed the db connection string.
With the credentials we can log in using ssh as the aaron user.
Escalating to root
Our user has the ability to run netutils as root.
Testing netutils showed, we could use it to download a file from our attacker machine with http.
Testing again after creating the test file showed it would be downloaded, and owned by root.
This indicated, that we could likely overwrite files owned by root on the remote system. Since we didn’t have access to the root directory, we needed to use a symlink to save a file to the root directory.
A symlink was created in our user home directory, pointing to the root user ssh authorized_keys file, so we could upload a public key. Our public key is named john. We upload it to the system and it is saved to the symlink that was also named john.
After the key was downloaded to the system, we were able to use the private key to log in as root using SSH.