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

漏洞描述

开发中文件上传功能很常见,作为开发者,在完成功能的基础上我们一般也要做好安全防护。

文件处理一般包含两项功能,用户上传和展示文件,如上传头像。

文件上传攻击示例

upload.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

Select the file to upload:

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

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

Content-type验证

upload.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

2712d60d9a58cc69ab0adf6edf088d03.png

upload.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,则可成功上传。

上传成功后访问该文件则可看到如下显示

d64d40827afac04a33068a983fec9581.png

文件扩展名验证

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

upload.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

$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

$uploaddir = 'd:/uploads/';

$name = $_GET['name'];

readfile($uploaddir.$name);

?>

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

解决漏洞示例

upload.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 here\n";

} else {

echo "File uploading failed.\n";

}

?>

view.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.文件上传限制文件大小,个人上传数量等

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值