CTF实战:利用.phtml绕过文件上传黑名单漏洞

发布时间:2026/7/31 10:12:15

CTF实战:利用.phtml绕过文件上传黑名单漏洞 1. 项目概述从一道CTF题看文件上传漏洞的本质最近在带新人打CTF靶场发现很多朋友对文件上传漏洞的理解还停留在“传个一句话木马”的层面遇到稍微有点防护的题目就无从下手。正好BUUCTF平台上的ACTF2020新生赛Upload1这道题就是一个非常经典且教学意义十足的文件上传漏洞实战案例。它不像那些“裸奔”的靶场而是设置了一道简单的前端验证逼着你去思考“绕过”这件事。这道题的核心通关点就在于利用.phtml后缀来绕过常见的黑名单过滤机制最终拿到服务器权限。如果你正在学习Web安全尤其是对文件上传漏洞的绕过技巧感兴趣那么通过这道题的详细拆解你不仅能学会如何通关更能深刻理解黑名单过滤的局限性、.phtml文件在特定环境下的执行原理以及一套完整的“发现-分析-绕过-利用”实战思路。这远比死记硬背几个Payload有用得多。接下来我就以这道题为蓝本带你完整走一遍渗透测试流程并深入聊聊背后的那些“门道”。2. 题目环境与初步信息收集2.1 靶场启动与界面初探首先我们需要在BUUCTF平台上找到并启动[ACTF2020新生赛]Upload1这道题目。启动后你会获得一个临时的靶机地址通常格式为http://xxx.node4.buuoj.cn:port/。用浏览器访问这个地址映入眼帘的是一个非常简洁的文件上传界面。这个界面通常只包含一个文件选择框、一个上传按钮以及可能的一些提示文字比如“上传图片”。从用户体验上看它和一个普通的头像上传功能没什么区别。但作为安全测试者我们的第一反应不应该是直接上传文件而是查看前端代码。按下F12打开开发者工具查看网页的HTML源码和JavaScript代码。为什么这一步至关重要很多入门级的文件上传漏洞防护仅仅依赖于前端JavaScript进行文件后缀名验证。这种验证对用户是透明的但对我们来说绕过它可能就像禁用浏览器JS一样简单。通过查看前端代码我们可以快速判断验证逻辑是放在前端客户端还是后端服务器端这直接决定了我们后续的绕过策略。2.2 前端代码分析与绕过尝试在本题的HTML源码中我们很快就能发现端倪。通常代码中会包含一段类似下面的JavaScript验证函数function checkFile() { var file document.getElementById(file).value; if (file null || file ) { alert(请选择要上传的文件!); return false; } // 定义允许的后缀名 var allow_ext .jpg|.png|.gif|.jpeg; // 提取文件后缀 var ext_name file.substring(file.lastIndexOf(.)); if (allow_ext.indexOf(ext_name) -1) { alert(仅允许上传图片文件); return false; } }这段代码的逻辑非常清晰它定义了一个白名单allow_ext包含.jpg, .png, .gif, .jpeg然后获取用户选择文件的后缀名进行比对。如果不在白名单内就弹出警告并阻止表单提交。绕过方法一直接禁用JavaScript这是最粗暴有效的方法。在浏览器设置中禁用JavaScript或者使用Burp Suite这类代理工具拦截请求并直接修改前端验证就形同虚设了。你可以直接上传一个.php文件然后通过Burp将请求转发出去。绕过方法二修改前端代码在开发者工具的“元素”Elements选项卡中直接找到这个检查函数并将其删除或者修改allow_ext变量加上.php然后正常上传。注意在实际渗透测试中前端验证绕过通常只是第一步甚至可能是一个“烟雾弹”。出题人故意留下这么明显的前端漏洞往往意味着后端还有更严格的检查在等着你。所以即使前端绕过成功也不要高兴得太早。2.3 使用Burp Suite进行请求拦截与重放为了更专业地进行测试我们通常会使用Burp Suite。配置好浏览器代理后我们尝试上传一个名为shell.php的文件内容为一句话木马?php eval($_POST[cmd]);?。拦截请求点击上传后Burp Suite的Proxy模块会拦截到这个HTTP POST请求。观察请求你会看到请求体中包含了文件内容Content-Disposition头部中指明了文件名filenameshell.php。尝试直接放行如果我们直接Forward这个被前端JS拦截前就被Burp捕获的原始请求此时还未经过JS验证会发生什么很多时候后端会返回一个错误比如“文件类型不允许”。这证明了后端也存在验证。重放与测试我们将这个请求发送到Burp的Repeater模块。在Repeater中我们可以随意修改filename参数比如改为shell.jpg然后发送请求观察响应。通过对比修改前后端的不同响应我们可以一步步摸清后端过滤器的规则。在本题目中当你将.php改为.jpg发送后可能会发现上传成功了但访问上传后的文件却显示的是图片的二进制乱码或者直接提示“图片无法显示”。这说明服务器虽然允许.jpg文件上传但并没有把它当成PHP脚本来执行。我们的任务就是找到一个既能通过后端验证又能被服务器当作PHP代码来执行的文件名。3. 核心漏洞原理黑名单过滤与.phtml的妙用3.1 什么是黑名单与白名单文件上传的防护逻辑主要分为两种黑名单Blacklist和白名单Whitelist。黑名单明确禁止某些危险的后缀名上传比如.php,.asp,.jsp,.exe等。它的思想是“除了已知危险的其他都允许”。优点配置相对简单。缺点防不住未知的危险后缀或者名单遗漏。.phtml绕过就是利用了这个缺点。白名单只允许某些安全的、预期的后缀名上传比如.jpg,.png,.pdf等。它的思想是“只允许已知安全的其他都禁止”。优点安全性远高于黑名单。只要名单收得紧几乎无懈可击。缺点业务逻辑可能更复杂需要严格校验文件内容。ACTF2020新生赛Upload1这道题从后端的行为来看采用的是一种不完善的黑名单机制。它可能禁止了.php但遗漏了其他一些也能执行PHP代码的后缀。3.2 为什么.phtml可以执行PHP代码这才是本题的技术核心。.phtml后缀并不常见但它确实在某些特定的Web服务器配置下可以被解析为PHP脚本。历史渊源在早期.phtml有时是.php3,.php4,.php5是PHP文件的一种可选扩展名。有些管理员或老旧系统为了区分纯PHP文件和混合了大量HTML的PHP文件会使用.phtml。服务器配置Apache服务器通过AddType指令或FilesMatch在配置文件中关联特定后缀与PHP解析引擎。一个典型的配置可能如下AddType application/x-httpd-php .php .phtml .php3这行配置告诉Apache凡是遇到.php,.phtml,.php3结尾的文件都交给application/x-httpd-php即PHP解析模块来处理而不是直接将其文本内容返回给浏览器。本题环境出题人显然搭建了一个特意配置了解析.phtml的ApachePHP环境。当黑名单只过滤了.php时上传.phtml文件就能同时满足两个条件绕过黑名单检查被服务器当作PHP执行。3.3 其他可能被遗漏的后缀除了.phtml不完善的黑名单还可能遗漏很多“变种”后缀这取决于服务器配置。了解这些有助于你在其他题目或真实场景中拓宽思路大小写绕过Php,PHP,pHp在Windows服务器上文件名通常不区分大小写。双写绕过p.phphp如果过滤逻辑是简单删除字符串”php”删除后剩下p.hp可能结合其他漏洞。点号、空格绕过shell.php.或shell.php在某些系统处理文件名时末尾的点或空格会被自动去除。特殊解析绕过.php5,.php4,.php3,.pht这些是PHP历史版本或变种后缀。嵌套后缀shell.jpg.php如果只检查最后一次点号后的后缀可能被绕过但现代防护通常会检查整个文件名。利用服务器解析特性shell.php.jpg配合Apache的AddHandler错误配置可能导致文件被解析为PHP。这在本题不适用但属于重要知识点。实操心得在测试时不要只盯着一个.phtml。我会习惯用一个Burp Intruder入侵者模块加载一个包含几十种常见绕过后缀的字典如php, php3, php4, php5, phtml, pht, jsp, asp, aspx...以及它们的大小写变体对filename参数进行模糊测试快速探测目标系统的黑名单范围。4. 完整通关步骤详解掌握了原理我们来一步步完成这道题。4.1 准备攻击载荷我们需要的不是一个简单的.phtml文件而是一个包含能够执行系统命令或读取文件代码的Webshell。最简单的PHP Webshell?php eval($_POST[cmd]);?这是一句话木马通过POST传递cmd参数执行任意PHP代码。但它过于敏感很多WAF或简单的关键词过滤会拦截eval、$_POST等函数。更隐蔽的测试载荷对于CTF题目我们通常需要读取服务器上的特定文件比如flag。因此可以使用更直接的文件操作函数?php echo system(cat /flag); ?或者为了更通用地测试命令执行?php if(isset($_GET[c])){ system($_GET[c]);} ?这样我们上传后访问shell.phtml?cls就能列出目录。本题专用载荷由于题目目标是获取flag我们可以直接将读取flag的命令写入GIF89a ?php echo file_get_contents(/flag); ?这里开头的GIF89a是GIF图片的文件头。这是一种简单的文件内容欺骗技巧。如果后端不仅检查后缀还通过exif_imagetype()或getimagesize()函数检查文件内容是否为图片添加这个文件头有可能绕过内容检查。虽然本题可能不需要但这是一个好习惯。我们将上述代码保存为一个文本文件然后将其重命名为shell.phtml。4.2 实施上传绕过打开Burp Suite配置代理浏览器设置好代理。访问靶场地址在文件上传界面选择我们制作好的shell.phtml文件。点击上传此时Burp会拦截到请求。在Burp Proxy中观察请求。关键的部位是请求体Body部分你会看到------WebKitFormBoundaryxxx Content-Disposition: form-data; namefile; filenameshell.phtml Content-Type: text/html GIF89a ?php echo file_get_contents(/flag); ?直接Forward这个请求。因为前端JS可能只检查了.php对.phtml放行。如果前端JS白名单里没有.phtml我们可能需要先按2.2节的方法禁用或绕过JS。观察服务器响应。如果上传成功页面通常会返回上传文件的存储路径比如文件上传成功路径uploads/shell.phtml务必记录下这个完整路径它是我们访问Webshell的URL。4.3 访问Webshell并获取Flag假设上传成功后的路径是http://xxx.node4.buuoj.cn:port/uploads/shell.phtml。直接在浏览器中访问这个URL。如果我们的代码是?php echo file_get_contents(/flag); ?那么页面应该会直接显示出flag的内容格式可能为actf{xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}。如果使用的是带参数的一句话木马则需要使用中国蚁剑、冰蝎这类Webshell管理工具连接或者手动构造POST请求。对于本题直接输出flag的代码是最快捷的。常见问题排查访问后显示源代码或下载文件这说明服务器没有将.phtml配置为PHP解析。请检查你是否正确使用了.phtml后缀或者尝试其他备用后缀如.php5。页面空白或报错检查你的PHP代码语法是否正确。在本地PHP环境中测试无误后再上传。上传路径找不到仔细查看上传成功后的提示信息路径可能有哈希或时间戳前缀。使用Burp Repeater多次测试并观察响应。4.4 利用中国蚁剑进行可视化连接备选方案如果题目需要进一步的目录浏览或交互使用工具更高效。假设我们上传的是一句话木马shell.phtml内容为?php eval($_POST[pass]);?。打开中国蚁剑右键点击“添加数据”。URL地址栏填写完整的Webshell地址http://xxx.node4.buuoj.cn:port/uploads/shell.phtml。连接密码填写pass与POST参数名一致。编码器选择default默认。点击“添加”。如果一切正常左侧会列出服务器目录结构。通常flag文件位于根目录/、/var/www/html或当前目录下文件名可能是flag、flag.txt、flag.php等。直接双击打开即可查看。5. 深度防御绕过技巧与实战扩展通关一道题是基础更重要的是举一反三。文件上传漏洞的防御和绕过是“矛与盾”的持续对抗。5.1 进阶绕过技巧组合拳现实中防御措施往往是多层的。我们需要思考如何组合使用技巧。后缀名绕过 内容欺骗场景后端同时检查后缀名黑名单和文件内容要求是图片。方法制作一个图片马。用copy命令Windows或cat命令Linux将一个真实图片和一个PHP Webshell合并。Windows:copy normal.jpg /b shell.php /a webshell.jpgLinux:cat normal.jpg shell.php webshell.jpg上传这个webshell.jpg。然后利用文件包含漏洞或解析漏洞如Apache的test.jpg.php错误解析让图片中的PHP代码得以执行。这在没有.phtml这样的解析后缀时是常用思路。修改HTTP请求头场景后端通过Content-Type字段验证文件类型。方法Burp拦截上传请求将Content-Type: application/octet-stream修改为Content-Type: image/jpeg。利用WAF/过滤器的逻辑缺陷场景过滤函数递归删除或替换“php”字符串。方法双写绕过。上传文件名为shell.pphphp如果过滤器将php替换为空则第一次替换后变为shell.ph可能无法识别或者设计为递归删除直到没有php为止最终变为shell.。这需要根据响应判断过滤逻辑。5.2 防御视角如何构建更安全的上传功能作为开发者从这道题里应该学到如何避免此类漏洞强制使用白名单这是最重要的原则。只允许.jpg,.png,.gif等业务必需的后缀。名单要短且明确。文件内容校验不仅检查后缀还要用可靠的服务端库如GD库、ImageMagick验证文件内容确实是所声称的格式。防止图片马。重命名文件上传后使用随机算法如时间戳MD5生成新的文件名并彻底丢弃用户上传时的原始文件名。这可以防止所有基于文件名的绕过。控制文件权限上传目录设置为不可执行脚本。在Apache中可以通过.htaccess文件设置php_flag engine off。在Nginx中确保上传目录的location块不传递给PHP-FPM。文件存储位置尽量不要让用户能直接通过Web URL访问上传的文件。如果必须访问可以通过一个单独的脚本如download.php?idxxx来读取和输出文件并在脚本中做严格的权限和类型检查。使用云存储或独立服务将文件上传到OSS、AWS S3等对象存储服务这些服务通常有更完善的安全机制。5.3 在BUUCTF及其他靶场中的实战联想做完这道题你可以顺藤摸瓜在BUUCTF上挑战其他文件上传题目巩固技能[极客大挑战 2019]Upload可能需要结合前端绕过、内容欺骗和.user.ini文件利用。[SUCTF 2019]CheckIn经典的.user.ini与图片马组合利用。Upload-Labs这是一个专门的文件上传漏洞靶场项目包含了从简单到困难的20关涵盖了前端验证、MIME类型验证、黑名单、白名单、条件竞争、图片马、解析漏洞等几乎所有常见考点是系统学习的最佳选择。我个人在实际操作中的体会是文件上传漏洞的利用过程就像一场“猜谜游戏”。你需要根据服务器的每一次响应错误信息、成功信息、返回路径来不断调整你的Payload。保持耐心系统性地测试每一种可能后缀、内容、头部并深刻理解每一层防御措施背后的原理才能真正做到“以不变应万变”。这道Upload1题目就是一个完美的起点它用最简单的设置引出了黑名单过滤这个最核心的弱点。记住.phtml更要记住这种“发现过滤规则-寻找规则遗漏-构造有效载荷”的通用渗透测试思维。

相关新闻