资讯中心

文件上传漏洞实战:从绕过校验到Webshell利用的完整攻防解析

📅 2026/8/12 14:46:18
文件上传漏洞实战:从绕过校验到Webshell利用的完整攻防解析
1. 项目概述一次典型的文件上传漏洞实战复盘最近在复盘一些经典的CTF题目特别是Web安全方向的发现很多新手朋友对文件上传这个老生常谈的漏洞点理解还是停留在“传个一句话木马”的层面。其实一个设计精良的文件上传挑战考察的是对HTTP协议、服务器配置、后端语言特性乃至WAFWeb应用防火墙绕过手法的综合理解。今天我们就来深度拆解一道来自GXYCTF2019的题目——“BabyUpload”。这道题被收录在BUUCTF平台上是很多人的Web入门必做题也经常出现在“buuctf snake”、“bp类型buuctf”这类解题思路的搜索关键词里。它看似简单却巧妙地设置了多层过滤非常适合用来梳理文件上传漏洞的完整攻击链。简单来说这道题模拟了一个存在缺陷的上传功能。我们的目标很明确绕过前端和后端的重重检查成功上传一个能够被服务器解析执行的恶意脚本文件通常是Webshell并利用它来读取服务器上的敏感文件即Flag。整个过程涉及前端JS校验绕过、MIME类型检测、文件扩展名黑名单、文件内容检测等多个环节。下面我将以第一人称视角带你完整复现我的解题思路、踩过的坑以及最终的成功路径。无论你是刚接触CTF的新手还是想巩固文件上传知识点的朋友相信这篇详尽的复盘都能给你带来收获。2. 环境探测与信息收集面对任何Web挑战盲目测试是最低效的。我的习惯是先当一会儿“乖用户”摸清楚这个上传功能到底提供了什么又隐藏了什么。2.1 前端界面与基础功能观察访问题目链接首先映入眼帘的是一个非常简洁的文件上传表单。通常包含一个input typefile选择框和一个提交按钮。第一步我随手选择了一个正常的图片文件比如test.jpg进行上传。结果与初步分析成功上传服务器返回了文件上传成功的提示并且显示了文件的访问路径例如uploads/test.jpg。这很好说明基础的上传功能是正常的服务器确实存在一个用于存储上传文件的目录uploads/。关键信息这个返回的路径是绝对关键的信息。它告诉我们上传后的文件存放在哪里这是我们后续尝试访问Webshell的URL依据。前端校验猜测在点击提交时我没有看到明显的文件选择框变化或延迟初步判断可能没有复杂的JavaScript实时校验。但这一点不能完全确定需要进一步验证。注意在实际渗透测试或CTF中返回的文件路径有时会包含时间戳、随机字符串或哈希值以防止直接猜测文件名。这道题返回的路径相对简单降低了难度但思路是相通的——我们必须拿到这个路径。2.2 绕过前端JavaScript校验很多上传功能会在前端用JavaScript对文件扩展名进行白名单校验比如只允许.jpg, .png, .gif。这是一种非常初级且极易绕过的防护。测试方法直接修改HTML在浏览器中按F12打开开发者工具找到文件上传的input元素。我们可以尝试修改其accept属性如果存在或者直接修改网页中内联的JavaScript校验函数使其总是返回true。使用代理工具拦截这是更通用、更可靠的方法。我使用Burp Suite这也是“bp类型buuctf”中“bp”的常见指代在本地设置好代理。具体操作在Burp Suite中开启代理拦截Proxy - Intercept is on。回到浏览器选择一个webshell.php文件点击上传。此时请求会被Burp Suite截获。如果请求根本没有被发出来或者页面直接弹出“文件类型不允许”的警告那基本可以确定存在前端JS校验并且校验失败了。为了绕过我们可以先上传一个合法的图片文件然后用Burp Suite拦截这个请求再将请求体body中的文件内容替换成我们的Webshell代码同时将文件名也改为shell.php。因为校验只在浏览器端发生一旦请求发出服务器端接收到的就是我们修改后的内容。本题情况在“BabyUpload”这道题中经过测试我发现即使直接选择.php文件也能正常发出HTTP请求并被服务器接收处理没有遇到前端JS拦截。这说明第一道防线前端校验不存在或者非常薄弱。我们的战场直接转移到了服务器端。3. 服务器端过滤机制深度剖析服务器端的防护才是真正的挑战。我们需要像剥洋葱一样一层层试探它的过滤规则。3.1 第一层扩展名黑名单与MIME类型检查我首先尝试直接上传一个最简单的PHP一句话木马文件内容为?php eval($_POST[‘cmd’]);?命名为shell.php。服务器响应上传失败返回错误信息提示“文件类型不允许”或类似的语句。这证实了后端有过滤。测试与绕过方法修改扩展名尝试一些常见的畸形扩展名看黑名单是否完整。shell.php- 被拦截。shell.pHp(大小写) -测试结果通常会被拦截因为后端一般会统一转为小写再比对。shell.php3,shell.phtml,shell.phps- 这些是PHP的其它可执行扩展名取决于服务器配置。本题测试可能被拦截说明黑名单较全。shell.php.(末尾加点) - 在某些系统处理中点会被去除变成shell.php。shell.php%20(末尾加空格) - 同理空格可能被去除。shell.php::DATA(NTFS流) - 主要针对Windows服务器本题环境通常是Linux无效。shell.jpg.php- 被拦截因为通常检测最后一个点后面的后缀。shell.jpg.php(双扩展名) - 同上。修改Content-Type使用Burp Suite拦截上传shell.jpg合法图片的请求然后做两件事将文件内容替换为PHP代码。将HTTP请求头中的Content-Type字段从image/jpeg改为text/php或application/x-php。同时将文件名改为shell.jpg。原理有些后端只检查Content-Type头而不检查实际文件扩展名。如果它只允许image/jpeg,image/png等类型我们改成text/php就会失败。反之如果它有一个Content-Type白名单只允许图片类型我们就要把Content-Type保持为image/jpeg而把文件名改成shell.php试试。本题关键发现经过一系列测试我发现在本题中同时满足以下两个条件文件才能上传成功文件的扩展名必须在某个“安全”列表里比如.jpg,.png,.gif。请求头中的Content-Type也必须是对应的图片类型如image/jpeg,image/png。这意味着如果我们上传一个内容为PHP代码的文件但它的名字是shell.jpg且Content-Type是image/jpeg服务器可能会“被骗过”允许它上传。但这只是第一步上传一个.jpg文件服务器默认不会把它当作PHP脚本来执行。3.2 第二层文件内容检测与“图片马”服务器可能会检查文件内容的开头部分文件头/魔术字节以确保它真的是一个图片。例如JPEG文件的文件头是FF D8 FF E0PNG的文件头是89 50 4E 47。绕过方法制作图片木马我们不能直接上传纯文本的PHP代码需要将PHP代码嵌入到一个真实的图片文件中。使用copy命令Windowscopy normal.jpg /b shell.php /b webshell.jpg这会将shell.php的二进制内容追加到normal.jpg的末尾生成webshell.jpg。图片查看器依然能正常显示图片前半部分但服务器在解析时如果存在漏洞可能会执行末尾的PHP代码。使用文本编辑器用十六进制编辑器如010 Editor或Notepad打开一个图片文件滚动到最末尾另起一行插入我们的PHP代码。使用GD库等生成写一个PHP脚本用imagecreate()生成一个最小的1x1像素的图片然后将代码写入图片元数据如EXIF信息。本题中更常用的方法是直接追加。上传测试制作好webshell.jpg后用Burp Suite确保文件名和Content-Type(image/jpeg) 都符合要求然后上传。结果服务器返回成功并给出了路径例如uploads/xxxxxx.jpg。这说明我们绕过了扩展名和内容类型检查。3.3 第三层解析漏洞与路径穿越现在我们有了一个包含PHP代码的jpg文件在服务器上。但直接访问http://target/uploads/xxxxxx.jpg服务器只会把它当作静态图片来响应不会执行其中的PHP代码。这就是最后的也是最关键的一环如何让服务器将这个.jpg文件以PHP的方式解析常见的思路有服务器解析漏洞一些老旧版本的服务器如Apache的特定版本、IIS 6.0等存在解析漏洞例如filename.php.jpg会被解析为php执行。在本题环境中测试通常无效。.htaccess文件上传针对Apache如果能上传一个.htaccess文件内容为AddType application/x-httpd-php .jpg就可以让该目录下所有.jpg文件都被当作PHP解析。但本题通常会对.htaccess文件的上传进行严格限制。利用文件包含漏洞这是本题以及很多现代CTF上传题的核心考点。如果网站其他地方存在文件包含漏洞Local File Inclusion, LFI我们就可以通过包含这个上传的图片马来执行其中的PHP代码。信息收集延伸我们需要检查题目是否有其他功能点。例如是否存在一个“查看图片”的功能其URL参数可能是?filexxxxxx.jpg。如果这个参数没有经过严格过滤就可能造成文件包含。本题的突破点经过对题目其他页面的搜索或查看源码提示有时题目描述或注释会给出线索发现本题很可能存在文件包含点。假设我们找到了一个URLhttp://target/index.php?pageupload/xxxxxx.jpg。那么访问这个URL时服务器会尝试将upload/xxxxxx.jpg作为PHP文件包含进来。由于我们上传的jpg文件末尾包含PHP代码这些代码就会被服务器执行4. 完整攻击链实操与Webshell利用让我们串联起整个攻击流程。4.1 步骤一制作图片木马首先准备一个正常的jpg图片大小尽量小比如1x1像素的图片方便操作。然后创建一个shell.php文件内容为?php eval($_POST[‘ant’]); ?这里使用符号抑制错误eval()函数执行POST参数ant传来的代码。ant是连接密码可以自定义。使用命令行合成# 在Linux或Mac下 cat normal.jpg shell.php shell.jpg # 或者在图片末尾直接追加 echo ‘?php eval($_POST[“ant”]);?’ shell.jpg现在shell.jpg就是一个既能被识别为图片又包含PHP后门的文件。4.2 步骤二配置代理与拦截上传打开Burp Suite确保代理监听正确浏览器代理设置好。在题目上传页面选择我们制作好的shell.jpg。点击上传前打开Burp Suite的拦截Intercept on。点击上传请求会被Burp Suite捕获。4.3 步骤三修改请求以通过校验查看Burp Suite拦截到的原始请求Raw格式它大概长这样POST /upload.php HTTP/1.1 Host: target ... Content-Type: multipart/form-data; boundary----WebKitFormBoundary... ------WebKitFormBoundary... Content-Disposition: form-data; name“file”; filename“shell.jpg” Content-Type: image/jpeg ... (这里是文件的二进制数据开头是FF D8 FF E0等JPEG头) ...我们需要确保filename“shell.jpg”保持不变已经是.jpg。Content-Type: image/jpeg保持不变。文件体的二进制内容就是我们制作好的图片马。通常这里不需要修改。直接点击“Forward”放行请求。4.4 步骤四获取上传路径与寻找包含点服务器返回成功信息例如File uploaded successfully: uploads/95b7b0b9d2091bff1c2f8d4e2f4a1a3c.jpg记下这个路径uploads/95b7b0b9d2091bff1c2f8d4e2f4a1a3c.jpg。接下来需要在题目网站寻找文件包含的入口。这可能通过查看页面源码在HTML注释中寻找线索。参数爆破使用Burp Suite的Intruder模块对可能的参数如file,page,load,path等进行模糊测试。基于题目名的猜测有时包含点就在上传页面本身或者有一个index.php?actionviewimagexxx的页面。假设我们通过扫描或猜测找到了包含点http://target/index.php?file4.5 步骤五利用文件包含执行代码构造包含我们图片马的URLhttp://target/index.php?fileuploads/95b7b0b9d2091bff1c2f8d4e2f4a1a3c.jpg访问这个URL。如果文件包含漏洞存在且未被过滤服务器就会尝试解析jpg文件中的PHP代码。如何验证执行成功我们上传的是一句话木马它本身不会直接输出内容需要我们用工具连接。使用中国菜刀、蚁剑(AntSword)或C刀(Cknife)等Webshell管理工具。在工具中添加一个新的Shell。URL地址填写包含图片马的完整URLhttp://target/index.php?fileuploads/95b7b0b9d2091bff1c2f8d4e2f4a1a3c.jpg连接密码填写我们写在PHP代码中的ant。连接类型通常选择PHP。点击连接。如果成功工具会列出服务器的目录结构。4.6 步骤六定位并获取Flag连接成功后我们就获得了服务器的一个Webshell权限。接下来就是寻找Flag文件。Flag文件通常命名为flag,flag.txt,flag.php,.flag或者存放在根目录/、网站根目录下。使用文件管理功能依次查看常见目录/,/var/www/html,/tmp等。使用命令执行功能如果支持输入find / -name “*flag*” 2/dev/null来搜索包含flag关键词的文件。找到Flag文件后直接查看或下载其内容提交即可完成挑战。5. 核心原理与防御思考通过这道“BabyUpload”我们可以清晰地看到文件上传漏洞的经典三层防御与绕过前端校验仅用于改善用户体验安全性为0。绕过方法禁用JS或代理拦截修改请求。后端扩展名与类型检查黑名单总有遗漏如.php5,.phtml,.htaccess。绕过方法尝试冷门扩展名、大小写、点空格、双写(.pphphp)等。白名单更安全只允许.jpg, .png, .gif等。绕过方法结合其他漏洞如本题的文件包含。MIME类型检查检查Content-Type头。绕过方法Burp Suite修改为允许的类型。文件头检查检查文件幻数。绕过方法制作图片马保留正确的文件头。执行权限控制这是最终防线。即使恶意文件上传成功也要防止它被解析执行。存储目录无执行权限将上传目录配置为仅能存储静态文件。重命名使用随机哈希值重命名文件避免用户猜测路径。禁用危险解析在服务器配置中禁止将图片目录下的文件当作PHP解析。修复文件包含漏洞这是本案的关键。严格过滤文件包含函数的参数限制可包含的路径范围。从防御者角度一个健壮的上传功能应该使用白名单验证文件扩展名和MIME类型。对上传文件进行重命名如使用md5(时间戳文件名).jpg并隐藏真实存储路径。将上传文件存储在Web根目录之外通过后端脚本读取并输出而不是直接提供URL访问。对图片文件进行二次渲染使用GD库或ImageMagick重新生成图片彻底破坏嵌入的恶意代码。定期进行安全审计杜绝文件包含、命令注入等次级漏洞。6. 常见问题与排查技巧实录在实际操作和教学过程中我遇到了不少反复出现的问题这里记录一下问题1上传的图片马访问时直接下载不执行PHP代码。排查这说明服务器没有把.jpg当作PHP解析。可能的原因1) 不存在文件包含漏洞你包含的URL不对2) 服务器配置严格禁止了其他后缀的PHP解析3) 图片马制作有问题PHP代码没有被正确包含在请求中。解决首先确认文件包含点是否正确。可以用一个包含?php phpinfo();?的图片马测试如果包含成功会显示PHP信息页面而不是下载或显示乱码。其次检查Burp Suite中上传请求的文件体部分确保PHP代码确实在文件末尾。问题2使用蚁剑/菜刀连接失败返回200但内容为空或报错。排查连接密码错误URL填写错误不是图片马直接地址而是包含该图片马的URL目标服务器禁用了eval等危险函数一句话木马代码被服务器端的WAF或内容过滤器删除了。解决尝试使用更隐蔽的Webshell代码如使用assert()函数代替eval()或使用字符串拼接、编码等方式绕过内容过滤。例如?php $a$_POST[‘ant’]; assert($a); ?。同时仔细核对URL和密码。问题3找不到文件包含的入口点。排查题目可能没有明显的包含点需要结合其他漏洞。例如上传.htaccess文件如果允许或者利用条件竞争在上传瞬间文件被重命名前访问它。解决进行目录扫描寻找其他PHP文件。查看首页、上传页、查看页的源代码寻找注释线索。如果题目是Apache服务器可以尝试上传.htaccess文件内容为Sethandler application/x-httpd-php这样所有文件都会被当作PHP解析但通常会被拦截。问题4服务器对文件内容进行了严格的过滤删除了?php ?标签。绕过技巧使用短标签? eval($_POST[‘a’]); ?需要服务器开启short_open_tag。使用script language“php”eval($_POST[‘a’]);/script仅在某些旧版本PHP中有效。使用图片EXIF信息注入将代码写入图片的EXIF注释字段然后用?php exif_read_data(‘shell.jpg’, ‘COMMENT’);?来包含执行需要服务器环境支持且包含点能触发代码执行。本题更高级的绕过有时过滤的是php这个字符串。可以尝试用.连接符?php $a‘ph’;$b‘p’; $func$a.$b; $func(‘代码’);?或者使用编码、加密等方式。这道“BabyUpload”就像一把钥匙打开的是文件上传漏洞这个庞大知识体系的大门。它告诉我们安全是一个链条任何一个环节的疏忽都可能导致全线崩溃。作为开发者需要建立纵深防御作为安全研究者则需要培养这种层层递进的测试思维。下次当你再看到上传按钮时脑海里浮现的应该就是这一整套的攻防画卷了。