上传漏洞【高危】
原理概述
应用程序因为业务需要,提供了上传文件的功能,但是在上传点处对所上传的文件安全检查不够严格甚至不做检查,导致黑客可以上传任意文件,进而获取网站控制权甚至是服务器控制权。
危害
上传的文件若为web脚本文件,服务器的web容器解释并执行了黑客上传的脚本,导致黑客直接获取网站控制权
上传文件为crossdomainxml,则黑客能控制flash在该域下的行为
上传文件为病毒,木马等,黑客可以诱骗用户或者管理员下载执行
···
BadCase样例
BadCase样例1:
/**
对上传的文件不做任何检查,黑客可以直接上传任意文件,最常见的利用方式就是上传一个具有文件
操作功能的脚本,直接获得网站控制权
**/
if($_FILES)
{
$dest="/upfilestore/";
move_uploaded_file($_FILES['upfile']['tmp_name'],$dest$_FILES['upfile']['name']);
}
BadCase样例2:
/**
本例看似对上传的文件做了类型检查,非图片类型的则不允许上传
但$file['type']是从客户端获取而来的数据,黑客可以将数据包截获下来更改http header中
content-type的值来绕过检查
**/
$dest="/upfilestore/";
$uptypes=array(
'image/jpg',
'image/jpeg',
'image/png',
'image/gif'
);
$file=$_FILES['upfile'];
if(!in_array($file['type'],$uptypes))
{
die("invalid types!");
}
else
{
move_uploaded_file($file['tmp_name'],$dest$file['name']);
}
BadCase样例3:
/**
本例使用了黑名单来检查上传的文件名后缀,如果是黑名单中的类型则禁止上传
但是黑名单是一种很不好的思想,存在被绕过的风险
本例中黑客可以上传xxxphp4,xxxphp3,xxxasa,xxxpl等类型的文件都可以绕过检查
**/
function getExt($filename)
{
return substr($filename,strripos($filename,"")+1);
}
$disAllowExt=array('php','asp','aspx','jsp','exe');
$fileExt=strtolower(getExt($_FILES['upfile']['name']));
if(in_array($fileExt,$disAllowExt))
{
die("disallowed type");
}
else
{
$dest="/upfilestore/";
move_uploaded_file($_FILES['upfile']['tmp_name'],$dest$_FILES['upfile']['name']);
}
BadCase样例4
/**
在PHP版本<4并且magic_quotes_gpc=off的情况下,即使是使用白名单检测文件名后缀也是
存在被绕过的风险的
黑客可以提交hackphp\jpg, 其中\0代表0x00,这样能绕过文件名后缀的检查,也能由于0x00的
截断作用使得最终保存在服务器上的为hackphp
本例中黑客可以提交file=hackphp%00,然后上传一个后缀为jpg的文件,最终生成的就是hackphp文件
**/
error_reporting(0);
if(isset($_FILES))
{
$ext_arr = array('flv','swf','mp3','mp4','3gp','zip','rar','gif','jpg','png','bmp');
$file_ext = substr($_FILES['upfile']['name'],strrpos($_FILES['upfile']['name'],"")+1);
if(in_array($file_ext,$ext_arr))
{
$tempFile = $_FILES['upfile']['tmp_name'];
$targetPath = $_SERVER['DOCUMENT_ROOT']$_REQUEST['file']rand(10, 99)date("YmdHis")""$file_ext;
if(move_uploaded_file($tempFile,$targetPath))
{
echo 'upload success''<br>';
}
else
{
echo("upload failed!");
}
}
else
{
echo("upload failed");
}
}
修复建议
对于上传的文件,一定要做文件类型检查,且一定要在服务端做检查,如果只在前端用JS检查则完全可以被绕过
上传之后的文件,需要对文件重命名为一个随机文件名
如果PHP版本小于4则需要对上传的文件名进行转义,防止被截断上传
样例如下:
$file=$_FILES['upfile'];
//检查PHP版本,如果小于4则要检查magic_quotes_gpc选项的值,如果该值关闭,则要对上传
//的文件名转义
if(PHP_VERSION<'4')
{
if(!ini_get('magic_quotes_gpc'))
{
$file['name']=addslashes($file['name']);
}
}
//白名单控制允许上传的文件名后缀
$allowedExt=array('jpg','gif','jpeg','pdf');
$fileExt=pathinfo($file['name'],PATHINFO_EXTENSTION);
if(!in_array($fileExt,$allowedExt))
{
die('disallowed type');
}
else
{
$dest='/upfilestore/';
//将文件重命名一个随机文件名
$filename=md5(rand())date("YmdHis")$fileExt;
move_uploaded_file($file['tmp_name'],$dest$filename);
}
如果只允许上传图片类型的文件,那么可以使用PHP的GD库对上传的文件的内容做二次渲染,从而可以剔除文件内容中非图片的数据,防止黑客在正常图片中注入恶意代码
样例如下:
首先需要在PHPini中设置启用GD库的扩展,找到extension=php_gddll将其前面的分号(;)去掉即可
; Windows Extensions
; Note that ODBC support is built in, so no dll is needed for it
; Note that many DLL files are located in the extensions/ (PHP 4) ext/ (PHP 5)
; extension folders as well as the separate PECL DLL download (PHP 5)
; Be sure to appropriately set the extension_dir directive
;
;extension=php_bzdll
extension=php_curldll
;extension=php_fileinfodll
extension=php_gddll
代码如下:
$file=$_FILES['upfile'];
$dest='/upfilestore/'; //存放上传文件的路径,各产品线根据自己的实际情况进行设置
//检查PHP版本,如果小于4则要检查magic_quotes_gpc选项的值,如果该值关闭,则要对上传
//的文件名转义
if(PHP_VERSION<'4')
{
if(!ini_get('magic_quotes_gpc'))
{
$file['name']=addslashes($file['name']);
}
}
//白名单控制允许上传的文件名后缀,这里只允许上传图片
$allowedExt=array('jpg','gif','jpeg');
$fileExt=pathinfo($file['name'],PATHINFO_EXTENSTION);
if(!in_array($fileExt,$allowedExt))
{
die('disallowed type');
}
else
{
if($fileExt=='jpg'||$fileExt=='jpeg')
{
$ext='jpeg';
}
else
{
$ext='gif';
}
//重新渲染图片内容,这里默认采用原始大小,因此不做截取操作,各产品线可根据需要自行截取大小
$createFun='imagecreatefrom'$ext;
$newimageFun='image'$ext;
$im=$createFun($file['tmp_name']);
//给文件重命名一个随机文件名
$filename=md5(rand())date("YmdHis")$ext;
//保存文件到指定位置
$newimageFun($im,$dest$filename);
//释放内存
imagedestroy($im);
}