|
一个攻击者把请求路径写成../../../../etc/passwd,目录服务器就乖乖地把密码文件吐了出来。攻击这不是目录电影情节,是攻击每天发生在互联网角落的真实攻击。目录穿越(Path Traversal)的目录核心就一句话:程序信任了用户输入的路径,没有验证它是攻击否越过了允许访问的目录边界。 举个我实际见过的目录例子。某政府网站的攻击文件下载功能,URL长这样:/download?目录file=report_2023.pdf。开发者觉得“我都用了白名单后缀.pdf了,攻击还能出什么事?目录”结果攻击者把参数改成file=../../../../etc/passwd%00.pdf,利用空字节截断绕过了后缀检查。攻击服务器返回了完整的目录root用户列表。这类漏洞在2010年前后泛滥成灾,攻击现在依然存在于老旧系统里。目录 为什么拦不住?很多程序员默认“用户输入是善意的”。他们写代码时想的是“正常用户会输入文件名”,而不是“任何字符串都可能出现在这里”。更麻烦的是,过滤逻辑本身可能出问题——有人用str_replace把../替换成空字符串,但攻击者传....//,替换后剩../,照样穿越。这种猫鼠游戏里,防御方永远在补漏。 真正的解法其实朴素得让人尴尬:不要拼路径,用文件ID查数据库映射;或者强制规范路径后验证前缀。比如PHP里realpath()后再检查是否以允许的目录开头。但很多老代码就是图省事,直接字符串拼接。 我印象最深的一次渗透测试,目标是个内部工单系统。它的附件下载接口只校验了文件是否存在,没校验路径。我用../../../../windows/win.ini拿到了系统版本信息,再配合..%2f..%2f..%2f..%2fapache/logs/access.log翻到了管理员的session ID。整个过程不到十分钟,没用到任何高级工具,就靠浏览器地址栏。事后报告里我写:“修复需要改动约20行代码,但你们已经带着这个洞上线了六年。” 这类漏洞的根源不是技术难度,而是开发者的心智模型。他们假设文件系统是安全边界,却忘了用户输入是穿越边界的梯子。防御思路其实就一条:把所有输入当恶意数据,用规范化的绝对路径做最终裁决,而不是相信过滤函数。毕竟,过滤黑名单总有漏网之鱼,但白名单验证是釜底抽薪。 |