早些年,提到Web渗透,或者搜索一些黑客教程,基本都会看到文件上传漏洞。
它是一个很经典的漏洞,但它本质其实不是一个漏洞,而是网站本身的上传文件功能,不过如果我们上传了Webshell,那么,它就成为了文件上传漏洞。
打开我们的靶机Metasploitable2:
这里就是我们可以测试文件上传的地方,我们先在低安全级别的情况下测试:
在目录中输入这行路径,就可以直接访问这个图片。
果然,成功执行!
我们来分析下低安全级别的源代码:
<?php
if (isset($_POST['Upload'])) {
$target_path = DVWA_WEB_PAGE_TO_ROOT."hackable/uploads/";
$target_path = $target_path . basename( $_FILES['uploaded']['name']);
if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {
echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';
} else {
echo '<pre>';
echo $target_path . ' succesfully uploaded!';
echo '</pre>';
}
}
?>
为了配合分析,我用Burp把上传文件的Request复制下来,除了基本的HTTP头,需要注意的就是下面这部分内容:
代码首先判断点击了Upload之后,找到DVWA根目录,然后,重新定义新的路径;
从请求获得文件名1.php赋值,如果目标文件不存在,显示失败,否则显示成功;
代码缺陷:
1、没有对文件类型进行过滤,本意接受上传图片,实际却可以上传php;
2、对文件大小限制为100K其实没有用。由于是客户端进行限制的,利用截断,我们可以轻易修改文件大小限制;
最终得出结论:低安全级别的文件上传做得很差,可以轻易利用漏洞。
接下来看看中安全级别:
上传png文件成功,但是上传php文件失败。
想要找到失败原因,需要查看源代码:
<?php
if (isset($_POST['Upload'])) {
$target_path = DVWA_WEB_PAGE_TO_ROOT."hackable/uploads/";
$target_path = $target_path . basename($_FILES['uploaded']['name']);
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_type = $_FILES['uploaded']['type'];
$uploaded_size = $_FILES['uploaded']['size'];
if (($uploaded_type == "image/jpeg") && ($uploaded_size < 100000)){
if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {
echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';
} else {
echo '<pre>';
echo $target_path . ' succesfully uploaded!';
echo '</pre>';
}
}
else{
echo '<pre>Your image was not uploaded.</pre>';
}
}
?>
可以从代码中分析得出结论:这里需要验证type、name和size三个变量才能成功上传
而且要求type必须是image/jpeg,所以哪怕是png格式的图片也无法上传
绕过:
既然要验证type,name,size那么我就修改
由于size和name符合要求,那么我就修改type为image/jpeg:使用Burp截断
进一步可以上传木马等等!
总结:中等安全等级的文件上传也可以轻易饶过,不过需要截断工具,但其实已经可以防止最低级的脚本小子了。
接下来看看高级安全等级:
源代码:
<?php
if (isset($_POST['Upload'])) {
$target_path = DVWA_WEB_PAGE_TO_ROOT."hackable/uploads/";
$target_path = $target_path . basename($_FILES['uploaded']['name']);
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_ext = substr($uploaded_name, strrpos($uploaded_name, '.') + 1);
$uploaded_size = $_FILES['uploaded']['size'];
if (($uploaded_ext == "jpg" || $uploaded_ext == "JPG" || $uploaded_ext == "jpeg" || $uploaded_ext == "JPEG") && ($uploaded_size < 100000)){
if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {
echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';
} else {
echo '<pre>';
echo $target_path . ' succesfully uploaded!';
echo '</pre>';
}
}
else{
echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';
}
}
?>
分析可以看出:
这里对扩展名进行了过滤,所以上传1.php会失败,修改类型也会失败
总结:高安全级别也不安全。
怎么更安全呢?
一般文件前一部分字节表明这个文件的类型,我们可以来验证这一部分内容;
因为哪怕修改了扩展名也无法修改这里的文件类型,我们可以用mimetype命令来验证;
所以,这种方式更安全!
那么,有没有办法绕过这种更安全的方式呢?
但是,想要使用会报错,因为服务器端没有用php去解释这个文件。
所以我们把1.png修改为1.php.png,再发送过去,服务器端就会去解释了,然后就可以像上面一样利用了。
那么,文件上传漏洞是无法被解决了吗?
不是。
安全有效的解决办法:目录权限限制,不给执行权限
sudo chmod a-x uploads/
对保存上传文件的文件夹进行权限限制