你不知道的文件上传漏洞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脚本的10个技巧(1)
Oct 09 PHP
用PHP 快速生成 Flash 动画的方法
Mar 06 PHP
在PHP里得到前天和昨天的日期的代码
Aug 16 PHP
PHP XML备份Mysql数据库
May 27 PHP
PHP随机数生成代码与使用实例分析
Apr 08 PHP
关于php 接口问题(php接口主要也就是运用curl,curl函数)
Jul 01 PHP
PHP curl 获取响应的状态码的方法
Jan 13 PHP
ThinkPHP标签制作教程
Jul 10 PHP
linux下编译安装memcached服务
Aug 03 PHP
PHP实现简单数字分页效果
Jul 26 PHP
zend framework中使用memcache的方法
Mar 04 PHP
基于PHP实现生成随机水印图片
Dec 09 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
phpmyadmin里面导入sql语句格式的大量数据的方法
2010/06/05 PHP
php操作mongoDB实例分析
2014/12/29 PHP
图文详解PHP环境搭建教程
2016/07/16 PHP
PHP结合Ffmpeg快速搭建流媒体服务的实践记录
2018/10/31 PHP
在模板页面的js使用办法
2010/04/01 Javascript
读jQuery之八 包装事件对象
2011/06/21 Javascript
Javascript引用指针使用介绍
2012/11/07 Javascript
验证控件与Button的OnClientClick事件详细解析
2013/12/04 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
JavaScript创建对象的方式小结(4种方式)
2015/12/17 Javascript
js事件驱动机制 浏览器兼容处理方法
2016/07/23 Javascript
JavaScript仿微博输入框效果(案例分析)
2016/12/06 Javascript
微信小程序 石头剪刀布实例代码
2017/01/04 Javascript
Angular.js与node.js项目里用cookie校验账户登录详解
2017/02/22 Javascript
基于Vue实例生命周期(全面解析)
2017/08/16 Javascript
JS实现的简单下拉框联动功能示例
2018/05/11 Javascript
layui实现给某一列加点击事件
2019/10/26 Javascript
js函数柯里化的方法和作用实例分析
2020/04/11 Javascript
Element Steps步骤条的使用方法
2020/07/26 Javascript
go和python调用其它程序并得到程序输出
2014/02/10 Python
深入理解Python3 内置函数大全
2017/11/23 Python
Python和Java进行DES加密和解密的实例
2018/01/09 Python
详解Python下ftp上传文件linux服务器
2018/06/21 Python
对numpy中的transpose和swapaxes函数详解
2018/08/02 Python
python模式 工厂模式原理及实例详解
2020/02/11 Python
input元素的url类型和email类型简介
2012/07/11 HTML / CSS
Forever 21美国官网:美国标志性快时尚品牌
2017/02/20 全球购物
艺龙旅行网酒店预订:国内、港澳台酒店
2018/06/26 全球购物
德国内衣、泳装和睡衣网上商店:Bigsize Dessous
2018/07/09 全球购物
英国家用电器折扣网站:Electrical Discount UK
2018/09/17 全球购物
美国手工艺品市场的领导者:Annie’s
2019/04/04 全球购物
以思科路由器为例你写下单臂路由的配置命令
2013/08/03 面试题
体育老师的教学自我评价分享
2013/11/19 职场文书
2014年党员干部四风问题自我剖析材料
2014/09/29 职场文书
物业前台接待岗位职责
2015/04/03 职场文书
2015年仓库管理工作总结
2015/05/25 职场文书