文件下载漏洞
文件下载漏洞:一个被低估的文件入口
某天下午,一家中型企业的下载内网突然出现大量加密流量。安全团队排查后发现,漏洞攻击者是文件通过一个看似无害的附件下载功能——download.php?file=report_2023.pdf——把服务器上的/etc/passwd给拉走了。没人想到,下载这个写了三年的漏洞老功能,居然成了突破口。文件
文件下载漏洞的下载本质,就是漏洞服务器对用户输入的“文件路径”过度信任。开发者图省事,文件直接把参数拼进文件系统路径,下载结果用户传个../../../../etc/passwd,漏洞服务器就乖乖把文件吐出来。文件更狠的下载还能配合%00截断(老版本PHP)或者编码绕过,直接读取应用源码、漏洞数据库配置,甚至~/.ssh/id_rsa。
我见过最离谱的一次,是某后台管理系统的“导出报表”功能,参数直接是filename=2024_01.csv。安全测试时输入filename=../../../../etc/shadow,响应头里居然真带回了root哈希。开发小哥一脸无辜:“我以为文件名是固定的,没想过有人会改参数。”——这就是典型的“功能正常”掩盖了“逻辑缺陷”。
别把这类漏洞和路径穿越混为一谈。路径穿越只是读文件,文件下载漏洞还包括了“下载”这个动作本身的风险。比如,有些系统虽然过滤了../,却忘了处理绝对路径——输入/etc/passwd照样能下载。还有些系统把下载目录写死,但允许通过file://协议读本地文件,这又牵扯出SSRF的变种。
修复方案并不复杂,但需要点强迫症。最有效的一招是“映射表”:后端维护一个允许下载的文件ID(比如file_id=101),前端只传ID,后端查表拿真实路径。如果业务上必须传文件名,那就做白名单校验,只允许[a-zA-Z0-9_\-\.],并且强制拼接一个固定前缀目录,再把realpath()的结果和预期前缀比对。别信任何黑名单,..、%2e%2e、....//这些变体防不胜防。
还有个细节容易被忽略:下载响应头里的Content-Disposition和Content-Type。如果没正确设置,攻击者可能诱导用户执行恶意文件——比如把一个PHP文件伪装成PDF下载下来,配合其他漏洞就能getshell。至少得加上X-Content-Type-Options: nosniff,并强制Content-Disposition: attachment。
说实话,这类漏洞在现在的框架里已经很少见了——Laravel、Spring都有内置的文件响应机制,只要你不手动拼路径。但老项目、外包代码、快速迭代的运维脚本里,它依然像地雷一样躺着。我每次做渗透测试,都会先翻翻下载功能的参数名,十次里有三次能挖到点东西。你永远不知道哪个“临时功能”会变成下一个入口。
别指望开发人员都懂安全编码,最好的办法是上线前用自动化工具扫一遍,同时给下载接口加统一的入口过滤器。如果公司实在缺人,至少把file参数改成id,逼着后端走查表逻辑——这招能挡住九成的手贱尝试。
(责任编辑:热点)
