去年年末那阵子,我一直卡在文件上传漏洞这个坎上。靶场文档翻了不少,视频也刷了好几遍,原理说得头头是道,可真让我上手操作一次,却总觉得差点意思。直到某个周六下午,我认认真真把 upload-labs 第一关过了一遍,用蚁剑成功连上那个一句话木马的时候,那种"噢,原来整个链路是这么走通的"的踏实感,才真正让我觉得自己入了门。
这篇文章就把我那次完整的实操过程记录下来,从搭靶场、写木马、绕过前端校验,到蚁剑连接成功,每一步踩过的坑和当时的思考都会写出来。如果你也是刚接触文件上传、正准备刷 upload-labs 的新手,这篇文章应该能帮你少走不少弯路。
1. 准备阶段:工具选型与靶场搭建
1.1 为什么用 upload-labs 作为练习靶场
upload-labs 是一个专门用来练习文件上传漏洞的本地靶场项目,作者是 c0ny1。它把常见的上传绕过场景按关卡拆开,从最简单的 JS 前端校验,一路做到二次渲染、条件竞争、中间件解析漏洞,每一关都对应一个真实的绕过思路。坦白讲,市面上讲文件上传漏洞的文章一大堆,但多数是零散的知识点,upload-labs 胜在成体系,能让你一关一关顺着打,循序渐进地把绕过技巧吃透。
当时我在 GitHub 上把它 clone 下来之后,扔进了 phpstudy 的 WWW 目录,启动 Apache 和 MySQL 就能直接访问。这个靶场对运行环境的要求很低,理论上讲,只要 PHP 版本不是太离谱都能跑起来。我自己用的是 PHP 5.4.45,老版本兼容性最好,如果你电脑上装的是 PHP 7.x 甚至 8.x,部分关卡可能会有小问题,建议在 phpstudy 里多装一个低版本 PHP 备用。
还有一点要提醒新手:upload-labs 是一个完全本地运行的环境,所以你可以放心大胆地折腾。我在实际操作里见过不少朋友,第一次接触文件上传漏洞就想着去测公网上的网站,这是非常不可取的。在自己电脑上搭靶场,既能完整体验漏洞利用的全过程,又不会惹上任何法律风险,这才是正确入门的方式。
1.2 蚁剑到底是什么,为什么选它
蚁剑(AntSword)是一个开源的跨平台网站管理工具,本质上是用来连接和管理 WebShell 的客户端。它的核心功能就是通过一个"一句话木马"作为入口,用客户端和你自己的服务器之间建立一个通信通道,之后你就可以通过图形界面操作目标网站的文件、数据库、虚拟终端等。
选蚁剑而不是其他同类工具,我当时主要考虑三点:
第一,蚁剑是开源免费的,在 GitHub 上就能拿到源码,社区活跃度很高,遇到问题搜一搜基本都有答案。第二,它的编码器机制非常灵活,数据在传输过程中可以经过各种自定义编码,这在后面刷更高关卡的时候特别有用。第三,界面清爽,对新手友好——左侧是目标列表,右键就能添加数据,连上之后文件管理器、虚拟终端、数据库管理一目了然,不会让你面对一堆复杂的配置无从下手。
下载蚁剑的时候我第一次就踩了坑。GitHub 上的源码直接 clone 下来是没法直接运行的,你需要先安装依赖。官方提供了已经打包好的发行版,建议直接去 Releases 页面下载,省事很多。我自己用的时候下载的是 AntSword-Loader,这是个加载器,它会帮你把核心代码跑起来,界面是中文的,配置好本地 WebSocket 地址后就能正常使用。
2. 看清对手:upload1 这关到底在考什么
2.1 第一关的源码逻辑拆解
upload-labs 的第一关,全名叫"JS 校验绕过"。打开页面,你会看到一个很朴素的文件上传表单,和一个写着请选择图片的按钮。很多人上来就直接选个 PHP 文件点上传,结果被弹窗拦住,提示"不允许上传 php 后缀的文件"。
这里就要说一个关键概念了:这个弹窗不是服务器给你的响应,而是浏览器自己弹出来的。也就是说,这一关的校验逻辑完全写在前端 JavaScript 里,根本没有经过服务器。我们来看一下这关的核心源码:
function checkFile() { var file = document.getElementsByName('upload_file')[0].value; var allow_ext = ".jpg|.png|.gif"; var ext_name = file.substring(file.lastIndexOf(".")); if (allow_ext.indexOf(ext_name) == -1) { alert("不允许上传" + ext_name + "后缀的文件!"); return false; } }这段代码的逻辑很直白:拿到你选的文件名,截取最后一个点后面的后缀,然后去一个字符串里找有没有这个后缀。如果没找到,就弹窗拦下来。你看,这里所有的动作都发生在浏览器里,页面文件本身都没有向服务器发送任何数据。
明白了这一点,这一关的难点就消失了。既然校验在浏览器里,那我们就有很多种办法绕过它。关键不在于掌握多少奇技淫巧,而在于你能不能意识到一个核心事实:前端校验只是一种用户体验优化,根本不是安全防护。
2.2 为什么后端没有兜底校验
我们再来看看这关在服务器端是怎么处理的。
$is_upload = false; $msg = null; if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $temp_file = $_FILES['upload_file']['tmp_name']; $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']; if (move_uploaded_file($temp_file, $img_path)) { $is_upload = true; } else { $msg = '上传失败'; } } }整个后端脚本的逻辑是:判断上传目录存在,然后把临时文件移动到目标目录,文件名直接用你上传时的原始文件名。整个过程没有对文件后缀做任何检查,没有对文件内容做任何检查,甚至连文件大小都没有限制。
也就是说,只要你能让请求到达服务器,服务器就照单全收。这其实是很多初学阶段容易忽略的问题——你以为网站做了校验就安全了,但校验如果只放在客户端,那对攻击者来说就和没穿衣服一样。这一关的设计目的,就是让你最直观地感受到"前端校验可以骗、后端校验才是关键"这个文件上传漏洞的核心矛盾。
3. 实操全过程:从构造木马到蚁剑连接成功
3.1 写一个能用的木马文件
在实操任何工具之前,得先有一个能被连接的目标。Upload 1 的最终目标是上传一个 PHP 的一句话木马,然后用蚁剑连上它。
所谓一句话木马,简单理解就是一个极简的 PHP 脚本,它本身不做任何事,只是接收你通过 POST 请求传过来的参数,然后交给 PHP 的 eval 函数执行。这样,攻击者就能通过这个小小的文件,实现在服务器上执行任意 PHP 代码。
我写的木马文件是这个样子:
<?php @eval($_POST['x']); ?>解释一下这几个部分的含义:<?php ... ?>是 PHP 代码标记,@是错误抑制符,作用是即使执行出错也不输出错误信息,避免暴露细节;eval()是 PHP 中一个可以把字符串当作 PHP 代码执行的函数;$_POST['x']表示从 HTTP POST 请求中获取键名为 x 的参数值。整句话连起来,就是你往 POST 数据里塞一段 PHP 代码,它就会在服务器上原样执行。
这里要注意几个细节:
第一,eval 本身是 PHP 的一个语言构造器,不是普通函数,所以很多安全设备不会对它做函数级拦截。第二,@符号一定不要省,否则你在蚁剑测试连接的时候,一旦出错页面会直接抛出 PHP 警告信息,拖慢排查速度。第三,文件保存为 shell.php 就行,后缀必须是 php,因为它要靠 PHP 解析器来解释执行。
3.2 三种绕前端校验的方法,新手按需选择
写好 shell.php 之后,直接去页面上传是肯定会被拦的,因为前端 JS 不认识 .php 这个后缀。我第一次操作的时候,实打实地试了三种绕过方式,各有优缺点,给新手朋友们逐一排一下优先级。
方法一:直接禁用浏览器 JavaScript
这个方法最暴力,也最省事。既然检测逻辑是浏览器里的一段 JS 代码,那我把 JS 关掉,它不就拦截不了了吗?我用的浏览器是 Chrome,具体操作是在设置里找到隐私设置中的网站设置,把 JavaScript 选项设为不允许。或者更简单的办法,直接在地址栏左边点一下网站设置图标,把 JavaScript 权限改成禁止。
刷新上传页面,再选 shell.php 上传,你就会发现页面安安静静地提交了请求,没有任何弹窗,上传成功。这种方法的好处是零成本,缺点是你得记得传完文件之后恢复 JS 权限,不然之后浏览其他网站会造成各种不便。
方法二:通过浏览器开发者工具删掉校验函数
如果你不想全局禁用 JS,可以在开发者工具里进行微操。按 F12 打开 DevTools,切到 Sources 面板,找到当前页面的 JS 代码,选中 checkFile 函数,然后直接右键 Delete。或者更优雅一点的做法:选中函数代码后,把它整体替换成 return true。
这种方式比禁用 JS 更精准,而且能让你真切感受到"前端代码是可以被随意篡改的"。不过我建议新手朋友第一次做的时候,还是优先用禁用 JS 的方法,因为修改代码容易误操作,万一删错了还影响整个页面的交互逻辑。
方法三:抓包改请求
这个方式我是后面才掌握的,但它其实是最接近实战的。用 Burp Suite 开启代理,浏览器这边设置好代理地址和端口,然后正常在页面上选择一个非 php 后缀的文件(比如 1.jpg)点击上传。Burp 拦截到请求之后,把文件名 1.jpg 改成 shell.php,再放行。因为前端校验的是你本地选择文件的名字,而请求到达服务器时已经经过了 Burp 的修改,所以服务器收到的是 shell.php。
这种方法的好处是不用动浏览器,也不用改前端代码,完全从网络请求层面完成了绕过,这正是实际渗透测试中绕前端校验最通用的手法。但在第一关这个场景里,它的步骤相对繁琐,等你把前两种方式都跑通了,再回来练习这种方式会更有收获。
3.3 上传之后不要急着连接,先确认文件位置
无论你用哪种方法成功上传了 shell.php,先别急着打开蚁剑,你需要先确认文件到底存到了哪里。
upload-labs 的第一关会把上传文件保存到 upload 目录下,URL 路径一般是这样的:
http://127.0.0.1/upload-labs/upload/shell.php我在浏览器里直接访问这个路径,页面一片空白。这个结果是正常的,因为 shell.php 本身不会输出任何内容,它的功能只是执行你 POST 过来的参数。页面空白也在一定程度上说明文件已经成功落盘了。
这里要特别提醒一个新手很容易糊涂的点:一个 php 文件能正常访问,不代表它的代码是正常的。如果你访问 shell.php 看到了 PHP 报错信息,比如 Syntax error、Unexpected token 之类的,一定要回头检查文件内容。常见的问题有:分号用了中文全角;、单引号写成了中文引号、<?php前面多了一个空格。这些细小的失误我全都踩过,每次都得重新上传一遍才能排除干净。
3.4 蚁剑连接的全过程记录
文件上传完成后,打开蚁剑,正常情况下会自动弹出加载器。进入主界面后,右键左侧空白区域,选择"添加数据",这时候会弹出配置界面,需要填几项关键信息:
- URL 地址:填
http://127.0.0.1/upload-labs/upload/shell.php - 连接密码:填
x,这个必须和你木马里面的$_POST['x']对应 - 连接类型:选择
普通,原理是直接通过 POST 传递参数给 eval 执行 - 编码器:第一关场景用默认的
default就行
填完之后点击"测试连接",我回忆一下当时的心情,真的是手心里捏了一把汗。几秒钟之后,右侧返回了一个绿色的成功标识,那一刻心才放下来。
测试通过之后,双击这个目标条目,蚁剑会打开一个类似文件管理器的界面,里面能直接看到 upload-labs 的目录结构。你能看到 upload 目录下所有文件,可以右键下载、上传、重命名、删除。旁边的虚拟终端模块也能直接敲 Linux 命令和 Windows 命令。虽然对第一关来说,我们只是验证了一下功能,但那一刻我是真的体会到了文件上传漏洞的杀伤力——一个看似不起眼的"上传功能",一旦没做好校验,等于把整台服务器的控制权交到了攻击者手里。
4. 新手最容易翻车的五个问题
4.1 蚁剑测试连接一直失败
理想很丰满,现实很骨感。我第一遍操作的时候,测试连接直接失败,红色错误提示瞬间把我从"即将通关"的美梦里拉了回来。排查了一遍,发现是 URL 地址填错了——我把路径写成了http://127.0.0.1/upload/shell.php,少了 upload-labs 这个目录层级。
蚁剑连接失败的原因基本上集中在四个方面,我整理了一下:
| 可能原因 | 排查思路 |
|---|---|
| URL 地址拼写错误 | 用浏览器直接访问该地址,确认文件是否存在 |
| 连接密码与木马不一致 | 检查木马里的$_POST['x']和蚁剑里的密码是否相同 |
| 木马代码有语法错误 | 浏览器访问时是否出现 PHP 错误提示 |
| 后端拦截 | 第一关不存在这种情况,但高关卡需要留意 |
4.2 文件上传成功,但蚁剑提示文件不存在
这个问题其实和上面的问题原因很相似,但有一个非常隐蔽的可能:你上传文件时用的名字是shell.php,多了一个空格,或者编辑器自动加了 .txt 后缀,导致实际落盘的文件名跟你预期的不一样。
我在操作时就犯过这个错误。当时用 Windows 自带的记事本写的木马文件,保存的时候没注意,文件名变成了shell.php.txt。上传之后访问http://127.0.0.1/upload-labs/upload/shell.php.txt,浏览器直接把源码给显示出来了,因为服务端根本不把它当 PHP 解析。后来在 upload 目录里看到文件真实名字才恍然大悟。
所以上传之后,一定要先去 upload 目录确认一下文件真实名称和后缀,再填 URL 给蚁剑。
4.3 PHP 版本过高导致部分关卡无法复现
这里多说一句 upload-labs 的兼容性问题。如果你用的 PHP 7.0 以上版本,第一关倒是没什么影响,但后面的关卡里,有些和上传路径拼接、文件头检测相关的知识点,在高版本 PHP 下会表现得不那么明显,甚至直接失效。
我的建议是使用 phpstudy 这类集成环境,安装两个 PHP 版本:5.4 和 7.4。跑 upload-labs 的时候切到 5.4,遇到个别关卡需要函数特性再切到 7.4,这样能让你尽可能完整地还原漏洞触发的原始环境。
4.4 在浏览器里访问木马页面时直接看到了报错信息
如果你访问 shell.php 时,页面出现了类似这样的报错:
Parse error: syntax error, unexpected end of file那几乎可以肯定文件内容出了问题。最常见的原因是 Windows 记事本默认把文件存成了带 BOM 的 UTF-8 格式,BOM 头会被 PHP 解析器当成输出内容,导致后续代码出错。解决办法是用 NotePad++ 或 VS Code 打开文件,另存为 UTF-8 无 BOM 格式,再重新上传。
4.5 蚁剑能连上,但虚拟终端没有权限
还有一个很常见的问题:蚁剑能连上,文件也能列出来,但点击虚拟终端输入命令后,返回的却是"权限 denied"或者找不到命令。
这是因为 PHP 运行的用户对目标文件的执行权限不够,特别是 phpstudy 在 Windows 上运行时,文件系统权限体系比较复杂。第一关场景下通常不会遇到,但如果你后面用了 Apache 以服务方式运行,确实可能出现这个问题。解决办法是修改 Apache 的运行用户,或者在蚁剑的虚拟终端里切换到其他用户。不过新手阶段不用纠结这个,先把上传链路摸通才是重点。
5. 打通 upload1 之后,后面还能干什么
5.1 后续关卡的思路预告
第一关通过之后,你已经掌握了文件上传漏洞最本源的逻辑:一切绕过都从"服务端没有校验"或者"校验可以被骗"出发。接下来 upload-labs 的关卡会逐步加大难度,核心思路会围绕这样几个方向展开:
- MIME 类型校验:服务器检查 Content-Type 字段,需要通过改包绕过
- 扩展名黑名单过滤:服务器拦截常见脚本后缀,需要尝试大小写绕过、双重后缀、空格绕过
- 白名单校验:只允许上传 jpg、png 等图片类型,需要考虑解析漏洞配合使用
- 文件内容校验:检测文件头是否包含图片特征,需要用图片马绕过
- 二次渲染:图片会被重新生成,需要利用特定格式的图片木马
我的建议是,每一关通关后,都回头看一下源码,搞清楚服务端到底校验了什么,你的绕过手法是针对哪个校验环节做的。只刷关不读源码,练几关就会茫然。
5.2 千万别把技能用错地方
写到这里,必须认真多说一句。文件上传漏洞、一句话木马、蚁剑这些技能,是安全从业者用来做授权测试、漏洞挖掘和应急响应的工具,不是让你拿去攻击别人网站的利器。
在黑产领域,这类工具被大量滥用,比如上传 WebShell 之后篡改页面、植入后门、拖取数据,这些行为都会带来严重的法律后果。我们学习这些技术,应该把它们放在本地靶场、CTF 比赛、SRC 漏洞报告平台这些授权场景里使用。上线之前,也一定要先确认目标系统是你自己搭建的,或者已经获得了书面的测试授权。
这是安全圈子最基本的职业底线。如果你真的对安全攻防感兴趣,后面还有很长的路要走,别让这条路开始的方向就走偏了。
走完 upload1 这一关,我对"什么叫实战"有了不一样的理解。书本上讲一万遍"前端校验不可靠",都不如自己在浏览器里删一次 checkFile 函数来得记忆深刻。蚁剑连接成功的那一刻,我盯着屏幕上的文件列表愣了几秒——一个两行代码的木马文件,真的就能拿到服务器目录的控制权,这种冲击感是任何理论教程都给不了的。
如果你也正在刷 upload-labs,卡在某一关的时候别急着看 WP。先去读源码,再去想想这个校验是前端还是后端,是黑名单还是白名单,是过滤后缀还是过滤内容。把这几个问题想明白了,再动手去试,多数关卡自己就能解出来。过完第一关,你已经有能力在这个领域走得更远了。