你不知道的文件上传漏洞php代码分析


Posted in PHP onSeptember 29, 2016

漏洞描述

开发中文件上传功能很常见,作为开发者,在完成功能的基础上我们一般也要做好安全防护。
文件处理一般包含两项功能,用户上传和展示文件,如上传头像。

文件上传攻击示例

upload.php

<?php
$uploaddir = 'uploads/'; 
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} 
else {
 echo "File uploading failed.\n";
}
?>

upload.html

<form name="upload" action="upload1.php" method="POST" ENCTYPE="multipart/formdata">
Select the file to upload: <input type="file" name="userfile">
<input type="submit" name="upload" value="upload">
</form>

上述代码未经过任何验证,恶意用户可以上传php文件,代码如下

<?php eval($_GET['command']);?>

恶意用户可以通过访问 如http://server/uploads/shell.php?command=phpinfo(); 来执行远程命令

Content-type验证

upload.php

<?php
if($_FILES['userfile']['type'] != "image/gif") {//获取Http请求头信息中ContentType
 echo "Sorry, we only allow uploading GIF images";
 exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

该方式是通过Http请求头信息进行验证,可通过修改Content-type ==> image/jpg绕过验证,可以通过脚本或BurpSuite、fiddle修改
如下
Content-Disposition: form-data; name="userfile"; filename="shell.php"
Content-Type: image/gif

图片类型验证

该方法通过读取文件头中文件类型信息,获取文件类型

备注:如JPEG/JPG文件头标识为FFD8

你不知道的文件上传漏洞php代码分析

upload.php

<?php
$imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
 echo "Sorry, we only accept GIF and JPEG images\n";
 exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

可以通过图片添加注释来绕过此验证。
如添加注释<?php phpinfo(); ?>,保存图片后将其扩展名改为php,则可成功上传。
上传成功后访问该文件则可看到如下显示

你不知道的文件上传漏洞php代码分析

文件扩展名验证

 通过黑名单或白名单对文件扩展名进行过滤,如下代码

upload.php

<?php
$blacklist = array(".php", ".phtml", ".php3", ".php4");
foreach ($blacklist as $item) {
if(preg_match("/$item\$/i", $_FILES['userfile']['name'])) {
 echo "We do not allow uploading PHP files\n";
 exit;
}
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)){
 echo "File is valid, and was successfully uploaded.\n";
} else {
 echo "File uploading failed.\n";
}
?>

当黑名单不全,构造特殊文件名可以绕过扩展名验证

直接访问上传的文件

将上传文件保存在非web root下其他文件夹下,可以防止用户通过路径直接访问到文件。
upload.php

<?php
$uploaddir = 'd:/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.\n";
} else {
echo "File uploading failed.\n";
}
?>

用户不可以直接通过http://localhost/uploads/ 来访问文件,必须通过view.php来访问
view.php

<?php
$uploaddir = 'd:/uploads/';
$name = $_GET['name'];
readfile($uploaddir.$name);
?>

查看文件代码未验证文件名,用户可以通过例如http://localhost/view.php?name=..//php/upload.php,查看指定的文件

解决漏洞示例

upload.php

<?php
require_once 'DB.php';
$uploaddir = 'D:/uploads/'; 
$uploadfile = tempnam($uploaddir, "upload_");
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
$db =& DB::connect("mysql://username:password@localhost/database");
if(PEAR::isError($db)) {
unlink($uploadfile);
die "Error connecting to the database";
}
$res = $db->query("INSERT INTO uploads SET name=?, original_name=?,mime_type=?",
array(basename($uploadfile,basename($_FILES['userfile']['name']),$_FILES['userfile']['type']));
if(PEAR::isError($res)) {
unlink($uploadfile);
die "Error saving data to the database. The file was not uploaded";
}
$id = $db->getOne('SELECT LAST_INSERT_ID() FROM uploads');
echo "File is valid, and was successfully uploaded. You can view it <a href=\"view.php?id=$id\">here</a>\n";
} else {
echo "File uploading failed.\n";
}
?>

view.php

<?php
require_once 'DB.php';
$uploaddir = 'D:/uploads/';
$id = $_GET['id'];
if(!is_numeric($id)) {
die("File id must be numeric");
}
$db =& DB::connect("mysql://root@localhost/db");
if(PEAR::isError($db)) {
die("Error connecting to the database");
}
$file = $db->getRow('SELECT name, mime_type FROM uploads WHERE id=?',array($id), DB_FETCHMODE_ASSOC);
if(PEAR::isError($file)) {
die("Error fetching data from the database");
}
if(is_null($file) || count($file)==0) {
die("File not found");
}
header("Content-Type: " . $file['mime_type']);
readfile($uploaddir.$file['name']);
?>

上述代码文件名随机更改,文件被存储在web root之外,用户通过id在数据库中查询文件名,读取文件,可以有效的阻止上述漏洞发生

总结

通过以上示例分析,可总结一下几点

1.文件名修改,不使用用户上传的文件名

2.用户不可以通过上传路径直接访问文件

3.文件查看采用数据库获取文件名,从而在相应文件服务器读取文件

4.文件上传限制文件大小,个人上传数量等

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP中str_replace函数使用小结
Oct 11 PHP
超小PHP小马小结(方便查找后门的朋友)
May 05 PHP
PHP mysql与mysqli事务使用说明 分享
Aug 17 PHP
php二维数组排序详解
Nov 06 PHP
yii框架配置默认controller和action示例
Apr 30 PHP
PH P5.2至5.5、5.6的新增功能详解
Jul 14 PHP
ThinkPHP做文字水印时提示call an undefined function exif_imagetype()解决方法
Oct 30 PHP
php表单敏感字符过滤类
Dec 08 PHP
php+mysqli批量查询多张表数据的方法
Jan 29 PHP
PHP查看当前变量类型的方法
Jul 31 PHP
PHP进程通信基础之信号
Feb 19 PHP
ThinkPHP防止重复提交表单的方法实例分析
May 10 PHP
PHP的Json中文处理解决方案
Sep 29 #PHP
PHP二分查找算法示例【递归与非递归方法】
Sep 29 #PHP
PHP快速排序quicksort实例详解
Sep 28 #PHP
PHP实现QQ快速登录的方法
Sep 28 #PHP
PHP自定义错误用法示例
Sep 28 #PHP
PHP构造函数与析构函数用法示例
Sep 28 #PHP
PHP设计模式之工厂模式与单例模式
Sep 28 #PHP
You might like
全国FM电台频率大全 - 9 上海市
2020/03/11 无线电
php 404错误页面实现代码
2009/06/22 PHP
php计算两个日期时间差(返回年、月、日)
2014/06/19 PHP
php对文件进行hash运算的方法
2015/04/03 PHP
微信公众平台实现获取用户OpenID的方法
2015/04/15 PHP
PHP pthreads v3下同步处理synchronized用法示例
2020/02/21 PHP
javascript下使用Promise封装FileReader
2016/02/19 Javascript
AngularJs学习第八篇 过滤器filter创建
2016/06/08 Javascript
浅谈jquery中的each方法$.each、this.each、$.fn.each
2016/06/23 Javascript
JS敏感词过滤代码
2016/12/23 Javascript
JS实现520 表白简单代码
2018/05/21 Javascript
vue中锚点的三种方法
2018/07/06 Javascript
vue-cli设置publicPath小记
2020/04/14 Javascript
Python和php通信乱码问题解决方法
2014/04/15 Python
在Gnumeric下使用Python脚本操作表格的教程
2015/04/14 Python
介绍Python的Django框架中的静态资源管理器django-pipeline
2015/04/25 Python
详解Python Socket网络编程
2016/01/05 Python
Python编码类型转换方法详解
2016/07/01 Python
Python 判断图像是否读取成功的方法
2019/01/26 Python
Python标准库使用OrderedDict类的实例讲解
2019/02/14 Python
python简单实现矩阵的乘,加,转置和逆运算示例
2019/07/10 Python
详解在Python中以绝对路径或者相对路径导入文件的方法
2019/08/30 Python
python使用Geany编辑器配置方法
2020/02/21 Python
Python列表如何更新值
2020/05/27 Python
python中round函数保留两位小数的方法
2020/12/04 Python
哄娃神器4moms商店:美国婴童用品品牌
2019/03/07 全球购物
幼儿园中班新学期寄语
2014/01/18 职场文书
医校毕业生自我鉴定
2014/01/25 职场文书
中文师范生自荐信
2014/01/30 职场文书
装饰工程师岗位职责
2014/06/08 职场文书
校庆口号
2014/06/20 职场文书
农村文化活动总结
2014/08/28 职场文书
网吧七夕活动策划方案
2014/08/31 职场文书
2014年图书管理员工作总结
2014/12/01 职场文书
幽灵公主观后感
2015/06/09 职场文书
2016年小学圣诞节活动总结
2016/03/31 职场文书