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

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

文件上传攻击示例

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 action="upload1.php" method="POST" ENCTYPE="multipart/formdata"> Select the file to upload: <input type="file"> <input type="submit" value="upload"> </form>

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

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

恶意用户可以通过访问 如?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;; 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"; } ?>

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

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

查看文件代码未验证文件名,用户可以通过例如?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=https://www.jb51.net/article/\"view.php?id=$id\">here</a>\n"; } else {   echo "File uploading failed.\n"; } ?>

view.php

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1492b8badf90988f04c0357603844d2c.html