
从 Pass-1 打到 Pass-21这是我反复刷过好几遍的一个经典上传靶场。upload-labs 几乎就是为“文件上传漏洞”这一个大类量身定制的练功房每一个关卡都对应一种真实的检测思路从最简单的 JS 前端校验一路到二次渲染、条件竞争难度曲线拉得很开。如果你是搞 Web 安全的或者正在做网站开发想搞清楚上传功能到底哪里容易出问题这套靶场比翻十篇漏洞文章都管用。先说清楚它能干什么upload-labs 是一个跑在本地 PHP 环境里的开源靶场通过几十个 PASS 关卡模拟一个又一个“看似有校验、实则能绕过”的上传点。你作为一个攻击者想办法把非法的文件传上去而每一个关卡背后对应一种真实的检测机制。攻完一遍你基本就能把市面上主流的文件上传检测手段认全也知道它们分别能怎么绕、怎么防。适合 PHP 基础还没丢、想系统梳理上传漏洞的同学也适合开发同学反向看看自己写的校验能不能扛住这些花招。1. 整体思路拆解为什么“上传”能单独立一个靶场文件上传功能单独做一个靶场不是小题大做。在真实业务里头像上传、附件上传、证件照上传、编辑器里的图片上传几乎是每个网站都有的基础功能而“能接收文件”这件事本身就意味着巨大的攻击面。你发一个字符串给服务器服务器顶多回显一下但你要是把一段可执行代码伪装成图片传上去服务器一旦把它当脚本解析你就等于拿到了一个在别人机器上跑命令的入口。upload-labs 的设计思路非常清晰把上传点可能遇到的检测方式拆成一个个独立关卡每一关只突出一种检测或绕过维度。这样做的好处是你学到的不是零散的 payload而是一套能够迁移的思维框架。打多了你会发现所谓的“绕过”本质上就三类让检测器看不到恶意内容、让解析器把非脚本文件当脚本执行、或者让文件落地和检测之间存在时间差。再说说环境选型。upload-labs 是 PHP 写的依赖 Apache 和 MySQL这一点很多人一开始不理解为什么不用 Python 写个靶场这里门道很深。PHP 在文件上传解析上有一堆历史遗留特性比如“多重扩展名解析”“中间件与代码层解析不一致”“.user.ini 和 .htaccess 级别的配置覆盖”这些都是在真实环境中被反复利用的点用 Java 或 Python 重现起来既不真实也麻烦。靶场故意选 PHP Apache 这套“经典组合”就是为了让你原汁原味地体验这些坑。1.1 关卡设计与学习路径的对应关系upload-labs 的关卡排布是循序渐进的。前面几关考的是“检测点在哪”比如 Pass-1 只在浏览器端用 JavaScript 校验Pass-2 只看 Content-TypePass-3 用了一个并不完整的黑名单。这些关卡单独拿出来都很弱但它们教会你一件事先定位检测发生的位置再决定用什么方式绕过。中间段的关卡开始出现组合检测比如黑名单 MIME 双重校验或者大小写、空格、点号这些 Windows 和后端解析器特性。到这里你会发现绕过不只是“改个后缀”那么无脑而是要精确理解“黑名单到底挡了什么、没挡什么”。后面几关进入重口味领域条件竞争、二次渲染、图片马配合包含漏洞。这些已经不是单纯改包能搞定的需要你写脚本去抢时间窗或者手工构造一张在渲染前后均保持恶意逻辑的图片。这些技术在真实攻击里是压箱底的东西平时很难有地方让你放开手去练。1.2 这套靶场要解决的实际问题我见过很多刚入门的朋友拿着一句话恶意代码到处塞遇到拦截就一脸懵。upload-labs 的逻辑是反过来的先让子弹飞一会儿让你看清楚每一种检测的判定依据是什么然后再告诉你“为什么这个检测可以被绕过”或“为什么这个检测绕不动”。当你能把每一关的检测逻辑和绕过原理都讲明白你对上传漏洞的理解才算真正成型而不是停留在“改后缀、改 Content-Type”这两板斧。对于开发同学这套靶场还有一层额外价值把每一关的检测代码翻出来看其实就是一份“上传校验反面教材合集”。知道攻击者是怎么想的你写校验的时候才会有敬畏心才会想到“后缀白名单不如判断文件真实内容”“服务器和代码层的解析规则必须同步”。这些经验光看书是记不牢的。2. 环境搭建与部署本地把 upload-labs 跑起来upload-labs 本质上就是一个源码压缩包你把它扔进 PHP 站点目录配好数据库就能访问。搭建本身不难但有几个坑值得提前说免得你来来回回折腾。2.1 准备工作PHP 版本与组件要求upload-labs 对系统环境的要求很低甚至不需要你用到多少 PHP 高级特性。我自己用过的推荐组合是组件建议版本说明Apache2.4.x必须用 Apache别用 Nginx理由后文详细说PHP5.6 或 7.x推荐 5.6/7.0部分老特性在新版 PHP 下行为有差异MySQL5.7 或 8.0用于记录上传日志和用户信息phpstudy / XAMPP任意集成环境最省事注意关闭 443 端口冲突即可注意你如果非要用 Nginx 跑这个靶场Pass 里涉及 Apache 解析特性比如 .htaccess、多后缀解析会直接失效等于少了一大半学习内容。老老实实用 Apache。2.2 部署步骤与初始化配置第一步把 upload-labs 源码解压到 Apache 的网站根目录比如phpstudy_pro\WWW\upload-labs。目录结构里核心就两个部分一份是每个 PASS 关卡对应的前端页面和后端处理脚本另一份是数据库初始化文件。第二步打开 MySQL导入项目自带的 SQL 文件。这个步骤主要为了记录每次上传的日志不导数据库的话页面也能开但部分关卡会报错所以建议老老实实导一次。默认数据库名和密码配置在项目的配置连接文件里打开改成本地 MySQL 的实际账号密码即可。第三步访问http://127.0.0.1/upload-labs/能看到一个关卡列表页就说明环境起来了。最后建议把 PHP 的display_errors打开方便排错。在 php 配置文件里把display_errors On设上然后重启 Apache。调试靶场的时候错误信息就是你的指路明灯。2.3 目录权限与运行机制upload-labs 的每一个 PASS 关卡都会把上传文件写入到自己对应的目录下比如Pass-01/目录里有一个upload子目录就是要接收文件的落盘位置。如果上传后页面提示失败先去检查这些目录的写入权限。在 Windows 环境下用 phpstudy多数情况不存在权限问题在 Linux 下部署比如我后来为了练命令行为转到了 Docker 环境需要给项目目录执行chmod -R 755并确保www用户可写。还有一个细节某些关卡用到.htaccess或.user.ini作为上传内容Apache 默认开了AllowOverride All才会生效如果你发现相关关卡怎么都打不出预期效果先检查 httpd.conf 里的这个配置。3. 核心机制解析文件上传检测到底在查什么你真去梳理一遍 upload-labs会发现所有检测手段翻来覆去就那么几个维度。我把它们拆开讲把这些原理吃透了后面过每一关都是顺水推舟。3.1 检测维度的五大类第一类是客户端检测。这类校验只发生在浏览器端通过 JavaScript 检查文件后缀或类型。它看起来像个防护实际上一点防护力都没有因为 HTTP 请求可以随便抓包改包。Pass-1 就是典型的例子全流程用于演示“这种校验形同虚设”。第二类是 MIME 类型检测。服务端读取上传文件时携带的Content-Type比如image/jpeg、image/png只认这个就放行。问题在于 Content-Type 是由客户端声明的我用 Burp Suite 改一下请求包头就能绕过。Pass-2 就是这个套路。第三类是基于扩展名的黑名单检测。服务端把常见危险后缀收集成一个数组比如php、php5、phtml、asp等发现上传文件后缀命中就拦截。这类检测看起来比前两种靠谱但实际上极度依赖名单的完整性漏一个特殊后缀就等于全漏。第四类是基于扩展名的白名单检测。只允许列出的后缀上传比如只允许jpg、png、gif。白名单的防御强度远高于黑名单因为它默认拒绝一切未知后缀。但白名单并非铁板一块如果服务端的解析规则存在漏洞攻击者仍然可以把“合法后缀”变成“可执行内容”比如配合解析漏洞和中间件特性绕过。第五类内容检测。它不关心后缀和 MIME而是读取文件内容本身去判断检查文件头几个字节是不是图片特征GIF89a、\xFF\xD8\xFF或者直接用getimagesize()验证是否是一个有效的图片文件。这种检测在防御上更硬核但二次渲染和图片马技术就是专门对付它的。3.2 解析层面的“隐形黑手”很多时候上传点本身校验已经很严了最后被打穿问题出在解析环节。Apache 处理多后缀文件时有一个典型的分裂行为对于shell.php.jpgApache 默认会依据识别出的“第一个能解析的后缀”来决定处理方式在特定配置下shell.php.jpg会被当作 PHP 解析执行。这就是所谓的多后缀解析特性。另一个经典的解析特性是.htaccess。它是 Apache 的目录级配置文件你可以通过上传一个.htaccess文件来改变同目录下其他文件的解析规则。比如写入一段配置让某个文件被当成 PHP 执行。这一招在 upload-labs 里有专门的关卡演示也是目前很多虚拟主机上最头疼的绕过手段。PHP 层面还有一个.user.ini的奇葩特性。它允许在目录下自定义 PHP 配置其中有两条指令可以做到“引入”效果auto_prepend_file和auto_append_file在目标脚本执行前自动包含指定文件。只要上传目录运行 PHP攻击者就可以通过上传.user.ini配一个图片马让同目录下任意 PHP 文件在解析时自动带入恶意代码。3.3 业务逻辑层的漏洞除了以上“纯技术”检测upload-labs 还有几关是在考业务逻辑中的疏漏例如条件竞争。典型场景是服务端先接收文件、保存到临时目录再检测是否恶意不是就删除。这中间存在一个时间窗如果攻击者持续高速上传同名文件并且上传和删除之间有一瞬的间隙就可能把文件抢先拿到手。这种“检测和落盘”顺序颠倒造成的漏洞在后端开发中非常现实尤其是在异步处理架构里经常被忽视。再比如渲染逻辑。有些头像上传功能会对图片做二次裁剪、压缩、重渲染本意是为了去掉夹带的恶意代码。但如果攻击者精心构造一张图片使得渲染前和渲染后都保留恶意代码那这种“净化”就会失效。upload-labs 也专门设置了一关来练这个。不要小看这种攻击真实环境里我见过好几起通过图片马配合本地文件包含漏洞拿下权限的案例。4. 关卡实战从 Pass-1 到 Pass-21 的通关思路梳理我把 21 关按检测类型重新分组来讲这样比一关一关地报菜名更有价值。你跟着这个思路走一遍基本能建立起自己的绕过决策树。4.1 纯前端校验与 MIME 校验抓包改包是基本功Pass-1 的页面里嵌入了一段 JavaScript检查选择的文件后缀是否在图片类型白名单里。最容易的办法是先把一个shell.jpg改成shell.php在文件选择框里它会被拦截但你别点上传直接用 Burp Suite 拦截上传过程然后把文件名部分改回.php或修改前端脚本禁用它。更直接的方法是把选择文件的对话框指向一个 jpgBurp 拦截后把文件内容和后缀改成 php 再放行。这个关卡的要点是理解“前端校验不参与服务端安全决策”。Pass-2 的检测逻辑是取$_FILES[file][type]判断是不是 image 类型。这里连文件名都不看了只认请求头里的 Content-Type。你在 Burp 里把随便一个上传包的Content-Type改成image/jpeg就放行。这类校验在真实系统里特别常见很多开发以为“校验了类型”实际上校验的只是一个客户端随口就能改的字符串。4.2 黑名单绕过扩展名的各种变形从 Pass-3 开始进入黑名单时代。常见黑名单数组覆盖了php、php5、phtml等常见后缀。但用它做防御的核心问题在于“漏”。举个例子Apache PHP 环境下.php3、.php4、.php7、.pht这些后缀都可能在特定配置下被当成 PHP 解析。黑名单没列全就意味着攻击路径没堵死。Pass-4 是黑名单的进阶考法它把各种变形后缀都列上了但还是忽略了一个点——大小写。在部分服务器配置里Windows 文件系统和 Apache 的解析对大小写敏感度不一致shell.Php或shell.pHP这种变形可能绕过检测但依旧被解析。当然你要先确认目标环境是不是大小写敏感不能想当然。后面还有一关考的是“空格和点”。逻辑上是把黑名单匹配到的后缀直接删除或替换为空。如果你是shell.php.检测器先把末尾的点去掉再比对发现并没有命中黑名单就放行了但 Windows 系统保存文件时会自动去掉末尾的点落盘后就成了shell.php。这波操作的精髓在于“检测器看到的文件名”和“文件系统实际保存的文件名”不一致。还有利用shell.php%00.jpg这类空字节截断的不过在新版 PHP 里已经失效靶场中用到的场景有限。4.3 白名单与内容检测从改包到构造文件白名单关卡很难靠变形后缀糊弄过去因为检测器只认jpg、png、gif。这时候有两种思路。一种是尝试 Apache 多后缀解析漏洞上传shell.php.jpg如果服务器配置不合理实际解析时会选中其中的.php后缀来执行另一种是直接上传独立配置文件比如.htaccess或.user.ini用配置手段把同目录下的.jpg文件变相变成可执行代码。到了内容检测这一层你需要构造一个“图片马”。最简单的方式是找一个正常的 GIF 图片在文件末尾拼接一段恶意代码比如GIF89a文件头后面接?php ... ?。如果服务端只检查文件头几个字节这种图片马就能直接过。如果服务端用getimagesize()做完整校验图片马仍能通过因为它本身确实是一张合法图片。遇上二次渲染就麻烦一点。靶场会对上传后的图片重新生成一次相当于把图片里夹带的内容“洗”掉。这里有一个标准做法先上传一张图片马再下载渲染后的图片对比渲染前后文件的差异找到渲染后仍然保留的区块把恶意代码精确写入这些区块。用十六进制编辑器比如 010 Editor逐字节对比把恶意代码嵌入到渲染不会覆盖的那部分数据里。我当年在这关上花了一个下午老实说这种攻击在真实场景里成功率并不高但用来理解“服务端对文件内容的所有权”这个概念非常值得。4.4 条件竞争与综合关卡压轴硬货Pass-19 和 Pass-20 这类综合关卡考验的不再是单一技巧而是逻辑漏洞。以条件竞争关卡为例它的流程是先允许文件上传落盘以后再判断是否合法不合法再删除。看似有兜底但攻击者可以通过循环上传同一个文件来抢时间窗。我写过一段 Python 脚本用多线程持续上传构造好的恶意文件同时另一个线程不断请求已上传文件的 URL一旦在删除前命中就拿到了一个已落盘的脚本文件。这类题目提醒我们检测与删除永远不能作为唯一防线最好的方案是在存储层就把可执行风险隔离掉比如把上传目录设置为不可执行脚本或者存到对象存储里以静态资源方式访问。这是我在靶场里得到的最深刻教训之一。5. 踩坑实录与排查技巧upload-labs 打了这么多遍我把常见问题整理成了一份速查表帮你少走弯路。5.1 环境问题导致的关卡异常现象可能原因处理方法页面白屏PHP 版本过高老代码兼容性问题换 PHP 5.6/7.0或打开 display_errors 看错误编号上传后提示“文件目录不可写”目录权限不足Linux 下chmod -R 777 upload注意生产环境别这么干.htaccess关卡失效httpd.conf 未开启 AllowOverride确认AllowOverride All并重启 Apache上传成功但访问一片空白恶意代码被杀软拦截或 PHP 短标签未开检查杀软隔离区把短标签?改成?php图片马显示无法打开文件头损坏用十六进制工具修复文件头为合法图片头注意Windows 下跑靶场最容易踩的坑是杀毒软件把落盘的图片马直接隔离。这是干扰问题不是技术问题。给项目目录加白名单或者暂时关闭实时防护即可。5.2 通关思路的常见误区误区一一上来就改后缀。实际上很多关卡的检测取的是完整文件路径或者 MIME 值你要先看后端代码到底 check 的是哪个字段再决定改哪里。正确地做法是每一关先打开对应的处理脚本读一遍校验逻辑而不是黑盒乱试。误区二盲目使用大马和混淆代码。靶场里只需要证明“代码能执行”即可上传一个?php phpinfo(); ?就够用了不需要上免杀马。打靶的目的是理解原理不是练免杀这一点尤其要说给刚入门的同学在靶场里用力过猛反而学不到东西。误区三忽略 HTTP 响应细节。很多关卡服务器是否拦截会在响应头或响应体中给出明确提示比如返回“文件类型不正确”“只允许上传图片”。这些提示信息其实就是检测维度的线索认真看能帮你少请求十几遍。5.3 我的排查方法论刷 upload-labs 遇到异常我一般按这个顺序排先看请求包和响应包确认检测是否发生在传输途中再看目标目录有没有文件落地判断是“没传上去”还是“传上去被删了”最后翻服务端源码精确定位校验语句。这三步走完90% 的问题都能定位。还有一个小技巧把每一关处理脚本里move_uploaded_file()之后、删除逻辑之前的代码输出出来看比如加一段echo打日志能很直观地看到文件在什么时刻被处理、被删除。当然这是在本地靶场怎么折腾都行真实环境里你不能这么干但本地调试这招效率极高。6. 从靶场到现实防线开发者应该怎么做打穿每一关不应是终点反向思考怎么防才是这套靶场的真正价值。我把防线总结为“存储隔离 内容校验 解析系统一致性”三层防御。6.1 存储层断掉可执行链路上传目录必须被排除在脚本执行范围之外。Apache 下可以设置目录的php_admin_flag engine offNginx 下则用location ~ \.php$ { deny all; }。这样一来即使攻击者成功上传了文件也无法通过 HTTP 请求触发 PHP 解析。这个措施代价极低效果却非常显著属于“投入产出比”最高的一个加固动作。文件命名也要做文章。不要沿用用户上传时的原始文件名用时间戳加随机字符串生成新文件名并确保后缀由代码侧映射决定。比如无论用户传什么都强制保存为.jpg即使文件内容里有恶意代码落盘文件的扩展名也不会被解析为可执行脚本。6.2 校验层白名单和内容检测配合使用后缀必须使用白名单而且白名单要最终映射到服务器实际使用的 MIME 表不能让“允许jpg”和“服务器实际解析jpg的行为”脱节。内容检测要使用finfo读取真实文件类型而不是只看文件头几个字节。getimagesize()适合图片类校验能兼职验证文件是不是完整图片推荐在头像类上传场景里继续用。更激进的做法是所有上传文件不落本地磁盘直接进对象存储或云存储访问时通过独立域名和静态资源解析。这样即使上传内容不干净也无法渗透到应用服务器内部。这套架构已经成为很多公司默认采用的方案对大多数场景来说比在代码层苦苦校验更省心。6.3 解析层让服务器行为符合预期Apache 的多后缀解析特性、.htaccess的配置继承、.user.ini的自动包含这些都是“合法功能带来的漏洞面”。你要定期检查服务器配置关闭不必要的解析特性并确保中间件、PHP 版本、系统文件系统在文件名大小写、末尾空格等处理上行为一致。防御的本质不是堆砌手段而是消除“检测逻辑”和“解析逻辑”之间的所有不一致。从实操角度我建议每个做 Web 开发的团队都跑一遍 upload-labs并强制要求开发人员对每一关写出“为什么能绕过”和“如何修复”。这种练习对安全意识养成的帮助比任何安全培训都来得扎实。我个人刷完这套靶场最大的体会是文件上传漏洞从来不是单个函数的问题而是整条链路上多个“小信任”叠加的结果。你以为校验了后缀就安全了但服务器解析规则和你的想法不一致你以为重渲染能清掉恶意代码但渲染器保留的冗余数据恰好成为藏身之处。安全是一个系统行为而 upload-labs 用 21 个关卡把这个道理拆解得明明白白。最后再分享一个扩展玩法打完通关后你自己尝试在某个关卡上增加一种新的检测逻辑再试着绕过它。这种“出题”和“做题”交替的模式才是把上传漏洞吃透的最快路径。