你不知道的文件上传漏洞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 相关文章推荐
3
Oct 09 PHP
php去除重复字的实现代码
Sep 16 PHP
PHP分多步骤填写发布信息的简单方法实例代码
Sep 23 PHP
php中利用explode函数分割字符串到数组
Feb 08 PHP
php使用百度天气接口示例
Apr 22 PHP
php一维二维数组键排序方法实例总结
Nov 13 PHP
PHP技术开发微信公众平台
Jul 22 PHP
PHP Cookei记录用户历史浏览信息的代码
Feb 03 PHP
PHP程序员的技术成长规划
Mar 25 PHP
Thinkphp通过一个入口文件如何区分移动端和PC端
Apr 18 PHP
php批量修改表结构实例
May 24 PHP
关于PHP转换超过2038年日期出错的问题解决
Jun 28 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
按上下级层次关系输出内容的PHP代码
2010/07/17 PHP
6种php上传图片重命名的方法实例
2013/11/04 PHP
PHP整合PayPal支付
2015/06/11 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
2015/10/26 PHP
PHP获取对象属性的三种方法实例分析
2019/01/03 PHP
JQuery 无废话系列教程(二) jquery实战篇上
2009/06/23 Javascript
jquery实现网页查找功能示例分享
2014/02/12 Javascript
在JavaScript中构建ArrayList示例代码
2014/09/17 Javascript
深入浅析Extjs中store分组功能的使用方法
2016/04/20 Javascript
BootStrap实现响应式布局导航栏折叠隐藏效果(在小屏幕、手机屏幕浏览时自动折叠隐藏)
2016/11/30 Javascript
js获取地址栏中传递的参数(两种方法)
2017/02/08 Javascript
JavaScript+CSS相册特效实例代码
2017/09/07 Javascript
基于vue实现分页效果
2017/11/06 Javascript
详解多页应用 Webpack4 配置优化与踩坑记录
2018/10/16 Javascript
jquery.pagination.js分页使用教程
2018/10/23 jQuery
如何为vuex实现带参数的 getter和state.commit
2019/01/04 Javascript
vue中过滤器filter的讲解
2019/01/21 Javascript
微信小程序开发之map地图组件定位并手动修改位置偏差
2019/08/17 Javascript
基于纯JS实现多张图片的懒加载Lazy过程解析
2019/10/14 Javascript
微信小程序实现图片压缩
2019/12/03 Javascript
JS实现打砖块游戏
2020/02/14 Javascript
vue-cli3 引入 font-awesome的操作
2020/08/11 Javascript
Python中使用platform模块获取系统信息的用法教程
2016/07/08 Python
Python模块文件结构代码详解
2018/02/03 Python
Python 查找字符在字符串中的位置实例
2018/05/02 Python
Django shell调试models输出的SQL语句方法
2019/08/29 Python
使用virtualenv创建Python环境及PyQT5环境配置的方法
2019/09/10 Python
Django ModelForm操作及验证方式
2020/03/30 Python
HTML5-WebSocket实现聊天室示例
2016/12/15 HTML / CSS
德国箱包网上商店:koffer24.de
2016/07/27 全球购物
就业推荐表自我鉴定
2013/10/29 职场文书
贸易经济专业自荐书
2014/06/29 职场文书
县委班子四风对照检查材料思想汇报
2014/09/29 职场文书
优秀教师个人总结
2015/02/11 职场文书
六年级语文教学反思
2016/03/03 职场文书
本地通过nginx配置反向代理的全过程记录
2021/03/31 Servers