Some days ago I saw a vulnerable website. ImageShare site of my friend. I don’t use websites like this. I just checked it and I found a bug. After my discovery, I looked into that matter. That was deeper than I thought.
<h1>Upload a new image</h1>
<form action="?menu=upload" method="post" enctype="multipart/form-data">
<input name="my_str" type="text" value="TestString">
<input name="my_file" type="file">
<input type="submit" value="Send">
</form>
</pre>
And the handler on server-side:
<?php
if(isset($_FILES) && array_key_exists('my_file', $_FILES)) {
if (strtolower($_FILES['my_file']['type']) === 'image/jpeg') {
$name = 'testfiles/'.time().'_'.md5($_FILES['my_file']['name']).'.jpeg';
move_uploaded_file($_FILES['my_file']['tmp_name'], $name);
echo "<a href="{$name}">{$name}</a> uploaded...";
} else {
echo 'only jpeg!';
}
} else {
echo 'Upload error!';
}
Ok… What exactly happened?
Client:
- load the upload form
- select a file
- add title
- click on the submit button
- send request to the server
Server:
- gets the request
- calls php file
- does something before the highlighted code
- checks existence of the
$_FILES
array - checks
my_file
key exists in$_FILES
array - if (4) or (5) is false: prints out an error message and “exit”
- checks MIME-type of the file
- if MIME-type is not an
image/jpeg
: prints an error message and “exit” - generates a target filename from current timestamp, md5 hash of the filename and adds the jpeg extension
- moves temporary file to the target place
- prints a link of the file
Where is the bug?
What do you think, is the MIME-type filled up by the server? Or is it sent by the client? Yes, of course it is sent by the client =) Are you surprised? Client (?) sends the content, name and type of the file. Client or Attacker…
What is a request to upload files like?
POST /php_upload_vul/upload.php HTTP/1.1
Host: www.target_domain.tld
Origin: hxxp://www.target_domain.tld
Content-Length: {CONTENT_LENGTH_OF_THE_REQUEST}
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us)
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytdQDXALfOP7FtIa6
Accept: text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Referer: hxxp://www.target_domain.tld/php_upload_vul/index.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
------WebKitFormBoundarytdQDXALfOP7FtIa6
Content-Disposition: form-data; name="my_str"
This is the value of the `my_str` input field
------WebKitFormBoundarytdQDXALfOP7FtIa6
Content-Disposition: form-data; name="my_file"; filename="Name of the file.jpeg"
Content-Type: image/jpeg
{CONTENT_OF_THE_FILE}
------WebKitFormBoundarytdQDXALfOP7FtIa6--
What does an attacker do?
POST /php_upload_vul/upload.php HTTP/1.1
Host: www.target_domain.tld
Origin: hxxp://www.target_domain.tld
Content-Length: {CONTENT_LENGTH_OF_THE_REQUEST}
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us)
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytdQDXALfOP7FtIa6
Accept: text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Referer: hxxp://www.target_domain.tld/php_upload_vul/index.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
------WebKitFormBoundarytdQDXALfOP7FtIa6
Content-Disposition: form-data; name="my_str"
Photo about my ass
------WebKitFormBoundarytdQDXALfOP7FtIa6
Content-Disposition: form-data; name="my_file"; filename="very_funny_photo.jpeg"
Content-Type: image/jpeg
<!--?php phpinfo(); /* or some evil code */ ?-->
------WebKitFormBoundarytdQDXALfOP7FtIa6--
Yes… You can put a php code into the content of the file if you want. Now if I am able to make the system to require this file… I will be the puppet master.
Conclusion
- Never forget to check the extension of the file.
- Never forget to check the MIME-type of the file.
- Never forget to check EXIF (or similar) datas of the file (eg: width, height). (Oh yeah… This can be tricked.)
- If you are paranoid then try to load images with GD.
- Never forget to check any of the uploaded images can’t be required.
- Never forget to check configuration of you web-server (apache, nginx).
- If you are more paranoid than (4) … …
- DO NOT make a website.
- DO NOT host any website.
- DO NOT use any website.
- DO NOT breathe and DO NOT blow it out.