任意文件下载漏洞(Arbitrary File Download Vulnerability)是一种常见的Web安全漏洞,它允许攻击者通过修改输入参数,从服务器下载任意文件,而不仅仅是预期的文件;通常这种漏洞出现在处理用户输入的地方,尤其是文件下载功能中。
漏洞出现场景
1.文件共享和管理系统:在企业内部的文件共享系统中,用户可能需要下载各类文件。如果系统没有妥善地处理文件路径和名称,攻击者可以通过修改请求参数来下载不应公开的文件。
2.内容管理系统(CMS):许多CMS平台允许管理员和用户上传和下载文件。如果CMS没有对下载请求进行严格的验证和限制,攻击者可能利用该漏洞访问敏感文件。
3.电子商务网站:在电子商务网站中,用户可能需要下载发票、收据或其他交易相关的文件。如果这些下载请求没有正确的权限控制和路径验证,攻击者可以利用该漏洞下载其他用户的私人信息或系统文件。
4.在线教育平台:在线教育平台上,用户通常可以下载课程材料、作业和考试文件。如果文件下载功能存在漏洞,攻击者可能会下载其他用户的私人信息或平台的敏感数据。
老规矩,我们通过pikachu靶场来演示,并阐述漏洞产生的原理和利用方式。
示例:
1.打开任意文件下载对应的页面,发现此时页面中有多张球星图片,此时点击对应的链接就能够将图片下载至本地。
2.此时我们通过F12进行检查,找到任意一个链接的标签:可以看到此时链接中含有对应的下载地址(只是不完整)。
此时我们将URL进行拼接就可以得到下载图片所使用的url;
http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php?filename=kb.png
在新页面中访问则下载一张图片:
这个时候我们可以进行测试,该下载的链接是否对用户下载的请求进行安全过滤,若这个时候我要下载pikachu靶场首页,也就是index.php这个文件:
http://127.0.0.1/pikachu/index.php
那么此时我们就可以构造下载的url:指定文件的相对路径
http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php?filename=../../../index.php
可以发现此时首页源码已经被下载下来,同理我们可以通过这种方式去下载其他php源码,在实际环境中若出现这种漏洞则可以读取系统中的文件(如Linux的/etc/passwd)或者想本例中的源码用作后续白盒测试代码审计。
同样的,这边我们还是来通过源码来分析一下该漏洞产生的原理:
$file_path="download/{$_GET['filename']}";
//用以解决中文不能显示出来的问题
$file_path=iconv("utf-8","gb2312",$file_path);
//首先要判断给定的文件存在与否
if(!file_exists($file_path)){skip("你要下载的文件不存在,请重新下载", 'unsafe_down.php');return ;
}
$fp=fopen($file_path,"rb");
$file_size=filesize($file_path);
//下载文件需要用到的头
ob_clean();//输出前一定要clean一下,否则图片打不开
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length:".$file_size);
Header("Content-Disposition: attachment; filename=".basename($file_path));
$buffer=1024;
$file_count=0;
//向浏览器返回数据
//循环读取文件流,然后返回到浏览器feof确认是否到EOF
while(!feof($fp) && $file_count<$file_size){
$file_con=fread($fp,$buffer);$file_count+=$buffer;
echo $file_con;
}
fclose($fp);
检查文件是否存在:使用file_exists
函数检查指定路径的文件是否存在。如果文件不存在,调用skip
函数(假设是自定义函数,用于跳转和显示错误消息),并终止脚本执行。
使用ob_clean
清除输出缓冲区,以确保没有其他输出干扰文件下载。
设置Content-type
头为application/octet-stream
,表示下载的文件是二进制流。
设置Accept-Ranges
头为bytes
,表示支持断点续传。
设置Accept-Length
头为文件大小。
Header("Content-Disposition: attachment; filename=".basename($file_path));
设置Content-Disposition
头,指示浏览器以附件形式下载文件,并指定下载的文件名。
循环读取文件并输出到浏览器:
-
使用
feof
函数检查文件指针是否到达文件末尾。 -
使用
fread
函数按缓冲区大小读取文件内容,并增加文件计数器。 -
使用
echo
函数输出读取的文件内容到浏览器。
可以看到当前程序对于客户端请求下载的文件并没有做过多的安全检查,仅仅检查了文件是否存在,而没有检查客户端要下载的文件是否在指定的文件夹中(是否为指定的文件范围)。
改进建议
为了增强安全性,可以做以下改进:
-
严格验证和过滤输入:只允许预定义目录中的特定文件下载。
-
防止路径遍历:移除路径操作符或使用安全的文件路径。
-
权限控制:确保只有有权限的用户可以下载特定文件。