当前位置: 首页 > 图文教程 > 网络编程 > PHP > windows环境下uploaded_file函数如何饶过缺陷

PHP
一贴学会PHP 新手入门教程
用PHP的ob_start() 控制您的浏览器cache
谈谈新手如何学习PHP 默默经典版本
黑夜路人出的几道php笔试题
一些 PHP 管理系统程序中的后门
用php获取本周,上周,本月,上月,本季度日期的代码
PHP 简单数组排序实现代码
PHP 多维数组排序实现代码
php 全局变量范围分析
php_xmlhttp 乱码问题解决方法
PHP 数组学习排序全接触
php Sql Server连接失败问题及解决办法
PHP 翻页 实例代码
php 随机数的产生、页面跳转、件读写、文件重命名、switch语句
PHP 5.3.0 安装分析心得
php 生成WML页面方法详解
php 取得瑞年与平年的天数的代码
php empty函数 使用说明
php natsort内核函数浅析
PHP 源代码分析 Zend HashTable详解

PHP 中的 windows环境下uploaded_file函数如何饶过缺陷


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-11-03   浏览: 165 ::
收藏到网摘: n/a

在早期的PHP版本中,上传文件很可能是通过如下的代码实现的:

......
if (isset($_FILES['file'])) {
$tmp_name = $_FILES['file']['tmp_name'];
}
if (file_exists($tmp_name)) {
copy($tmp_name,$destfile);
}
......
但是很可能会被伪造一个$_FILES['file']数组出来,如果tmp_name的内容会被指定为/etc/passwd等敏感信息的内容,那么很容易出现安全问题。PHP在后来的版本中用is_uploaded_file() 和 move_uploaded_file()解决了这个问题,用is_uploaded_file()不仅会检查$_FILES['file']['tmp_name']是否存在,而且会检查$_FILES['file']['tmp_name']是否是上传的文件,这样就使得伪造$_FILES变量变得不可能,因为脚本会在检查到$_FILES['file']['tmp_name']不是PHP上传的时候终止执行。
伪造变得不可能了么?在很多的脚本里面我看到初试化部分就有@extract($_POST)之类的操作,以保证程序在register globals为off的环境下能继续运行,这样的环境下我们很轻松可以伪造$_FILES数组,甚至将原来的$_FILES数组覆盖,但是想完全的伪造一个$_FILES数组还是很困难的,因为你无法饶过is_uploaded_file() 和 move_uploaded_file()。但是在windows下的php环境下测试时,我们发现php的临时文件很有规律,是C:\WINDOWS\TEMP\php93.tmp这种格式,上传的时候文件名字会是C:\WINDOWS\TEMP\phpXXXXXX.tmp这种格式变化,其中XXXXXX是十六进制的数字,并且是按照顺序增加的,也就是说如果这次上传的临时文件名是C:\WINDOWS\TEMP\php93.tmp,那么下次就会是C:\WINDOWS\TEMP\php94.tmp,临时文件名变得有规律。但是我们可能不知道当前的文件名是什么,这可以通过php自身的错误机制泄露出来,譬如我们将临时文件拷贝到一个没有权限的目录或者在目标文件里包含文件系统禁止的字符就可以将当前的临时文件名字给泄露出来,当然前提是没有错误抑制处理。
那么到底如何饶过is_uploaded_file() 和 move_uploaded_file()呢?看看php中is_uploaded_file()部分的代码:

PHP_FUNCTION(is_uploaded_file)
{
zval **path;
if (!SG(rfc1867_uploaded_files)) {
  RETURN_FALSE;
}
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &path) != SUCCESS) {
  ZEND_WRONG_PARAM_COUNT();
}
convert_to_string_ex(path);
if (zend_hash_exists(SG(rfc1867_uploaded_files), Z_STRVAL_PP(path), Z_STRLEN_PP(path)+1)) {
  RETURN_TRUE;
} else {
  RETURN_FALSE;
}
}
它是从当前的rfc1867_uploaded_files哈希表中查找看是否当前的文件名是否存在。其中rfc1867_uploaded_files保存了当前php脚本运行过程中由系统和php产生的有关文件上传的变量和内容。如果存在,就说明指定的文件名的确是本次上传的,否则为否。
php有个很奇怪的特性就是,当你提交一个上传表单时,php在做处理之前这个文件就已经被上传到临时目录下面,一直到php脚本运行结束的时候才会销毁掉。也就是说,你即使向一个不接受$_FILSE变量的php脚本提交这样一个表单,$_FILSE变量依然会产生,文件依然会被先上传到临时目录。问题就产生了。下面的脚本可能能说明这个问题:

<?
$a=$_FILES['attach']['tmp_name'];
echo $a.".............";
$file='C:\\WINDOWS\\TEMP\\php95.tmp';
echo $file;
if(is_uploaded_file($file)) echo '..................Yes';
?>
其中C:\\WINDOWS\\TEMP\\php95.tmp是我猜测的临时文件名字,当时,测试这个脚本的时候我们需要向它上传一个文件或者是100个文件,使得其中一个临时文件名为C:\\WINDOWS\\TEMP\\php95.tmp。如果此刻脚本有extract操作,我们就可以很方便的伪造出一个$_FILES变量了。不是么?可能要问伪造$_FILES变量有什么作用,我们就可以产生原来程序不允许的文件名了,php在处理上传的时候会对原来的文件名有一个类似于basename()的操作,但是一旦可以伪造之后我们就可以轻易的在文件名之内加\啊../啊等等你所喜欢的任何东西
实际利用可能有点苛刻,但是也总算是php一点瑕疵吧,呵呵。