0x00 介绍
-
文件类型验证:确保只允许特定类型的文件上传。
-
文件扩展名检查:检查文件的扩展名,但不要仅依赖于此。
-
MIME 类型验证:通过
finfo_file()
函数获取文件的 MIME 类型进行验证。 -
文件内容检查:对文件内容进行检查,防止上传恶意文件。
-
上传目录的权限设置:确保上传目录不可执行。
-
文件重命名:避免使用用户上传的文件名,生成随机文件名。
0x01 文件上传函数
$_FILES:一个超全局数组,用于处理上传的文件。
$_FILES['file']['name']:上传文件的原始名称。
$_FILES['file']['type']:上传文件的 MIME 类型。
$_FILES['file']['tmp_name']:文件在临时目录中的路径。
$_FILES['file']['error']:文件上传的错误代码。
$_FILES['file']['size']:文件的大小(以字节为单位)。
move_uploaded_file():将上传的文件从临时目录移动到指定的目标目录。
$uploadFile = 'uploads/' . basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadFile)) {
echo "File uploaded successfully.";
} else {
echo "File upload failed.";
}
is_uploaded_file():检查指定的文件是否是通过 HTTP POST 上传的。
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
// 文件是通过 HTTP POST 上传的
}
file_exists():检查指定的文件是否存在。
php
if (file_exists($uploadFile)) {
echo "File already exists.";
}
unlink():删除指定的文件。
if (unlink($uploadFile)) {
echo "File deleted.";
} else {
echo "Failed to delete file.";
}
basename():获取文件的基本名称。
$fileName = basename($_FILES['file']['name']);
pathinfo():获取文件路径信息,如目录名、基本名称、扩展名等。
$pathInfo = pathinfo($_FILES['file']['name']);
$extension = $pathInfo['extension'];
finfo_file()
功能:获取文件的 MIME 类型,通常用于验证文件类型。
示例:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if ($mimeType === 'image/jpeg') {
// 处理 JPEG 图片
}
getimagesize()
功能:获取图像文件的大小和其他信息。
示例:
$imageInfo = getimagesize($_FILES['file']['tmp_name']);
if ($imageInfo !== false) {
// 处理图像文件
}
file_put_contents()
功能:将字符串写入文件,通常用于保存上传的文件数据。
示例:
file_put_contents($uploadFilePath, $fileContents);
file_get_contents()
功能:读取文件内容到字符串中,通常用于处理上传的文件。
示例:
php
$fileContents = file_get_contents($_FILES['file']['tmp_name']);
0x02 示例
1、
搜索关键字
有特定模板,需要下载模板后加入马
2、
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");
if(isset($_POST['submit']))
{
if(file_exists(UPLOAD_PATH))
{
// 判断 content-type 的类型,如果是image/png则通过
if($_FILES['upload_file']['type'] == 'image/png')
{
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
if (move_uploaded_file($temp_file, $img_path))
echo "上传完成.";
else
echo "上传出错.";
}
}
}
<body>
<form enctype="multipart/form-data" method="post">
<input class="input_file" type="file" name="upload_file">
<input class="button" type="submit" name="submit" value="上传">
</form>
</body>
先思考一下代码中限制的条件是什么
if($_FILES['upload_file']['type'] == 'image/png') //条件
只验证MIME类型: 代码中验证了上传的MIME类型,绕过方式使用Burp抓包,将上传的一句话小马*.php中的Content-Type:application/php,修改成Content-Type: image/png然后上传即可
3、
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");
if(isset($_POST['submit']))
{
if(file_exists(UPLOAD_PATH))
{
$allow_ext = array(".jpg",".png",".jpeg");
$file_name = trim($_FILES['upload_file']['name']); // 取出文件名
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext); //去除字符串::$DATA
$file_ext = strtolower($file_ext); // 转换为小写
$file_ext = trim($file_ext); // 首尾去空
if(in_array($file_ext, $allow_ext))
{
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path))
echo "上传完成: {$img_path} <br>";
else
echo "上传失败 <br>";
}
}
}
<body>
<form enctype="multipart/form-data" method="post">
<input class="input_file" type="file" name="upload_file">
<input class="button" type="submit" name="submit" value="上传">
</form>
</body>
同样思考上传限制
if(in_array($file_ext, $allow_ext)) //满足两个条件
$allow_ext = array(".jpg",".png",".jpeg");
$file_name = trim($_FILES['upload_file']['name']); // 取出文件名
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext); //去除字符串::$DATA
$file_ext = strtolower($file_ext); // 转换为小写
$file_ext = trim($file_ext);
白名单的绕过: 白名单就是允许上传某种类型的文件,该方式比较安全,只有后缀为限制的后缀可上传成功,这时候需要搭配组合的漏洞才能getshell
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");
function getReailFileType($filename)
{
$file = fopen($filename, "rb");
$bin = fread($file, 2);
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode)
{
case 255216: $fileType = 'jpg'; break;
case 13780: $fileType = 'png'; break;
case 7173: $fileType = 'gif'; break;
default: $fileType = 'unknown';
}
return $fileType;
}
if(isset($_POST['submit']))
{
if(file_exists(UPLOAD_PATH))
{
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown')
{
echo "上传失败 <br>";
}else
{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path))
echo "上传完成 <br>";
}
}
}
<body>
<form enctype="multipart/form-data" method="post">
<input class="input_file" type="file" name="upload_file">
<input class="button" type="submit" name="submit" value="上传">
</form>
</body>
绕过检测文件头: 这种方式是通过文件头部起始位置进行匹配的从而判断是否上传,我们可以通过在上传文件前面追加合法的文件头进行绕过,例如在文件开头部位加上GIF89a<?php phpinfo();?> 即可完成绕过,或者如果是xffxd8xff 需要在文件开头先写上%ff%d8%ff<?php phpinfo(); ?> 然后,选择特殊字符,右击CONVERT->URL->URL-Decode编码后释放.
原文始发于微信公众号(Pik安全实验室):[代码审计] php 文件上传
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论