发布于6小时前6小时 背景文件上传类题目在 CTF 和靶场里很常见,但很多新手会卡在“能传上去,但访问不到”或“传了 PHP 却不解析”这类细节上。这里整理一个比较通用的解题流程,重点放在上传点探测、后端校验绕过、存储路径判断和 WebShell 落点验证。内容仅面向本地靶场和授权环境。典型场景:页面提供头像上传或附件上传,只允许图片格式。目标是上传一个可执行脚本,或者利用解析差异拿到代码执行。技术分析文件上传题一般不是单点判断,常见限制会叠加出现:前端校验:通过 JavaScript 判断后缀、MIME、大小。这类校验只能影响浏览器,不可信。后端后缀校验:黑名单或白名单,比如禁止 .php,只允许 .jpg/.png/.gif。Content-Type 校验:根据请求头里的 Content-Type 判断是否图片。文件内容校验:检查文件头,如 GIF89a、\x89PNG。重命名策略:上传后改成时间戳、随机 hash,影响落点定位。解析策略:Web 容器是否会把某些后缀交给 PHP/ASP/JSP 解析。解题时不要一开始就盲传一句话木马,建议先确认三个问题:上传请求的真实字段名是什么?上传后的文件是否可访问?路径如何构造?上传目录是否具备脚本解析能力?操作步骤1. 抓包确认上传请求使用 Burp Suite 或浏览器开发者工具抓包,重点观察以下字段:multipart/form-data 中的文件字段名,例如 file、upload、avatar。filename 参数是否保留原文件名。服务端返回中是否包含保存路径、文件名、URL。是否存在前端接口和后端接口不一致的情况。一个典型上传包如下:POST /upload.php HTTP/1.1 Host: target.local Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX ------WebKitFormBoundaryX Content-Disposition: form-data; name="file"; filename="test.jpg" Content-Type: image/jpeg GIF89a hello ------WebKitFormBoundaryX--如果这个请求能成功,先访问返回的图片地址,确认上传目录。例如返回 /uploads/20250101/test.jpg,说明后续测试可以围绕该目录展开。2. 区分前端校验和后端校验如果页面选择 .php 文件时直接提示格式错误,先看是否是前端 JS 限制。可以直接抓一个正常图片上传包,再在 Burp 里修改文件名和内容。例如把:filename="a.jpg" Content-Type: image/jpeg改成:filename="a.php" Content-Type: image/jpeg如果修改后仍能上传,说明只是前端限制。如果服务端返回“类型不允许”,再继续看后端策略。3. 测试后缀黑名单黑名单常见绕过方向包括大小写、复合后缀、特殊解析后缀等,但是否有效取决于服务器配置。可以按下面顺序低成本测试:a.php a.pHp a.php5 a.phtml a.php.jpg a.jpg.php判断标准不是“上传成功”本身,而是访问后是否执行脚本。例如 PHP 环境可以上传:<?php echo "upload_test_".md5(123); ?>访问后如果页面显示 upload_test_202cb962ac59075b964b07152d234b70,才说明被解析执行。若直接下载或显示源码,则只是存储成功。4. 测试 MIME 和文件头校验如果服务端检查 Content-Type,请求里把它改成图片类型即可测试:Content-Type: image/jpeg如果检查文件头,可以构造图片马。例如 GIF 头相对简单:GIF89a <?php echo "ok_".md5(1); ?>如果后端只通过 getimagesize() 或简单魔数判断,这类文件可能通过。但注意:通过内容校验不代表会执行,仍需要服务器按 PHP 解析该文件。5. 判断上传目录是否解析脚本很多靶场会把上传目录配置为禁止脚本执行,这时即使上传 .php 成功,也可能只会下载或返回 403。可以尝试以下判断:访问 /uploads/a.php 是否返回 PHP 执行结果。访问图片马 /uploads/a.jpg 是否只显示图片内容。观察响应头 Content-Type 和状态码。尝试已知解析后缀,如 .phtml、.php5。如果所有脚本后缀都不执行,思路应转向目录解析漏洞、包含漏洞组合、配置错误或二次处理绕过,而不是继续盲目换木马。代码示例下面是一个用于本地靶场测试上传策略的小脚本。它会批量尝试不同文件名和 MIME,方便快速判断过滤点。使用前确认目标是自己搭建或已授权的环境。import requests url = " field_name = "file" payload = b'GIF89a\n<?php echo "ctf_".md5(233); ?>' candidates = [ ("test.php", "application/octet-stream"), ("test.php", "image/jpeg"), ("test.phtml", "image/jpeg"), ("test.php5", "image/jpeg"), ("test.pHp", "image/jpeg"), ("test.jpg.php", "image/jpeg"), ("test.php.jpg", "image/jpeg"), ("test.gif", "image/gif"), ] for filename, mime in candidates: files = { field_name: (filename, payload, mime) } try: r = requests.post(url, files=files, timeout=8) print("=" * 60) print("filename:", filename) print("mime:", mime) print("status:", r.status_code) print("resp:", r.text[:300]) except requests.RequestException as e: print(filename, "error:", e)如果返回里有文件路径,可以再写一段访问验证。但不同靶场返回格式差异较大,建议先人工确认返回内容,避免脚本误判。常见组合思路1. 上传 + 文件包含如果上传目录不解析 PHP,但站点存在本地文件包含,例如:/index.php?page=uploads/test.jpg那么图片马里的 PHP 代码可能通过包含执行。这类题重点不在上传后直接访问,而在找到包含点。2. 上传 + .htaccess在 Apache 且允许 .htaccess 生效时,可以尝试上传配置文件改变解析规则。示例:AddType application/x-httpd-php .jpg然后再上传 shell.jpg。但现实环境里 AllowOverride 通常关闭,靶场题会明确给出条件或存在可测迹象。3. 双写后缀与截断问题老环境或特殊代码中可能出现截断问题,例如对文件名处理不严谨。现代 PHP 版本里经典空字节截断基本不可用,遇到这类题需要结合题目环境版本判断,不建议机械套用。注意事项不要只看上传成功。上传成功、可访问、可执行是三个不同阶段。优先阅读响应。很多靶场会在 JSON 里直接返回 url、path、msg,这是定位落点的关键。注意随机文件名。如果后端重命名,原文件名绕过后缀可能没有意义,需要看最终保存后缀。区分 Nginx 和 Apache。解析规则不同,Apache 的 .htaccess 思路不要直接套到 Nginx。控制测试 payload。靶场验证建议使用 echo md5() 这类无副作用代码,避免破坏环境。别忽略二次渲染。头像上传常会经过压缩、裁剪,PHP 代码可能被图片库重写清除。总结文件上传题的核心不是“背绕过字典”,而是按链路拆解:先确认请求,再定位过滤点,之后判断存储路径和解析行为。比较稳的节奏是:正常图片上传,确认路径。改包测试前端和后端校验边界。分别测试后缀、MIME、文件头。访问验证是否执行,而不是只看是否上传成功。如果不解析,考虑文件包含、解析配置、二次处理等组合点。按这个流程走,绝大多数靶场里的文件上传题都能快速缩小范围,避免在无效 payload 上浪费时间。 网络请求、日志与边界流量分析示意
5小时前5小时 这个题目里最容易混淆的是“文件上传成功”和“WebShell 可执行”并不是同一个问题。排查时建议把链路拆成四段:上传入口、落盘位置、访问路径、解释器执行。 在靶场里可以先上传一个无害标记文件,而不是直接使用可执行脚本,例如内容只写入固定字符串,然后确认服务端实际保存的位置: find /var/www -type f -mmin -5 -printf '%TY-%Tm-%Td %TH:%TM %p\n' grep -R "upload" /var/log/nginx /var/log/apache2 2>/dev/null 重点记录上传接口返回的原始文件名、服务端重命名后的文件名、响应中的路径,以及文件最终所在目录。接着分别验证: 是否可以通过 HTTP 直接访问该文件; 上传目录是否位于 Web 根目录下; 该目录是否继承了 PHP、JSP 等脚本处理规则; 服务端是否仅按扩展名判断,还是同时校验 MIME、文件头和内容; 是否存在二次处理,例如图片压缩、解压、转码或异步搬运。 例如 Nginx + PHP-FPM 环境中,真正危险的不是“能访问上传文件”,而是上传目录被 PHP location 处理。应确保上传目录只允许静态文件,并且不要把用户可控路径交给脚本处理: location ^~ /uploads/ { try_files $uri =404; location ~ \.(php|phtml|phar)$ { return 403; } } 更稳妥的做法是把上传目录放到 Web 根目录之外,下载时由应用读取文件并设置正确的 Content-Type,文件名使用随机值,扩展名使用服务端白名单映射,而不是直接沿用用户输入。 如果靶场要定位“落点”而不是只验证现象,可以同时看访问日志和进程行为: tail -f /var/log/nginx/access.log /var/log/nginx/error.log ps -ef --forest lsof -p <php-fpm-pid> 2>/dev/null 这样能区分是上传接口本身存在校验绕过,还是上传目录配置错误,或者是后续解析链路导致的执行。复现报告最好把这几个时间点分别写清楚:上传成功、文件落盘、HTTP 可达、解释器执行;否则很容易把“文件可访问”误判成“已经形成 WebShell 落点”。
创建帐户或登录后发表意见