CTF入门必刷:BabyUpload文件上传漏洞全解析

发布时间:2026/10/11 17:30:07
CTF入门必刷:BabyUpload文件上传漏洞全解析 CTF圈子里只要刷题时间够久几乎都躲不开一个叫 BabyUpload 的题目。它在各类主流练习平台的收录里属于最经典的入门级文件上传题页面上只有一个上传框一句“只能上传图片”的提示剩下的全靠你从请求和响应里一点一点猜。它不考太复杂的漏洞链却把文件上传从客户端到服务端的完整绕过路径安排得明明白白所以我一直觉得它是新手理解上传漏洞最好的“解剖标本”。这道题到底适合谁刷两种人。一种是刚学会抓包改包、但对服务端校验逻辑还停留在“加个 GIF89a 就行”阶段的新人另一种是已经能打通、但对自己的每一步操作都解释不了“为什么”的刷题党。BabyUpload 帮你完成的事就是建立一套“看到回显→脑补过滤规则→选择绕过方向”的决策流程。掌握了这套流程后面再遇到更复杂的上传题本质上就是在这张地图上加几个障碍物的事。1. 项目概述BabyUpload到底在考什么1.1 从一道题目能读出哪些信息先回到“读题”这个被很多人跳过的环节。CTF 里的任何一道 Web 题其实都自带三层信息平台名、题目名、页面长什么样。BabyUpload 这个命名直接点题说明考点就是文件上传页面如果只给你一个文件选择框和提交按钮那基本可以确定服务端至少有一层针对文件的接收和校验逻辑。这些信息单看哪一条都不起眼组合起来却能帮你提前划定攻击面。我做过不少文件上传题发现大多数叫 BabyUpload 的题目不管在任何平台复刻都会保留几个共同特征。第一它不提供源码你必须靠黑盒测试去猜后端逻辑第二上传成功的文件会被保存到固定目录然后你可能无法通过页面直接确认文件是否被解析第三响应信息往往非常简短比如“上传成功”“上传失败”“文件类型不正确”。这些文字不是摆设它们就是服务端判断逻辑的泄密窗口。这里顺便说个很常见的现象很多人一打开题就开始疯狂试后缀试到懵了就来问“为什么不行”。我通常反问一句你有没有认真读它的报错“文件类型不允许”和“上传失败”是完全不同的两回事。前者说明后缀名或 Content-Type 已经被校验后者可能是目录写入权限、文件大小、请求格式的问题。学会把响应当成源码来读是刷文件上传题的第一个基本功。1.2 文件上传题在 CTF 技能树里的位置文件上传之所以能成为 CTF 常驻考点背后是一个很朴素的现实几乎所有业务系统都需要上传功能头像、附件、照片、证书、文档而我们没法在用户点开页面之前就确定他会交上来一个什么样的文件。这是典型的“不可信输入进入可信环境”的问题天然适合当出题素材。更关键的是上传回来的文件一旦被当成代码执行后果往往是致命的。举个例子一个入门的 PHP 站点把上传目录放在 Web 根目录下攻击者只要传一个upload/shell.php访问时 PHP 解释器就会直接执行里面的代码。这意味着上传功能直接变成了远程代码执行入口。CTF 题里要拿 flag设计者又希望这道题兼具教学价值所以思路通常很直接把上传链路中的各个检测点暴露给你看你有没有能力逐个摸清、逐个绕过。从技能树角度看BabyUpload 的位置也很特殊。往上接 HTTP 协议、请求构造、前端知识往下接命令执行、内网渗透。它本身不难但它是很多后续漏洞链的起点所以几乎所有 Web 方向的新手学习路径里都会有这样一道题。1.3 新手为什么总卡在过滤规则上我不止一次看到有人能把绕过思路背得很熟比如“加个 GIF89a 文件头就能过内容检查”“php 改成 pHp 就能过后缀黑名单”但当你问他这些绕过为什么有效、为什么换个环境就失效时他就答不上来了。这就是典型的“会背不会用”。问题出在很多人没有理解服务端校验的顺序。一个上传请求到达后服务端不是把所有检测一次性做完的而是按顺序执行解析文件名、读取文件头、检查内容、移动文件。每一层都是一个独立的 if 条件任何一层先拒绝了后面的代码根本不会执行。如果你不知道当前环境到底走到哪一步你看到的就只有“失败”两个字而不是“哪一层失败”。BabyUpload 这类题的意义恰恰在于强迫你把“为什么”搞清楚。你得通过有限的回显反推出服务端代码的大致逻辑。这个反推过程看着很慢但它才是刷题真正的收获。等你形成了“基于响应判断检测点”的直觉很多难题突然就变得平易近人了。2. 文件上传漏洞的原理与四道检测关卡2.1 一次上传请求的生命周期要理解漏洞先得理解正常流程。浏览器里选择一个图片并点击提交浏览器会组装一个 HTTP 请求Content-Type 通常是multipart/form-data。请求体里每个上传文件对应一个 part里面包含文件名、Content-Type 和文件内容。服务端接收逻辑会先从请求体里取出文件存到临时位置然后按顺序执行判断扩展名是否允许、读文件头判断类型、把文件移动到目标目录、给用户返回结果。这中间有个关键点极易被忽略浏览器发送的 Content-Type 与“文件是否真的是图片”不是一回事。浏览器对 Content-Type 的判定来自操作系统对文件类型的猜测完全可以通过抓包工具手动改写。文件后缀同样是用户可控的。真正能证明文件“是图片”的只有服务端实际读取文件二进制头的那一步。所以一个完整的上传校验通常不是单点判断而是由拓展名、Content-Type、文件头、文件内容文本这四层构成的组合关卡。2.2 服务端的四道“安检门”把四道关卡分开看会更清楚。第一道是前端与客户端校验。它常常写在页面 JavaScript 里比如不允许选择非图片文件。有些题前端会直接拦截你提交看起来像“绕不过去”但这个限制只存在于浏览器里。你用抓包工具拦截请求后想怎么改都行前端校验在真实攻击场景里只能算用户体验层面的限制。第二道是扩展名检测。这是最关键的一道。服务端会取上传文件名做判断逻辑分两种黑名单机制过滤 php、asp、jsp 等危险后缀白名单机制只允许 jpg、png、gif。BabyUpload 系列多数用的是黑名单。黑名单本质上就不是安全设计因为字符匹配总会有漏白名单严格一些但在中间件解析规则存在漏洞时同样可能被绕过。第三道是 MIME/Content-Type 检测。服务端有时会查找请求中每个 part 的 Content-Type 字段是否为image/jpeg或image/png。这一层最容易被绕过因为请求是纯文本你只要改一行就行。即使 Burp 这类工具用不上纯 curl 命令也能轻松指定任意 type。第四道是文件内容检测。严谨的服务端会读取文件开头几个字节判断魔数比如 JPEG 的FF D8 FF、PNG 的89 50 4E 47、GIF 的GIF89a。如果只查文件头攻击者可以在 PHP 文件前面拼上合法图片头做成“图片马”从二进制头看是图片但后面还是可执行代码。有些服务端还会更进一步检查文件内容里是否包含危险函数名这就是我们要用等价语法来绕的点。2.3 绕过思路的底层逻辑与规则赛跑把四道关卡搞清楚后绕过思路本质上就是“和规则赛跑”如果服务端只看扩展名就改变扩展名的写法如果服务端只看 Content-Type就修改请求头如果服务端读文件头就伪造文件头如果服务端过滤内容关键词就换成能执行但不会触发关键词的等价语法。这套逻辑放之四海皆准不管底层是 PHP、JSP、ASP 还是 Python核心都是找到规则覆盖不到的灰度地带。学上传漏洞最重要的不是背 Payload而是建立“规则条件判断”的思维。你脑子里能把服务端代码脑补出来绕过就变成一个纯粹的编程问题看到一个 if就找它的漏洞看到一段字符串匹配就想哪里能构造歧义。这样做题效率会高非常多。3. BabyUpload 的完整解题思路拆解3.1 第一步永远是探测不是盲试拿到 BabyUpload 页面后我一般不急着一上来就传 Payload。我会先传几个正常文件比如test.png、test.jpg观察成功后的回显和文件访问路径。然后传一个最简单的test.php内容是纯文本看看服务端到底拦不拦后缀。接下来再把test.php改成test.png.php试试再改成test.phP甚至test.php.。我的习惯是给每次尝试开一张表格记录文件名、响应信息、是否成功。两分钟内就能得到一张粗略的过滤规则地图。举个实际例子如果传test.png显示“上传成功”传test.php显示“文件类型不允许”说明至少存在一个和 php 相关的过滤逻辑。接下来要确定的是它在匹配文件名里的 php 子串还是只匹配扩展名它有没有同时查 Content-Type 或文件内容判断方法也很直接传一个内容为空、但后缀为 png 的文件看是否成功再传一个内容为 PHP 代码、后缀伪装成 png 的文件。如果前者成功而后者失败说明服务端查了文件内容如果都成功说明内容检测存在盲区。这类小实验看起来繁碎但信息量极大。还有一个容易漏掉的信息收集点上传成功后文件到底被放在哪里有的题回显直接给了完整路径有的默认放在upload/目录需要逐个去尝试。路径信息对后续访问和触发解析至关重要没有它你就算成功上传了代码也找不到地方让代码跑起来。3.2 黑名单绕过的三个常用方向这一节先讲通用方向具体 Payload 到实操章节再展开。第一个方向是“等价后缀”。黑名单只挡了 php但 PHP 还能以 phtml、php3、php4、php5 等后缀运行取决于服务器配置。做题时这些后缀不一定好使但每次都应该试一遍。成本极低收益可能极高。第二个方向是“名字变体”。大小写混写pHp、后缀前加点php.、加空格php、加%00截断、叠加多个点a.php..都能让基于字符串匹配的黑名单出现误判。这里有层技术细节值得注意不同的校验函数对尾部空格和点的处理不一样有些会主动去除再判断有些不会中间件在保存文件时也可能把尾部点删除。必须结合服务端实际使用的逻辑来判断哪个变体有效。第三个方向是“配置文件和解析特性”。Apache 在处理多后缀文件a.php.jpg时可能按从右往左找它认识的后缀来解析如果.htaccess这类配置没被禁用你还能直接控制目录的解析行为把某个后缀强制当成 PHP 来执行。这个方向不确定性高但一旦成功基本就是降维打击。3.3 内容检测与图片马的构造思路当你发现后缀已经绕过、Content-Type 也改成图片了却仍然上传失败大概率是卡在文件内容检测。此时就要上“图片马”。构造方式其实很简单在 PHP 代码前加上GIF89a或者加上 JPEG/PNG 的文件头字节让文件从二进制头上看是一张图片内容里却藏着代码。严格一些的服务端还可能检查上传文件的内容文本里有没有危险函数名这时候就要搭配下一小节说的等价语法。还有一个值得注意的点图片马的可见部分是纯文本用文本编辑器直接粘贴就够用。但有的服务端会真的去解析图片结构检查图片是否完整这类题就要求你把代码插入 GIF 的某一帧数据里手法会更复杂。BabyUpload 这个难度通常不需要做到那一步但你要知道存在这种升级可能免得换个题目就一脸懵。3.4 等价语法绕过危险关键词过滤很多内容过滤只会匹配特征字符串比如?php和eval。因此有必要知道PHP 引擎还支持其他入口写法。? ?是短输出标签能输出变量但不执行代码% %是 ASP 风格标签能否使用取决于 short_open_tags 配置script languagephp是老版本 PHP 支持的写法。这些等价语法在不同环境里的有效性不一样但在做题时都值得一试。我的经验是判断语法能不能用之前先想想 PHP 配置的默认值。很多环境开了短标签短语法就能用但如果文件内容里缺少完整的 PHP 起始标签一些环境只会把它当普通 HTML 输出而不是执行。更稳妥的办法是直接抓关键点先确认这个环境到底会不会解析 HTML 里的 PHP 标签再决定用哪种写法。你可以在本地试验环境里开着错误日志随时看到底哪一步没解析成功。4. 实操记录从搭环境到完整通关4.1 本地搭一套 BabyUpload 同款靶场不少新手怕动本地环境觉得麻烦但我强烈建议先搭一套。没有远程题你就没法反复调参做每一步都像盲人摸象。本地验证的好处在于你可以随时修改服务端过滤代码观察不同规则对同一个 Payload 的拦截效果这比在远程题里瞎猜高效太多。基于常见 BabyUpload 的出题思路我会这样写一份模拟代码仅用于本地学习验证?php $upload_dir ./upload/; if (!empty($_FILES[file])) { $filename $_FILES[file][name]; $ext pathinfo($filename, PATHINFO_EXTENSION); $blacklist array(php, phtml, php3, php4, php5, htaccess); if (in_array(strtolower($ext), $blacklist)) { die(文件类型不允许); } if ($_FILES[file][type] ! image/jpeg $_FILES[file][type] ! image/png) { die(文件类型不正确); } if (preg_match(/\?php/i, file_get_contents($_FILES[file][tmp_name]))) { die(内容包含非法字符); } if (move_uploaded_file($_FILES[file][tmp_name], $upload_dir . $filename)) { echo 上传成功: . $upload_dir . $filename; } else { echo 上传失败; } } ?这段代码综合了三个常见考点扩展名黑名单、Content-Type 校验、内容里过滤?php。实际题目的过滤顺序可能不同但整体思路一样。本地跑起来之后你可以一行一行注释掉某个校验看它对最终结果的影响。4.2 用 curl 和 Burp 手工构造上传请求我习惯先用 curl 做快速测试因为终端里能直接看到完整请求和响应不用开 GUI 工具。先传一个正常 PNG 文件curl -i -F filetest.png;typeimage/png http://localhost/upload/index.php正常情况会返回“上传成功”。接着把文件内容保持纯文本但名字改成test.php再传curl -i -F filetest.php;typeimage/png http://localhost/upload/index.php如果这次返回“文件类型不允许”说明扩展名黑名单生效了。现在把文件名改成test.php.jpg试试。如果成功说明服务端只是取了最后一个点后面的“jpg”并没有真正匹配到“php”这个危险子串。用 Burp 手工操作的好处是能直观看到 multipart 请求体里的 Content-Type 字段。如果服务端只判断文件 part 的 Content-Type 而没有检查二进制内容一个 PHP 文件只要把Content-Type: application/octet-stream改成image/png就能混过去。这个改动在 Burp 里就是一行文本非常直观改完直接转发。4.3 多检测点同时生效时的组合绕过如果我把上面代码里的三个检测逻辑全部打开单靠一个技巧就过不去了。此时要做的是组合文件名改成test.PhP.png让扩展名判定取到 png 从而绕过黑名单Content-Type 改成image/png文件内容用GIF89a开头后面接执行代码。一次请求把三个检查全部蒙混过去。具体构造流程我习惯先用文本编辑器写文件内容GIF89a ?php phpinfo(); ?然后另存为test.PhP.png。这里有个细节要特别注意pathinfo函数对test.PhP.png提取到的扩展名是 png黑名单匹配时壳到的是 png因此能通过第一关但 Apache 在解析时可能会从右往左寻找自己认识的后缀从而识别中间的php后缀。这个特性因版本和配置而异本地复现时一定要多试。如果是 Nginx 加 PHP-FPM 的环境这类多后缀解析通常不生效所以题型和服务器的强相关性在这里就体现出来了。碰到全拦的环境不要急。你可以在本地把服务端代码改成不同组合分别测试只拦后缀、只拦 MIME、只拦内容、全部拦截这四种情况并把结果记下来。这个过程能快速建立你对过滤逻辑的直觉也是我刷此类题最有效果的训练方式。4.4 上传成功之后访问、解析与收尾验证当服务端返回“上传成功”并且给了路径后下一步就是直接访问这个文件路径看能不能触发解析。我一般访问路径后关注三点返回的是执行结果还是文件原文还是 404。如果返回结果里能看到phpinfo()的输出说明你已经打通了整条链路。如果浏览器直接输出文件内容说明服务器把它当成普通静态文件处理了解析配置有问题或目录不在脚本执行范围内。如果直接 404可能是路径拼接出错也可能是上传目录位于 Web 根目录之外或者文件名被随机化处理了。经常有人走到这一步功亏一篑上传成功了路径也返回了但访问就是 404。原因大概率出在路径上而不是绕过逻辑上。拿 flag 之前建议多花一点时间验证文件确实落在了预期位置。等到访问能稳定返回执行结果之后这道题的核心流程才算真正闭环了。5. 高频报错与排查技巧实录5.1 上传失败但没有任何提示怎么办最让人头疼的是回显只有“失败”两个字连失败原因都不给。这种情况下我一般从三个方向排查文件大小是否超出限制、上传目录是否可写、请求的字段名是否和代码里对应。你可以先用一张确定合法的 png 文件测试如果不成功那问题就不在漏洞逻辑而在环境本身如果合法文件能成功再用二分法缩小范围。5.2 “小白鼠测试法”快速定位过滤规则我的一个习惯是把要测试的多个后缀做成一个小清单用同一个请求轮着测。比如依次测test.php、test.pHp、test.php3、test.phtml、test.php.、test.php%00.png旧式截断成功率低但值得测一次每次只改文件名保持其他参数不变。响应结果能直接告诉你黑名单匹配时是否忽略大小写是否先清理了点和空格是否存在路径截断。如果试了十几个后缀全部失败就要大胆怀疑是白名单机制。白名单场景下你要考虑的不是“怎么让 php 后缀通过文件名检查”而是“怎么让一个看起来像图片的文件被服务器当成 PHP 执行”。这就得回头依赖解析规则和中间件配置来打了思路要整个调转过来。5.3 常见问题速查表实际刷题过程中下面这些现象最常出现我整理成了表格方便对照现象可能原因排查方向传 png 成功传 php 失败扩展名黑名单测试大小写、等价后缀、双后缀改 Content-Type 后成功MIME 校验继续用图片马绕过内容检查加文件头依然失败内容检测更严格用短标签或等价语法绕过危险函数匹配上传成功但访问 404路径不对或目录不可访问找真实路径、检查解析配置上传成功但直接下载文件解析配置关闭换服务器思路尝试其他绕过点这张表看起来基础但每一条都在真实刷题里反复出现。尤其是最后一行文件没有被执行而是被下载说明服务器没有把上传目录交给脚本解析器。这种情况下你即便绕过了所有校验拿到的入口性质也完全不同。注意到这个区别能帮你省下大量无效尝试。5.4 容易被忽略的路径信息收集很多人只盯着上传逻辑却忘了观察成功回显里是否带了完整路径。无论是原样返回还是做了拼接路径信息对后续访问都极其重要。如果回显没给路径我会上传一个内容为特定标记的图片然后逐个尝试几个常见目录用内容匹配的方式确定它在磁盘上的位置。这个方法在真实渗透测试里同样适用属于低成本高回报的基础操作。6. 从 CTF 到真实业务文件上传漏洞的防御复盘6.1 文件上传功能的最低限度安全清单一道题做完如果没有回到防御视角知识点就很难沉淀。这里整理的是真实业务里文件上传功能最低限度要做好的检查项。首先用白名单而不是黑名单只允许 jpg、png、gif 等明确后缀其次重命名上传文件不保留用户提供的原始文件名用随机字符串加白名单后缀第三校验文件二进制头而不仅是 Content-Type第四限制文件大小第五把上传目录与 Web 可执行目录隔离第六设置目录权限禁止脚本执行第七如果是图片需求可以走服务端二次渲染。这条清单看着简单每一条都能对应到一个具体绕过手法。重命名直接打掉了路径依赖和文件名混淆白名单打掉了后缀变体二进制头校验打掉了 Content-Type 伪造目录隔离打掉了上传后直接访问解析的链路。一道题学会一个防御点比死记十个 Payload 更有价值。6.2 纵深防御别做单点对抗在实际业务里我从来不会只依赖某一个校验点因为任何单一规则都可能被绕过。纵深防御的意思是每一层都尽量拦截层与层之间互相补充。举个例子即使我用了白名单我仍会在上传后剥离可执行权限即使我把目录放到静态服务我仍会对上传文件做一次内容安全检查。这样即便某一层被绕过后面的层还能兜底。这也解释了为什么 BabyUpload 这类题会同时设置多个检测点。它模拟的正是真实系统的多层校验而不是让人用一个 Payload 打天下。理解这一点后你在解题时也会更有耐心去逐层试探而不是拿到题就盲目背 Payload。6.3 新手的进阶练习路线如果你想继续巩固文件上传方向我的建议是三步走。第一步做三到五道不同版本的 BabyUpload每道题都写一份“过滤规则 绕过方案”的复盘第二步尝试只给一个上传口但要求自己在没有源码的情况下通过响应反推出后端过滤代码的大致逻辑第三步把题目逐步变形比如加入随机文件名、加二次渲染、换 Nginx 解析看自己的绕过思路能不能跟上。我在实际练习里最大的体会是文件上传题的通关效率和你对“服务端如何处理一次请求”的熟悉程度成正比。与其焦虑自己掌握的 Payload 不够多不如多搭几个本地环境把检测点和解析行为一个个验证清楚。等你真的能根据响应信息预判出后端大概长什么样BabyUpload 就再也不会成为你的拦路虎了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询