As part of my university work, I am working through courses on HackTheBox. This is so I can add these courses and the skill assessments to my portfolio. In this brief writeup I will show how I went about performing the skills assessment for the file upload attack course.
Exploring
We are presented with the following page upon accessing the webpage hosted by the target machine. We can see various categories, all of which do nothing when clicked on. We have a features page, this can also not be accessed. We do, however, have a contact page.
When accessing this contact page, we are met with the following screen. We see a form where we can input a name, email and message, as well as upload an image. While the form itself is of interest, we can see that the file upload is what we came here for.
If we send in a sample .jpg image of a cat, we can see that the request data is the following:
A POST request is made where the image is uploaded, while the form data is uploaded separately by a GET request. The uploaded image is also returned as base64 encoded data. This means we cannot see where the image is hosted on the webserver.
Let’s start by fuzzing the data with several PHP file extensions and see if anything gets through. We will swap the data of the image with an XXE exploit and add some magic bytes to the front to have it recognized as an image file. This way, we can try and get access to the upload.php file and see what checks are being performed. This way, we can perfectly bypass the checks.
We can see that the data returned is still encoded as base64. This is not very handy for us, because we cannot use it at this point. Oddly enough, removing the magic bytes actually returns the data of upload.php as base64 encoded data.
If we decrypt this base64 code, we can see the full code of upload.php!
<?php
require_once('./common-functions.php');
// uploaded files directory
$target_dir = "./user_feedback_submissions/";
// rename before storing
$fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]);
$target_file = $target_dir . $fileName;
// get content headers
$contentType = $_FILES['uploadFile']['type'];
$MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']);
// blacklist test
if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) {
echo "Extension not allowed";
die();
}
// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
echo "Only images are allowed";
die();
}
// type test
foreach (array($contentType, $MIMEtype) as $type) {
if (!preg_match('/image\/[a-z]{2,3}g/', $type)) {
echo "Only images are allowed";
die();
}
}
// size test
if ($_FILES["uploadFile"]["size"] > 500000) {
echo "File too large";
die();
}
if (move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $target_file)) {
displayHTMLImage($target_file);
} else {
echo "File failed to upload";
}
At this point, we can use this knowledge to perfectly bypass any checks performed on the inputted data. As we can see below, the contents of /etc/passwd are returned in plaintext:
Now, if we attempt to enter our shellcode, we can upload it. Since we know where it is being uploaded, we can also access it as we normally would.
When we access it, we can see that the magic bytes fail to display, but are added to the output. Just the same, our command is also outputted.
We can see that the flag is in the root directory, too. When we cat the info out of it, we get the flag.
And when we input the flag into HTB as our answer, we can see it is correct and finish the exercise.
More writeups?
If you enjoyed this writeup or have questions, please leave a comment!
Want to read more (HackTheBox) writeups? Check them right here!