图片上传漏洞

休闲 2026-10-17 04:01:15 2

上传一张图片,图片服务器返回的上传路径是 /uploads/2024/1.jpg。再传一张,漏洞变成 /uploads/2024/2.jpg。图片看起来没问题。上传但如果我传的漏洞文件名叫 ../../etc/passwd 呢?

很多年前我做过一个企业站的项目,用户头像功能。图片前端做了校验,上传只允许 jpg 和 png。漏洞后端也做了,图片检查了 MIME 类型和扩展名。上传听起来挺严的漏洞。结果测试的图片时候,我把文件后缀改成 .jpg,上传内容是漏洞一个 PHP 脚本,上传成功了。更离谱的是,那个目录居然可以直接访问,而且服务器解析了它。一个 system($_GET['cmd']) 就这么跑起来了。

这不是什么高深的技术。问题出在后端只看了扩展名,没有检查文件内容,也没有禁止上传目录的执行权限。两个小疏忽叠在一起,就是一个完整的 webshell 入口。

文件名的坑

上传漏洞里最经典的一类,就是文件名处理不当。有人觉得把 ../ 过滤掉就安全了,但攻击者可以用 ....// 绕过,服务端删掉中间的 ../ 之后,剩下的又拼成了一个 ../。还有用 URL 编码的,%2e%2e%2f,看服务端在哪一层解码。

另一种是空字节截断。早年 PHP 版本里,shell.php%00.jpg 这种文件名,底层 C 函数遇到 \0 就认为字符串结束了,结果实际保存的是 shell.php。现在这个漏洞基本被修了,但类似的逻辑在别的语言或框架里偶尔还能碰到变种。

内容检测也不是万能的

有些系统会检查文件头。比如 JPEG 的前两个字节是 FF D8。那攻击者就在 PHP 代码前面加上这两个字节,后面跟 <?php ... ?>。文件头检测过了,但服务器解析的时候,PHP 引擎会忽略前面的二进制垃圾,照样执行后面的代码。

还有图片马。把一句话木马写进 JPEG 的 EXIF 注释字段里,文件本身是一张合法图片,但配合文件包含漏洞,就能被当成 PHP 执行。单独一个上传功能可能没事,一旦和本地文件包含组合,杀伤力就出来了。

真正的修复思路

我后来重新做那个头像功能时,改了策略。上传目录放在 web 根目录之外,通过一个脚本读取和输出图片,而不是直接暴露路径。文件名不用用户提供的,用 UUID 重命名。扩展名白名单只留 jpg、png、gif,而且用 getimagesize() 确认它确实是一张图片。最后,上传目录的 php_admin_flag engine off 写进配置,就算有人传了 PHP 文件进去,也不会被执行。

这套组合拳下来,成本不高,但把常见的几条路都堵死了。单独依赖任何一层都不够。扩展名白名单可以被解析漏洞绕过,内容检测可以被图片马绕过,目录权限配置错了,前面做得再好也白搭。

说白了,上传功能的核心原则就一条:永远不要相信用户传上来的任何东西。文件名、扩展名、MIME 类型、文件内容,全都可能是伪造的。你得假设每一个上传的文件都是恶意的,然后在这个前提下设计防御。

我见过太多系统,上传接口连登录都不需要,传完直接返回 URL,目录还能列出来。这种站,脚本小子拿个扫描器就能拿下来。不是技术有多难,是根本没当回事。

本文地址:http://www.79wt.cn/news/777f9599127.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

友情链接