文件上传漏洞复现笔记
预备知识
文件上传漏洞
- WebShell:webshell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。黑客在入侵了一个网站后,通常会将asp或php后门文件与网站服务器WEB目录下正常的网页文件混在一起,然后就可以使用浏览器来访问asp或者php后门,得到一个命令执行环境,以达到控制网站服务器的目的。
- 一句话木马:
<?php @eval($_GET['cmd'];) ?>
以及许多类似的变种
- 文件上传漏洞
文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷,而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的,“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
- 文件上传漏洞的原因:
- 没有对文件类型进行严格限制
- 没有对上传文件或目录的权限进行严格限制
最简单的文件上传功能
upload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>UPLOAD</title>
</head>
<body>
<form action="upload_file.php" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
upload_file.php
<html>
<head>
<meta charset="utf-8">
<title>上传文件</title>
</head>
<body>
<?php
if ($_FILES["file"]["error"] > 0)
{
echo "错误:: " . $_FILES["file"]["error"] . "<br>";
}
else
{
echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>";
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " 文件已经存在。 ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
echo "文件存储在: " . "upload/" . $_FILES["file"]["name"];
}
}
?>
</body>
</html>
- 效果截图:
- PHP文件上传原理:PHP文件通过表单形式上传,上传后,服务器会暂存一个临时文件,并且会随机命名,防止文件名重复,之后再把临时文件保存到指定目录。
防御方法
- 前端限制:
- 限制文件大小:通过隐藏的字段,可以设置文件最大上传大小,在服务端也可以检测,但是通过改包很容易绕过。
<input type="hidden" name="MAX_FILE_SIZE" value="10000" />
- 限制文件类型:
<input type='file' name='myFile' accept='文件的MIME类型' />
- MIME类型:MIME参考手册
- 限制文件大小:通过隐藏的字段,可以设置文件最大上传大小,在服务端也可以检测,但是通过改包很容易绕过。
- 后端限制
- 限制文件大小:
限制文件大小是十分有效的做法,通常限制越严格,被攻击的可能越小,但同样的,网站会严重牺牲用户体验。所以这种做法虽然被采取,但用处并不大。<html> <head> <meta charset="utf-8"> <title>上传文件</title> </head> <body> <?php if ($_FILES["file"]["error"] > 0) { echo "错误:: " . $_FILES["file"]["error"] . "<br>"; } else { if($_FILES["file"]["size"]/1024 < 0.02) { echo "上传文件名: " . $_FILES["file"]["name"] . "<br>"; echo "文件类型: " . $_FILES["file"]["type"] . "<br>"; echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>"; echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>"; if (file_exists("upload/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " 文件已经存在。 "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; } } else { echo "<h1 style='color: rend;'>上传文件非法!</h1>"; } } ?> </body> </html>
- 后缀名限制:
$temp = explode(".", $_FILES["file"]["name"]); $extension = end($temp); // 后缀白名单 $allowExts = array("gif","jpg","jpeg","png"); if(($_FILES["file"]["size"]/1024 < 100) && in_array($extension, $allowExts)) { ... } else { ... }
- 使用后缀名限制依然可能被绕过:
- Apache服务器:可以借助文件包含漏洞来运行后缀名是图片后缀,但是内容是PHP代码的文件。如果网站的后缀过滤采用的是黑名单,并且过滤不严,那么后缀名pHp、PhP等可能能够绕过后台的过滤。
- Nginx服务器:Nginx服务器的cgi.fix_pathinfo默认为1,使得其存在一个畸形解析的漏洞,我们上传一个文件a.jpg,然后以…/a.jpg/a.php,那么,服务器就会按php文件来解析a.jpg文件
- 通过文件头校验文件:
-
文件头:
文件头是位于文件开头的一段承担一定任务的数据,一般都在开头的部分。
一般来说,文件头用于标识文件种类。
-
常用的文件头:https://blog.csdn.net/bruce135lee/article/details/81077374
-
绕过方法:伪造文件幻术,PHP会把php脚本标签之外的文字解析为纯文本,所以在标签之前加入伪造的幻数,即可绕过文件头检查。
-
- 修改php文件执行权限:Apache服务器在文件上传目录下新建.htaccess文件,然后写入内容
<Files ~ ".php"> Order allow,deny Deny from all </Files>
- 限制文件大小: