10.03
Summary
Scripts using PHP 5.3 that accept multiple file uploads in a single
request are potentially vulnerable to a directory traversal attack.
Information about the mechanism for attack (corrupting array indices in
$_FILES
) has been publicly available since at least March 2011
June 2009. [1] [2] [3] [4] I submitted Sec Bug #55500
to point out the potential for directory traversal on August
24th, 2011.
[Note: I’ve been informed that a similar attack using the same vector was mentioned in the PHP Bug Tracker in September 2009. [5]]
[Update: As of January 1st 2012, a fix for this issue has been committed for PHP 5.4 and trunk in SVN r321664]
How does it work
The NYU Poly ISIS Lab blog has a wonderful, detailed writeup
discussing how it’s possible to cause PHP to mangle indices in the
$_FILES
array. In short, multi-part POST requests containing variables
with extra opening square brackets will cause PHP to build a malformed
$_FILES
array.
All of the previous writeups on this topic have focused on attacks where
the target script is using copy
instead of move_uploaded_file
to
handle uploads. While that assumption does allow for some interesting
exploitation, it’s a less realistic scenario. I decided to focus
exclusively on the implications of an attack if a target uses the
move_uploaded_file
function.
Consider the following script, based on code from the PHP documentation: [6] [7]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php
print_r($_FILES);
if (!empty($_FILES['pictures']))
{
// Modified slightly from http://php.net/manual/en/function.move-uploaded-file.php
$uploads_dir = '.';
foreach ($_FILES["pictures"]["error"] as $key => $error) {
if ($error == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
$name = $_FILES["pictures"]["name"][$key];
echo "move_uploaded_file($tmp_name, \"$uploads_dir/$name\");";
}
}
}
?>
<form action="" method="POST" enctype="multipart/form-data" >
<input type="hidden" name="MAX_FILE_SIZE" value="10000000">
<input type="file" name="pictures[[type]">
<input type="file" name="pictures[[name]">
<input type="file" name="pictures[name][">
<input type="submit" value="submit">
</form>
|
If you play around with sending requests to this script, you’ll see that
it wants to move the contents of the file uploaded as pictures[[type]
into a location specified by pictures[name][
‘s Content-Type. Since an
uploaded file’s Content-Type is not sanitized by PHP, any attacker can
send a request with any value, allowing for directory traversal.
But you shouldn’t accept an unsanitized filename from users! The bug is in the script, not the language!
Yes. And no.
Yes, leaving file uploads completely unsanitized is a bad idea. The code sample I posted above is vulnerable to a number of different attacks in its current state, the most glaring of which is that an attacker can upload arbitrary PHP files.
However, under normal circumstances PHP sanitizes the name parameter of
the $_FILES
array to prevent directory traversal attacks. That’s a
language feature which developers may be relying on for security and
which PHP’s maintainers have released security patches for in the past.
Just a few months ago, Krzysztof Kotowicz discovered an off-by-one
error in PHP’s file upload sanitization routine which made it possible
for an attacker to write to the root of a drive. That bug,
#54939, was fixed in PHP 5.3.7.
Workarounds
If you have a vulnerable script, you can mitigate the attack by calling
basename
on the user-provided filename. It should also be possible
to detect malicious requests because of the mangled index names.
Edit: Adam pointed out an older bug report referencing the array corruption issue and potential for security concerns. I’ve updated the post to reflect the new information.
Comments