A lot of PHP developers make the mistake of trusting the type field in $_FILES array. It isn’t uncommon to see upload scripts that attempt to validate the file by checking this field.
In this post, I will tell you why this field cannot be trusted. I will also show you a working example of how you can use PHP and cURL to easily spoof the mime type of a file.
What is the “type” field in $_FILES?
The “type” field in the $_FILES array represents the mime type of the file that has been uploaded. A mime type is something that is supposed to describe the file and its structure. For example, if the file is a JPEG image, then the mime type should be “image/jpeg”. If it’s an Excel file, then the mime type will probably be “application/vnd.ms-excel”.
Think of it as a “hint” about the identity of the file.
Why can’t you trust the “type” field?
The problem with the type field in the $_FILES array is that it comes from the client. This means that the person who is uploading the file can change it to anything they want to.
For example: They could change the mime type of a .exe file to “image/jpeg” or they could change a .php file to “text/csv”.
If that happens, your mime type validation check will let the file pass. As a result, an attacker will be free to upload any kind of file that they want to.
Even PHP files…
Spoofing MIME types with cURL and PHP.
In a previous post, I wrote a guide on how to upload files with cURL and PHP. Today, I am going to take that code and modify it to spoof the mime type:
//The URL I am uploading to. $uploadUrl = 'http://localhost/test/upload-script.php'; //Attempting to upload a PHP file. $badFile = 'C:\wamp64\www\test\file.php'; $ch = curl_init($uploadUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Set the MIME type of the PHP file to image/jpeg $badFile = new CURLFile($badFile, 'image/jpeg'); $postFields = array( 'user_file' => $badFile, ); curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); $result = curl_exec($ch); echo $result;
In the code above, I am attempting to upload a PHP file with a fake mime type.
On line 12, I instantiated the CURLFile object and set the second parameter to “image/jpeg”. As a result, cURL will upload my PHP file with the type “image/jpeg”.
A var_dump of the $_FILES array, showing my upload:
Despite the fact that our uploaded file is clearly a PHP file, the mime type is showing as “image/jpeg”. Fortunately, the PHP upload form script that I wrote actually checks the extension of the file. As a result, it did not get through.
However, when I modified the validation rules on that upload script to only check the mime type, the file got through. And I was able to browse to the uploaded file and execute it:
As you can see, the PHP file was moved to the uploads directory and I was able to run it.
If an attacker can do this on your web server, it is extremely bad news.
Always validate the file extension.