🛠️ 环境前置准备(避坑必看)
在开始打靶前,请务必确认你的 phpStudy (小皮面板) 或 XAMPP 满足以下配置,否则部分关卡将无法复现:
| 配置项 | 具体要求 |
|---|---|
| Apache 解析权限 | 打开 httpd.conf,将所有的 AllowOverride None 改为 AllowOverride All |
| 等价后缀解析 | 在 httpd.conf 末尾添加:AddType application/x-httpd-php .php .phtml .php3 .php5 |
| PHP 版本 | Pass 01-11:建议 PHP 7.x;Pass 12-13:必须切换到 PHP 5.2.17,并在 php.ini 中关闭魔术引号 magic_quotes_gpc = Off |
| 工具准备 | Burp Suite、蚁剑 (AntSword) 或哥斯拉 (Godzilla) |
💡 提示:练习 Pass-14 以后的关卡时,如果蚁剑连不上,检查 php.ini 里的 short_open_tag。如果为 Off,图片马使用 <? 简写将无法解析,建议统一使用 <?php 标准写法。
第一类:前端验证与 MIME 绕过 (Pass-01, Pass-02)
核心逻辑:服务器将验证逻辑放在客户端或信任了可伪造的 HTTP 头部。
Pass-01:前端 JS 验证
-
原理:仅在浏览器端使用 JavaScript 检查后缀,非 .jpg/.png/.gif 则弹窗阻止。
-
绕过手法:
- 直接禁用:浏览器 F12 → 设置 → 禁用 JavaScript。
- 抓包改名(推荐):先将木马改名为 shell.jpg,上传时用 Burp 抓包,在报文中将文件名改回 shell.php。
Pass-02:MIME 类型绕过
- 原理:后端检查 $_FILES['upload_file']['type'],即 HTTP 请求头中的 Content-Type 字段。
- 绕过手法:抓包后,将 Content-Type: application/octet-stream 修改为 image/png 或 image/jpeg 即可欺骗后端。
🛡️ 防御方案:绝对不能依赖前端 JS 和 HTTP 头部的 MIME 类型进行安全验证,必须在后端对真实后缀和内容进行校验。
第二类:黑名单过滤不严与特性绕过 (Pass-03 ~ Pass-11)
核心逻辑:服务器定义了"不允许上传"的后缀列表(黑名单)。只要找到不在列表里、但又能被服务器当作脚本执行的后缀,就能绕过。
Pass-03:特殊解析后缀(等价扩展名)
- 原理:黑名单漏掉了 .php3, .phtml, .php5 等。在配置了 AddType 的 Apache 环境下,这些后缀会被当作 PHP 解析。
- 绕过手法:将文件改名为 shell.php3 上传。
- 注意:若访问显示源码,请检查 Apache httpd.conf 是否添加了对应解析规则。
Pass-04:.htaccess 绕过
-
原理:黑名单屏蔽了几乎所有脚本后缀,但漏掉了 .htaccess(Apache 分布式配置文件)。
-
绕过手法:
- 上传一个名为 .htaccess 的文件,内容为:SetHandler application/x-httpd-php(让当前目录下所有文件都按 PHP 解析)。
- 再上传一个 shell.jpg(内含木马代码),访问该图片即可执行。
Pass-05:.user.ini 绕过
-
原理:利用 PHP 自有的配置文件特性(类似 .htaccess)。
-
绕过手法:
- 上传 .user.ini,内容为:auto_prepend_file=shell.jpg。
- 上传 shell.jpg。此时访问同目录下的任意正常 PHP 文件(如 readme.php),会自动包含并执行 shell.jpg。
Pass-06:大小写绕过
- 原理:Windows 系统对文件名大小写不敏感,但后端 PHP 黑名单检查往往区分大小写。
- 绕过手法:上传 .PhP 或 .pHp。
Pass-07:空格绕过
- 原理:Windows 在保存文件时,会自动去除文件名末尾的空格。
- 绕过手法:抓包,将文件名改为 shell.php (注意末尾有空格)。绕过了 === '.php' 的验证,保存到系统时变成了 shell.php。
Pass-08:点号绕过
- 原理:与空格同理,Windows 系统会自动去除文件名末尾的点 .。
- 绕过手法:上传 shell.php.。
Pass-09:::$DATA 绕过
- 原理:利用 Windows NTFS 文件系统的备用数据流特性。
- 绕过手法:上传 shell.php::$DATA,Windows 会把它当成 shell.php 的数据流保存,从而绕过黑名单且不影响执行。
Pass-10:组合拳(点空格点)
- 原理:后端代码使用 str_replace 等函数过滤危险后缀,但对末尾的空格和点处理不彻底。
- 绕过手法:构造 shell.php. .(点 + 空格 + 点),利用 Windows 特性最终保存为 shell.php。
Pass-11:双写替换绕过
- 原理:后端将黑名单后缀替换为空,但只替换一次。
- 绕过手法:将文件名改为 shell.pphphp,中间的 php 被替换为空后,前后拼接成 shell.php。
🛡️ 防御方案:
- 废弃黑名单,使用白名单:只允许 .jpg, .png, .gif 等明确安全的后缀。
- 严格的文件名清洗:判断后缀前,统一转小写,并使用 trim() 彻底去除末尾空格、点号和特殊字符。
第三类:白名单与路径截断 (Pass-12, Pass-13)
核心逻辑:开发者使用了白名单(只允许图片后缀),但拼接保存路径时出现了漏洞。
底层语言(如 C)在处理字符串时,遇到 \0(空字符/0x00)会认为字符串结束。若 PHP 版本 < 5.3.4 且 magic_quotes_gpc = Off,就会产生截断漏洞。
Pass-12:GET 型 %00 截断
- 原理:保存路径由 GET 参数控制。%00 后的内容被系统底层截断。
- 绕过手法:将上传路径修改为 save_path=../upload/shell.php%00,上传的文件名为 1.jpg。最终拼接结果是 ../upload/shell.php%00/1.jpg,系统保存时由于 %00 截断,文件被保存为 shell.php。
Pass-13:POST 型 0x00 截断
- 原理:保存路径由 POST 参数控制。POST 数据不会像 URL 那样自动解码 %00。
- 绕过手法:在 save_path 的 shell.php 后加一个空格作为占位,在 Burp 的 Hex 视图中找到那个空格的十六进制 20,手动修改为 00。
🛡️ 防御方案:升级 PHP 至 5.3.4 以上;不要将用户可控的变量直接用于构建文件保存路径。
第四类:文件内容检测与图片马 (Pass-14 ~ Pass-17)
核心逻辑:服务器不仅检查后缀,还开始检查文件内部的数据。
Pass-14 ~ Pass-16:图片马 (Magic Bytes)
-
原理:
- 14-15 关:读取文件前两个字节(魔术头),如 JPEG 是 FF D8,GIF 是 GIF89a;或使用 getimagesize() / exif_imagetype() 验证是否为真实图像。
- 16 关(二次渲染):服务器使用 imagecreatefromjpeg() 等函数,把上传的图片重新在内存里画一遍再生成新图,这会破坏附加在图片末尾的恶意代码。
-
绕过手法:
-
14-15 关(制作图片马):在 CMD 中使用命令:
copy normal.jpg /b + shell.php /a webshell.jpg将 PHP 代码追加到正常图片尾部。配合靶场自带的文件包含漏洞(include.php)即可执行。
-
16 关(二次渲染绕过):GIF 最容易绕过。先上传一个正常 GIF,下载经过服务器渲染后的 GIF,使用十六进制工具(如 010 Editor)对比两份文件,找到未发生变化的数据块,将一小段 PHP 代码(如 <?php phpinfo(); ?>)插入该区段。
-
🛡️ 防御方案:二次渲染是非常优秀的防御手段,配合单独的文件服务器(如阿里云 OSS、腾讯云 COS)效果最佳。
第五类:条件竞争 (Race Condition) (Pass-18)
核心逻辑:代码逻辑为"先上传,后检测"。服务器先把文件保存到网站目录,然后再检查后缀和内容,不合法则用 unlink() 删除。
Pass-18:条件竞争
-
原理:利用从"保存"到"删除"之间极短的时间差。
-
绕过手法:
- 使用 Burp Suite 的 Intruder 模块并发发送大量上传请求(上传 wshell.php,内容是一个会生成另一个木马的脚本)。
- 同时用另一个工具或线程疯狂访问该文件。
- 只要访问请求在删除前"抢占"成功,wshell.php 就会被执行,通常利用 file_put_contents 在当前目录生成一个不会被删除的后门文件(如 shell_b.php)。
🛡️ 防御方案:先检测,后保存。文件应先存放在系统临时目录,所有安全检测通过后再使用 move_uploaded_file() 移动到 Web 目录。
第六类:CVE 及框架/逻辑缺陷 (Pass-19 ~ Pass-21)
Pass-19:Apache 解析漏洞 + CVE-2015-2348
- Apache 解析漏洞:Apache 会从右向左解析后缀,直到认识为止。上传 shell.php.7z,Apache 不认识 .7z,会将其解析为 .php。
- CVE-2015-2348:move_uploaded_file() 函数内部的 \x00 截断,原理类似于 12 和 13 关。
Pass-20:自定义文件名过滤疏忽
- 原理:用户可以输入自定义文件名。代码将文件名以点 . 分割为数组进行黑名单检测,但对特定构造处理不当。
- 绕过手法:构造 shell.php/. 或利用空格/点组合绕过验证。
Pass-21:数组指针逻辑漏洞(代码审计)
-
原理:开发者使用 explode() 拆分文件名,并用 end() 取最后一个元素作后缀检查,但对数组形式的输入处理不当。
-
绕过手法:抓包修改 save_name 为数组形式:
- save_name[0] = shell.php/
- save_name[2] = jpg
- 最终拼接结果会绕过白名单检测并保存为 shell.php。
🛡️ 终极防御指南(The Golden Rules)
在真实业务开发中,防止上传漏洞不需要写几百行的过滤正则,只要遵循以下三大黄金法则,就能从根本上解决 99% 的问题:
| 法则 | 具体措施 |
|---|---|
| 1. 强制文件重命名(最重要) | 坚决不使用用户上传的原文件名。后端使用随机哈希(如 md5(uniqid()) + 时间戳)强制重命名。shell.php 会变成 8a2b...91f.jpg,即使上传成功也无法预测文件名。 |
| 2. 严格的白名单检测 | 利用 PHP 的 finfo_file() 结合文件扩展名白名单(只允许 .jpg, .png, .pdf 等),拒绝任何未知类型。 |
| 3. 目录权限分离(降维打击) | 存放上传文件的目录(如 /uploads/)绝对不能有脚本执行权限。在 Nginx/Apache 中限制该目录无法解析 .php。更好的做法是使用第三方对象存储(OSS/S3),彻底将文件存储与 Web 应用服务器物理隔离。 |
📋 快速参考:验证环节 vs 攻击手段 vs 防御方案
| 验证环节 | 攻击手段 | 最强防御方案 |
|---|---|---|
| 前端验证 | 禁用 JS、Burp 抓包 | 后端验证是唯一准则 |
| 后缀验证 | 特殊后缀、大小写、00 截断 | 白名单限制后缀 + 统一转小写 |
| 文件名验证 | 空格、点、::$DATA | 强制重命名(UUID / 时间戳) |
| MIME 验证 | 伪造 Content-Type | 获取文件流真实内容,不信任请求头 |
| 内容验证 | 图片马、二次渲染 | 彻底二次渲染 + 禁用执行目录权限 |
| 解析环境 | .htaccess、解析漏洞 | 关闭目录执行权限、Web 服务器加固 |