八、文件上传
8.1 介绍文件上传漏洞
在线代互联网的Web应用程序中,上传文件是一种常见的功能,因为它有助于提高业务效率,比如企业的OA系统,允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多,Web应用受到攻击的风险就越大,如果Web引用存在文件上传漏洞,那么恶意用户就可以利用文件上传漏洞将可执行脚本程序上传到服务器中,获得网站的权限,或者进一步危害服务器。
8.2 有关文件上传的知识
- 为什么文件上传存在漏洞
上传文件时,如果服务端代码未对客户端上传的文件进行严格的验证和过滤,就容易造成可以上传任意文件的情况,包括上传脚本文件(asp、aspx、php、jsp等格式的文件)。
- 危害
非法用户可以利用上传的恶意脚本文件控制整个网站,甚至控制服务器。这个恶意的脚本文件,又被称为WebShell,也可将WebShell脚本称为一种网页后门,WebShell脚本具有非常强大的功能,比如查看服务器目录、服务器中的文件,执行系统命令等。
8.3 JS检测绕过攻击
JS检测绕过上传文件漏洞常见于用户选择文件上传的场景,如果上传文件的后缀不被允许,则会弹窗告知,此时上传文件的数据包并没有发送到服务端,只是在客户端浏览器使用JavaScript对数据包进行检测,如图91所示。
这时有两种方法可以绕过客户端JavaScript的检测。
-
使用浏览器的插件,删除检测文件后缀的JS代码,然后上传文件即可绕过。
-
首先把需要上传文件的后缀改成允许的,如jpg、png等,绕过JS的检测,再抓包,把后缀名改成可执行文件的后缀名即可上传成功,如图92所示。
8.4 JS检测绕过攻击分析
客户端上传文件夹的HTML代码如下所示,在选择文件时,会调用JS的selectFile函数,函数的作用是先将文件名转换为小写,然后通过substr获取文件名最后一个点号后面的后缀(包括点号)。如果后缀不是“.jpg”,则会弹框提示“请选择jpg格式的照片上传”。
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<html>
<head>
<title>JS检查文件后缀</title>
</head>
<body>
<script type="text/javascript">
function selectFile(fnUpload) {
var filename = fnUpload.value;
var mime = filename.toLowerCase().substr(filename.lastIndexOf("."));
if(mime!=".jpg")
{
alert("请选择jpg格式的照片上传");
fnUpload.outerHTML=fnUpload.outerHTML;
}
}
</script>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" onchange="selectFile(this)" />
<br />
<input type="submit" name="submit" value="submit" />
</form>
</body>
</html>
服务端处理上传文件的代码如下所示。如果上传文件没出错,再通过file_exists判断在upload目录下文件是否存在,不存在的话就通过move_upload_file将文件保存到upload目录。此PHP代码中没有对文件后缀做任何判断,所以只需要绕过前端JS的校验就可以上传WebShell。
<?php
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
}
?>