目录
[四、Docker 改变了日志位置,也改变了排查方式](#四、Docker 改变了日志位置,也改变了排查方式)
[五、phpinfo 条件竞争:在临时文件窗口内完成包含](#五、phpinfo 条件竞争:在临时文件窗口内完成包含)
[八、没有 phpinfo 时的 Windows 路线](#八、没有 phpinfo 时的 Windows 路线)
[九、session.upload_progress:在 Docker 中利用已知 Session 文件名](#九、session.upload_progress:在 Docker 中利用已知 Session 文件名)
摘要:本文按授课原始顺序复盘 PHP 文件包含进阶实验,从 CRLF/LF 换行差异、日志与图片包含入手,逐步分析 Docker 日志重定向、phpinfo 条件竞争、Windows 通配符、session.upload_progress 以及 PHP 异常退出留下临时文件等路径。文章保留每次失败、报错、无回显和参数排查,强调成功条件、权限边界、版本限制与授权靶场语境,帮助读者把课堂操作还原为可验证的技术推理。
一、复盘范围与环境边界
这篇文章复盘一节围绕 PHP 文件包含展开的进阶授课。原课程从换行符差异讲起,逐步覆盖普通日志和图片包含、Docker 环境限制、phpinfo 条件竞争、Windows 文件名通配、session.upload_progress 以及异常退出后临时文件残留等路径。所有操作都应限定在课程提供的授权靶场、虚拟机或本地容器中,文中出现的地址、目录和服务仅作为实验环境中的参数。
复盘的重点不在于把某一种载荷包装成固定教程,而在于还原讲解中反复使用的判断方法:先确认目标系统与权限,再确认文件是否真正存在,随后根据报错、无回显或文件状态推断限制条件。每个案例都保留失败尝试,因为这些失败决定了下一步为什么必须改变。
课程还穿插了学习、面试和考试建议。例如,遇到安全测试题时,不能只记住"文件包含可以执行代码",还要能够说明包含路径、运行用户、临时文件生命周期和竞争窗口之间的关系。后文将这些经验放在对应案例之后,而不是抽离成脱离上下文的口号。
二、第一道门槛:换行符让同一条命令产生不同结果
复盘首先处理的是一个看似细小、却会直接决定命令是否执行的问题:提交内容中的换行符。Windows 通常使用 \r\n,Linux 使用 \n。在代理工具或抓包重放工具中,如果请求已经把 \r\n 作为结束符处理,额外保留的 \r 就会被带进 Linux 端的命令文本。

图 001:换行符与命令解析截图,展示跨平台换行符、原始请求字节、代理处理方式以及命令出现半条回显或报错时的对照验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
讲解者先从同学复现实验失败的现象倒推。命令看起来已经提交,但终端返回的内容多出 id 后缀、出现解析错误,或者只显示半条结果。这个现象并不能直接说明文件包含没有生效,因为 \n 仍然可能被 Linux 识别为换行;真正可疑的是残留的 \r。

图 002:换行符与命令解析截图,展示跨平台换行符、原始请求字节、代理处理方式以及命令出现半条回显或报错时的对照验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第一次尝试是把 Windows 风格的完整换行直接送入 Linux。代理工具会先吞掉其中一部分结束符,服务端收到的文本则可能保留一个不可识别的回车。Linux 解析到 \n 时完成换行,却把 \r 当成命令内容的一部分,于是命令本身被执行,随后因为多余字符报错。结果表现为"有执行痕迹但没有预期输出"。

图 003:换行符与命令解析截图,展示跨平台换行符、原始请求字节、代理处理方式以及命令出现半条回显或报错时的对照验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第二次尝试只保留 Linux 需要的 \n。此时代理仍能完成请求分隔,后端收到的命令以标准换行结尾,/bin/bash 后的 id 可以正常执行。由此得到的判断是:问题不在命令主体,而在请求构造阶段多出的 \r。排查时应先检查原始请求的字节,而不是盲目更换 payload。

图 004:换行符与命令解析截图,展示跨平台换行符、原始请求字节、代理处理方式以及命令出现半条回显或报错时的对照验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这一段的经验可以概括为:跨平台调试时,换行符属于协议数据,不是视觉上的空白。 只要请求经过抓包工具、代理或脚本二次处理,就要核对工具是否自动补齐或吞掉了结束符。

图 005:换行符与命令解析截图,展示跨平台换行符、原始请求字节、代理处理方式以及命令出现半条回显或报错时的对照验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
三、从基础文件包含到日志与图片
确认换行问题后,课程回到文件包含的基础模型。PHP 常见的四个函数是 include、require、include_once 和 require_once。后两者只允许同一文件被包含一次;在复盘案例中,真正反复使用的是前两个函数,原因是它们更直接地体现"读取目标文件并交给 PHP 解释器"的行为。

图 006:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这里需要把"能读到文件"和"能执行文件中的 PHP 代码"拆开。图片、TXT、Web 访问日志甚至 SSH 登录日志,从格式上都可能成为被包含的对象,但必须同时满足两个条件:PHP 进程对目标路径有读取权限,文件内容中确实存在可被解释的 PHP 片段。缺少任意一个条件,包含就会停在读取阶段或权限阶段。

图 007:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
日志包含案例从 Web 服务生成日志开始。课程先清理旧日志,再访问实验站点,确认 access.log 或 error.log 出现新的访问记录。这样做的目的不是证明漏洞,而是把"日志是否真的写入"从猜测变成可观察的事实。只有确认日志路径和写入权限,后续的包含测试才有意义。

图 008:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
随后把请求内容写入访问日志,再通过 include 读取日志文件。第一次观察到的现象是页面可能直接报错,原因通常不是 PHP 语法,而是日志路径猜错。APT 安装的 Nginx 日志位置相对固定,常见目录包括 /var/log/nginx/;源码编译安装则可能使用自定义前缀,若安装时显式改变目录,默认猜测就会失效。

图 009:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
因此,日志包含成功依赖两个前置条件:站点存在文件包含点,并且能够准确推断实际日志路径。 课程特别强调,权限也必须单独确认。SSH 登录日志常由 sshd 用户或受限权限维护,Web 进程即使理论上能够解析其中的 PHP 代码,也可能因为 others 没有读取权限而失败。除非实验环境允许调整权限,否则这条路径只能作为理论分支,不能当作稳定方法。

图 010:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
图片包含的成功率通常更高,因为上传头像等功能会返回图片的真实路径。课程中的做法是制作一个外观仍能正常打开的图片,在文件尾部追加 PHP 代码,再把上传后的图片路径作为包含目标。制作过程使用 copy /b 将图片二进制与 PHP 片段拼接,之后用编辑器确认图片仍可识别,并通过浏览器或代理核对真实 URL。

图 011:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这一步并非每次都能一次成功。图片二进制本身可能包含 PHP 解释器会处理的字符序列,导致语法解析提前中断。讲解者的修正方法不是修改解释器,而是更换图片样本、重复制作并重新验证。只要其中一张图片既能被站点当作图片接受,又能让包含点读到尾部代码,实验才算完成。

图 012:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
从日志和图片两个分支可以得到一个共同结论:文件包含的关键不是"文件类型",而是路径可达性、进程权限和内容可解释性。 这也是后面进入 Docker 环境后,日志路径看似存在却始终失败的原因。

图 013:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 014:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 015:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 016:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 017:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 018:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 019:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 020:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 021:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 022:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 023:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 024:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 025:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 026:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 027:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 028:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 029:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 030:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 031:日志与图片包含截图,展示日志路径、目录权限、请求参数、图片二进制拼接、上传结果和包含入口返回内容的逐步验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
四、Docker 改变了日志位置,也改变了排查方式
课程第二个阶段把实验环境切换到 Docker。讲解者用 PHP 与 Web 服务器镜像构建容器,并把宿主机目录映射到容器内的 Web 根目录。映射的目的不是增加功能,而是让代码和测试文件在宿主机编辑后立即出现在容器里,避免每次进入容器重新修改。

图 032:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
镜像拉取失败时,排查顺序先放在 Docker 守护进程的代理配置,而不是反复更换镜像标签。课程演示了在 systemd 服务目录配置代理、重新加载服务并重启 Docker 的过程,并建议优先阅读 Docker 官方文档核对配置字段。这样做的理由是代理配置属于服务级状态,容器命令本身写对了,镜像仍然可能因网络链路而无法拉取。

图 033:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
容器第一次启动后,访问映射端口没有得到页面。端口监听看起来是 0.0.0.0,所以问题不一定是构建失败。课程采用"先确认容器状态,再确认进程"的顺序:查看容器是否已退出,必要时使用 docker restart,重启后再次访问;进入容器检查 Web 根目录,确认宿主机映射文件已经出现;最后通过进程列表确认实际运行的是 Apache 而不是误以为的 Nginx。

图 034:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
重启能够解决一次访问失败,但它不是根因解释。这个案例中,容器状态在重启前后发生了变化,页面也从无法访问恢复到可访问。由此可以确认映射和端口配置基本有效,剩余问题集中在容器进程的启动时序或退出状态。复盘时要把"重启后恢复"记录为现象,而不是把重启本身当成普适修复。

图 035:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
进入容器后,日志问题变得明显。镜像只运行 Web 服务,没有额外的日志轮转组件,标准输出和标准错误被重定向到 /dev/stdout、/dev/stderr 一类的特殊文件。宿主机看到的 /var/log/apache2/access.log 可能只是指向这些特殊文件的链接,真正的输出位置已经改变。

图 036:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
课程先尝试直接包含 Apache 日志路径,结果出现文件不存在、读取卡住或页面无回显。继续用 ls -l、readlink 和 tail -f 跟踪后,确认日志链接最终指向进程文件描述符。/proc/<pid>/fd/1、/proc/<pid>/fd/2 等路径并不是普通日志文件,而是进程当前打开的标准流。

图 037:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这一步的失败具有代表性:路径字符串看起来正确,目录列表也能看到链接,但 Web 进程并没有权限把特殊文件当作可包含的普通文件读取。容器内的权限和宿主机权限不能混为一谈;即使实验以 root 进入容器,PHP-FPM 或 Apache 工作进程也可能以低权限用户运行。

图 038:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
因此,Docker 场景下的结论是:传统日志包含依赖的文件对象在容器中可能根本不存在,或者被重定向到不可读的进程流。 继续猜 /var/log 下的文件不会改变结果,必须先确认镜像的日志策略和进程权限。课程在这里结束日志路线,转向临时文件条件竞争。

图 039:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 040:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 041:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 042:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 043:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 044:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 045:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 046:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 047:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 048:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 049:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 050:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 051:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 052:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 053:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 054:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 055:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 056:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 057:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 058:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 059:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 060:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 061:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 062:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 063:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 064:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 065:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 066:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 067:Docker 环境截图,展示镜像构建、代理配置、端口监听、容器重启、进程确认、目录映射、日志重定向和权限排查。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
五、phpinfo 条件竞争:在临时文件窗口内完成包含
临时文件路线的出发点是 PHP 文件上传机制。向任意 PHP 页面发送上传请求时,PHP 会先把上传内容写入临时目录;后端业务是否接受这个文件,并不影响临时文件在请求处理早期被创建。请求完成后,PHP 才会关闭并清理它。这个生命周期决定了临时文件包含不是"找到一个永久路径",而是要在极短时间内抓住一个窗口。

图 068:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
课程用时间线把过程拆成几个阶段:客户端通过 POST 提交上传数据;PHP 解析请求并创建临时文件;文件内容写入临时目录;处理逻辑继续执行;请求结束后,临时文件被删除。真正可利用的窗口位于"写入完成"与"清理发生"之间。若包含请求在清理之后才到达,即使路径曾经存在,也只会得到失败。

图 069:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第一个难点是文件名随机。临时文件通常以 php 开头,后面跟随若干随机字符。直接暴力枚举需要覆盖很大的字符空间,而清理窗口只有几毫秒级别,枚举速度不可能赶在删除前完成。第二个难点是上传和包含属于两个请求:一个请求负责产生临时文件,另一个请求负责读取它。两个请求的速度互相独立,无法像同一请求内的参数传递那样依赖前后顺序。

图 070:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
为了获得临时文件的真实路径,课程引入 phpinfo。该页面会输出当前请求的环境和变量信息,其中包含上传临时文件的路径。于是流程被拆成两个线程:线程一持续向 phpinfo 发送上传请求并读取返回页面;线程二拿到线程一提取出的路径后,立即向文件包含点发起请求。

图 071:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
线程一不能简单地"请求一次就结束"。返回页面可能尚未完整生成,临时文件也可能在路径被读出前已经清理。课程脚本通过 socket 分块读取响应,每次读取固定大小的数据,直到遇到结束标记,再把各块拼接到缓冲区中。这样做的判断依据是:只有完整响应到达,正则匹配才不会因为数据截断而漏掉临时路径。

图 072:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
脚本使用正则从 phpinfo 输出中截取路径。匹配成功后,find 返回的是索引位置,代码再通过偏移量截出实际字符串。索引与偏移量的关系必须通过源码和页面格式共同核对,不能只看到"匹配成功"就认为取到的是完整路径。

图 073:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
取到路径后,线程二把主机、端口、包含入口和临时文件路径一起传入工作函数。工作函数建立 TCP 连接,发送包含请求。线程数量增加后,同一个上传请求会被多个包含线程同时尝试,目标是让至少一个包含动作早于清理完成。

图 074:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
为了延长窗口,课程还在请求头、Cookie 和其他可接收字段中填入大量无害字符,让 phpinfo 页面变大、处理时间变长。这个策略依赖输出缓冲配置:开启缓冲时,页面可能积累更多内容后再返回,临时文件因此存活更久;关闭缓冲时,延迟效果会减弱。课程测试显示,关闭与否主要影响成功率,而不是绝对决定成败,反复竞争仍可能命中。

图 075:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
脚本源码的阅读从 main 开始。命令行参数包含目标主机、URL、端口和线程数,端口与线程数存在默认值。随后通过 gethostbyname 将主机名解析为 IP,再调用初始化函数生成上传请求头、包含请求模板和工作线程。

图 076:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
初始化函数拼装的是标准文件上传请求。请求头中包含 Content-Disposition、Content-Type、边界字符串和文件内容。上传内容不是为了保留临时文件,而是为了让被包含的 PHP 片段在命中时写出一个新的持久文件。这样,即使临时文件随后被删除,后续仍可通过新文件继续验证结果。

图 077:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
请求体后面追加大量 a 字符,是为了让 phpinfo 返回变慢,不是为了改变 PHP 解析逻辑。脚本把相同的填充思路应用到 Cookie、访问头等可控位置,最终拼出一份足够大的上传请求。另一个函数则负责构造 include 请求,并预留临时路径和目标主机的格式化位置。

图 078:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
多线程执行函数接收可变参数,把主机、端口、请求头、临时路径和包含文件一并传递给工作函数。Python 中的 *args 允许一次接收多个位置参数;源码中出现它,是因为上传线程和包含线程需要共享的参数较多。进入工作函数后,脚本创建两条 TCP 连接,一条发给 phpinfo,另一条发给文件包含入口。

图 079:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
线程一不断读取 phpinfo 返回数据,并在缓冲区中寻找临时文件模式;线程二等待路径变量被填充,再发起包含。若线程一读到完整页面时文件已经被删除,匹配结果为空;若匹配成功但线程二启动过慢,包含仍会失败。课程没有把失败归咎于正则本身,而是把它解释为两个时间窗口没有重叠。

图 080:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
最终成功信号不是"请求返回 200",而是包含后的持久文件在 Web 目录出现。脚本原本会打印成功提示,但现场测试中有时没有输出;讲解者转而检查目标目录是否生成预期文件。文件存在意味着临时路径确实被抓到,并且包含动作执行过写文件逻辑。这个验证方式比单一回显更可靠。

图 081:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 082:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 083:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 084:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 085:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 086:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 087:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 088:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 089:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 090:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 091:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 092:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 093:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 094:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 095:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 096:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 097:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 098:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 099:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 100:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 101:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 102:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 103:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 104:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 105:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 106:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 107:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 108:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 109:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 110:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 111:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 112:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 113:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 114:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 115:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 116:phpinfo 条件竞争截图,展示上传请求、临时文件生命周期、响应分块读取、正则定位、请求头填充、线程函数和竞争结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
六、运行阶段的异常排查:无空间、无回显与真实成功
第一次运行脚本时,安装依赖提示磁盘空间不足。继续观察 top 和磁盘使用情况后,发现某个打印服务进程占用接近全部 CPU,并持续写入错误日志,日志文件很快膨胀到数 GB。课程把这段偏离主线的故障保留下来,因为它展示了应急响应中常见的判断顺序:先定位高负载进程,再确认它是否反复拉起,最后停用服务并清理日志。

图 117:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
直接 kill -9 进程后,进程很快以新的 PID 重现,说明它由服务管理器自动拉起。于是改用停止服务、清理已写满的日志,再禁用自动启动的方式。停止动作有延迟,CPU 图表也不会立刻下降;等文件句柄释放后,空间恢复,脚本安装才能继续。

图 118:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这段排查不能被简化为"杀掉进程即可"。如果不处理服务重启策略,进程会再次占满资源;如果只删日志而不停止写入,磁盘很快又会被填满。课程把它和面试中的挖矿木马排查联系起来,强调先保住系统可用性,再分析根因,并记录 PID、服务名和日志增长速度。

图 119:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
环境恢复后,脚本仍然出现运行即结束、没有成功也没有失败提示的情况。先检查目标的 phpinfo、包含入口和端口,确认页面可以访问;再检查脚本是否因 Python 版本或编码注释而提前退出。删除文件中额外的中文注释后,语法错误消失,但输出仍不稳定。

图 120:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这时不能把"没有回显"直接等同于"没有执行"。课程查看容器临时目录,发现目标文件已经生成。结合 include 参数实际指向临时文件的事实,可以确认上传线程和包含线程都命中过一次,只是成功提示没有打印。这个现象提醒复盘者:验证应至少包含响应、文件状态和服务日志三个层次。

图 121:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
脚本参数也带来一个容易忽略的坑。线程数增加到 150 时,若端口位置没有显式填入默认值,程序会把线程数误当成端口。修正参数顺序后,脚本能正常发起竞争;在另一台本地环境中仍出现"找不到临时文件",原因是该环境曾被手动改过,和 Docker 镜像的默认行为不同。

图 122:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
因此课程建议复现时优先使用统一的 Docker 环境。自定义系统中残留的 PHP 配置、临时目录权限和服务版本都可能改变结果。能够在容器中得到成功并不意味着所有主机都能复现,反之,本地失败也不能直接否定方法本身。

图 123:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 124:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 125:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 126:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 127:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 128:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 129:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 130:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 131:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 132:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 133:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 134:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 135:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 136:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 137:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 138:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 139:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 140:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 141:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 142:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 143:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 144:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 145:运行与异常排查截图,展示磁盘与 CPU 异常、服务停止、日志清理、参数修正、Python 版本处理以及无回显时的文件状态验证。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
七、公开案例的复盘价值:成功路径依赖前置条件
课程随后提到一个公开讨论过的租房平台案例,用来说明临时文件竞争并非只存在于课堂演示。案例叙述中同时具备文件包含入口和 phpinfo 页面,研究者使用同类脚本不断上传、提取临时路径,再让包含线程尝试读取,最终在站点目录生成持久文件。

图 146:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这段内容的复盘价值不在于照搬某个站点,而在于观察前置条件是否同时成立:包含入口是否存在,phpinfo 是否可访问,临时文件是否落在可预测的目录,以及运行用户是否具有写入权限。缺少其中任意一项,脚本都可能只产生大量失败请求。

图 147:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
讲解者还把它和简历、考试联系起来,认为真实公开案例可以作为项目描述的背景,但项目描述必须写清授权范围、验证证据和失败边界,不能只写"通过漏洞拿到权限"。对外发布时更应避免把公开案例改写成针对未知目标的操作指引。

图 148:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 149:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 150:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 151:公开案例截图,展示公开案例页面、包含入口和 phpinfo 前置条件、上传请求过程以及持久文件生成的授权复盘证据。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
八、没有 phpinfo 时的 Windows 路线
当目标运行在 Windows,且没有 phpinfo 页面时,课程转向系统自身的文件匹配能力。首先在配置文件中确认上传临时目录,将其改到实验者可观察的位置并重启服务。这样做是为了让临时文件路径从"随机且不可见"变成"目录已知、名称仍随机"。

图 152:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
随后构造两个请求:第一个向文件上传入口发送数据,让 PHP 创建临时文件;第二个把文件包含参数指向待匹配的路径。Windows 的 FindFirstFile API 支持通配符,课程利用这一点匹配随机文件名。重点不在 PHP 字符串本身,而在于最终由 Windows 文件 API 解释的路径模式。

图 153:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
课程对比了通配符语义。PHP 自身并不会把 * 和 ? 当作文件名通配符,但 Windows API 支持一组兼容 DOS 的模式,课件中用 <、> 和双引号等记号说明零个或多个字符、单个字符以及点号的匹配关系。只有把匹配符放在会调用 Windows API 的位置,模式才会生效。

图 154:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第一次构造请求时,回放地址仍是 127.0.0.1,目标容器无法访问,结果自然为空。修正为实验环境的实际地址后,重放同一份上传包,并删除多余字段,确认请求中只保留必要的文件表单和包含参数。请求成功后,检查 Web 目录是否出现持久文件,而不是只看代理工具中的状态码。

图 155:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
Windows 路线的全流程是:上传线程产生临时文件,包含线程用 FindFirstFile 支持的模式寻找它,匹配结果被写入请求参数,include 再读取匹配到的文件。两个表单字段名称相同会造成覆盖,最终留下的值取决于后一个字段的处理顺序,这也是课程要求逐字段核对数据包的原因。

图 156:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这套方法在 Linux 上不能直接套用。Linux 下的星号、问号属于 shell 或文件系统层面的展开规则,而上传请求中的参数仍由 PHP 解析;如果参数没有经过 shell,通配符不会自动变成文件名列表。课程用这个失败对照说明:同样的字符在不同解析层级中含义不同。

图 157:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 158:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 159:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 160:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 161:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 162:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 163:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 164:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 165:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 166:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 167:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 168:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 169:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 170:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 171:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 172:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 173:Windows 通配符截图,展示临时目录配置、上传请求、FindFirstFile 规则、通配符解析层、字段覆盖关系和 Windows 环境验证结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
九、session.upload_progress:在 Docker 中利用已知 Session 文件名
前两条路线在 Docker 环境中都可能受限:容器里没有可用的 phpinfo,也不会拥有 Windows 的文件 API。课程因此引入第三种机制:session.upload_progress。先通过 phpinfo 检查 session.upload_progress.enabled,确认实验镜像默认开启,再根据官方文档核对请求字段名称。

图 174:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这个选项原本用于前端上传进度显示。上传过程中,如果请求包含名为 PHP_SESSION_UPLOAD_PROGRESS 的表单字段,PHP 会把上传进度写入 Session。PHP 的 Session 默认落在文件中,文件名由客户端 Cookie 中的 PHPSESSID 决定,因此只要实验者显式设置 Session ID,就能提前知道待包含文件的名称。

图 175:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
课程先尝试提交同名字段,发现 Session 文件确实创建,但内容为空。继续查看配置后,定位到 session.upload_progress.cleanup。该选项开启时,上传结束会清理进度文件,所以仍然需要竞争;关闭它只能用于本地验证,真实测试不能假设可以修改目标配置。

图 176:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
脚本中,一个线程持续提交带有同名字段的上传表单,另一个线程尝试包含 sess_<已知ID> 文件。因为文件名不再需要猜测,竞争只围绕"写入完成"和"清理发生"展开,命中率高于随机临时文件。成功后,包含的 PHP 片段写入一个持久文件,Session 文件即使随后被清理也不影响后续验证。

图 177:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这一路线的边界也很清楚:必须开启上传进度功能,表单字段名必须精确,Session 存储方式必须是文件,且应用确实使用客户端可控的 Session ID。课程反复建议查阅 PHP 官方文档,而不是只根据 AI 生成的解释,因为字段名称和清理时机一旦写错,现象会与权限问题混在一起。

图 178:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 179:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 180:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 181:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 182:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 183:Session 上传进度截图,展示配置项、同名表单字段、PHPSESSID 对应的 Session 文件、cleanup 清理行为以及并发包含结果。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
十、异常退出留下临时文件:版本限制最严格的一条路线
第五种思路不再延长窗口,而是让 PHP 在清理动作之前异常退出。临时文件之所以难以直接爆破,一是文件名包含随机字符,二是请求结束后文件会被删除。如果能让进程在写入完成后、删除之前崩溃,临时文件就会留在临时目录,之后可以慢慢枚举。

图 184:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
课程使用一个 PHP 过滤器相关的崩溃条件演示这一思路,并明确它只适用于 7.1.20 之前的版本。版本要求是主要限制,常见服务器往往运行更高版本,因此实际遇到的概率很低。为了搭建验证环境,讲解者安装 PHP 7.0 系列,切换 PHP-FPM 服务,修改监听地址,停止旧版本并重新启动新版本,再让 Nginx 通过 9000 端口连接新的 FPM。

图 185:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
环境切换中出现了端口未监听、服务名写错和旧版本配置未替换等问题。排查顺序是先查看 PHP 版本,再检查 FPM 的 listen 配置,确认旧服务已停止,最后重启 Nginx 并用端口检查验证链路。没有 9000 监听时,即使 PHP 版本正确,Nginx 也无法把请求转交给 FPM。

图 186:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第一次发送异常请求只得到错误页面,临时目录没有新文件。复盘发现请求中根本没有上传文件,PHP 没有创建临时文件,自然不可能留下残留物。补上文件表单后,错误状态变成 502,同时临时目录出现多个未被清理的文件。这个失败说明测试必须同时满足"触发崩溃"和"确实产生上传文件"两个条件。

图 187:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
一旦 PHP 进程在清理阶段前退出,后续就不必再和删除动作竞争。课程建议批量提交多个表单,每个请求生成多个临时文件,以提升目录中出现可匹配名称的概率。文件名可能包含小写、大写和数字,字符空间越大,逐字符爆破越慢;生成的候选文件越多,匹配成功的概率才会增加。

图 188:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
这一路线最终仍然需要编写枚举脚本:固定临时目录前缀,循环尝试随机后缀,命中后再把路径交给包含入口。课程没有把它包装成稳定工具,反而强调版本稀少、依赖环境强、成功率低。对外复盘时应把它标记为受限实验技巧,而不是推荐的通用方案。

图 189:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 190:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 191:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 192:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 193:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 194:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 195:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 196:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 197:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 198:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 199:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 200:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 201:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 202:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 203:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 204:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 205:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 206:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 207:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 208:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 209:异常退出与残留文件截图,展示 PHP 版本配置、FPM 服务切换、端口冲突、错误响应、上传缺失、临时文件残留和枚举过程。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
十一、六条路线的横向比较与课程收束
课程最后把文件包含路线重新排了一遍。第一条是普通日志或图片包含,条件直观,但日志路径、权限和容器日志策略会造成失败。第二条是 phpinfo 条件竞争,需要解析返回页面并并发上传、包含;没有 phpinfo 时,Linux 环境通常难以继续。第三条是 Windows 通配符,只适用于 Windows 文件 API 能够解释通配模式的场景。

图 210:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
第四条是 session.upload_progress,通常只需确认配置开启、字段名称正确,并让上传线程与包含线程竞争清理时机。由于 Session 文件名由已知的 PHPSESSID 决定,这条路线在课程列出的环境中成功率最高。第五条是 PHP 异常退出留下临时文件,版本限制非常严格,适用面最窄。第六条涉及考试中出现的 pearcmd.php 等路径,课程只提示其与特定框架和调试信息有关,并计划在后续课程继续展开。

图 211:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
从考试角度看,讲解者认为中级认证的理论题覆盖面有限,实操题才会区分是否真正做过文件包含。复习时应能解释五个小问背后的条件,而不是背诵一个固定字符串。项目描述也应写清实验授权、环境、验证证据和失败原因,让读者能够判断结论是否可信。

图 212:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
最后的建议是把所有实验留在隔离网络和自有容器内。课程中提到校园网络隔离不足、内部服务可能被访问等现象,这类内容只能作为网络分区风险的提醒,不能转化为扫描真实内网的操作。复盘文章只保留"隔离失败会扩大影响面"这一经验,不提供针对未知系统的目标清单或行动步骤。

图 213:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
整节课最重要的经验不是某一个 payload,而是建立排查链:先确认解析层,再确认权限和路径;看到无回显时检查副作用;遇到容器时核对日志重定向;遇到随机文件名时分析生命周期;遇到竞争时同时测量产生、读取和删除三个时间点。只有把限制条件写出来,文件包含的成功或失败才具有可复现的解释。

图 214:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 215:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。

图 216:总结与延伸截图,展示课程总结、案例条件对比、版本和环境限制、认证练习提示以及授权隔离边界。;图中输入、输出或配置用于核对本阶段的技术判断,结论仅适用于课程授权实验环境。
十二、把每次尝试还原成证据链
换行符案例的证据链有三个层次。第一层是客户端原始请求:需要在代理的原始数据视图中确认结束符究竟是 0d 0a 还是 0a,不能仅凭编辑器显示的空行判断。第二层是服务端解析结果:如果返回内容中出现命令的一部分,说明请求已经到达解释器;如果同时出现 \r 相关错误,说明多余回车进入了命令文本。第三层是修正后的对照实验:同一条命令只替换换行字节,其余参数不变,输出恢复后才能把根因锁定在换行处理。
这类问题最容易出现的误判,是把"没有完整回显"当作"完全没有执行"。课程中多次出现只显示半条命令、额外出现 id 或返回空白的情况。正确做法是把请求拆成协议层、解释器层和业务层分别检查。协议层看换行和边界,解释器层看语法错误,业务层看文件是否生成或日志是否追加。三个层次中只要有一个已经产生副作用,就不能继续重复发送同一份错误数据。
日志包含的排查也遵循同样的证据顺序。先用访问请求让日志产生一条可识别记录,再用目录列表确认文件名,随后用权限信息确认 Web 进程能否读取。若目录列表显示文件存在而 include 报"无此文件",应优先检查容器、符号链接和进程根目录,而不是立即怀疑 PHP 语法。日志路径是一个由安装方式决定的配置事实,APT 安装、源码安装和容器镜像不能使用同一套假设。
权限问题需要区分"当前终端用户"和"执行包含的进程用户"。课程中通过 SSH 日志的 others 权限为零,说明即使管理员在终端中可以查看,也不代表 PHP-FPM 能够读取。对实验而言,可以在自有虚拟机里临时改变权限验证推理;对外复盘则必须明确这是授权环境中的可控变量,不能把修改真实服务器日志权限作为常规建议。
图片包含案例中的验证顺序也有明确层次。先在本地用编辑器打开拼接后的图片,确认图片头和尺寸仍可识别;再上传并记录站点返回的路径;最后才把该路径交给 include。如果直接把一个无法被图片解析器接受的文件当头像,后端可能在上传阶段就拒绝,后续包含失败与 PHP 解释无关。课程强调多准备几张图片,本质上是在排除二进制内容与 PHP 词法碰撞的偶然性。
Docker 案例提供了一个重要的环境迁移提醒。宿主机上看到的目录不一定等于容器内的目录,容器内看到的日志链接也不一定指向普通文件。复盘时应同时记录镜像、挂载点、端口映射和进程用户。只记录"访问 8086 成功"是不够的,因为端口能响应只说明网络链路建立,不说明 Apache、PHP-FPM、挂载目录和日志策略都符合预期。
镜像拉取失败的诊断要与应用失败分离。课程中把代理写入 Docker 服务配置,重载服务后重新拉取;这个动作针对的是守护进程访问远端仓库的能力。若镜像已经存在而容器启动失败,就不应继续修改代理。相反,应查看容器退出状态、启动日志和映射目录。把两类故障混在一起,会导致"反复重启但问题不变"的无效循环。
容器日志的符号链接需要通过目标解析确认。ls -l 只能告诉我们链接文本是什么,readlink 才能说明它最终指向哪里;tail -f 可以验证标准输出是否有新内容,但不能因此证明 PHP 能够包含该流。/proc/<pid>/fd/1 与普通 access.log 的差异在于它依赖进程是否仍然存活,并且权限由进程文件描述符和内核规则共同决定。这解释了为什么路径看似存在,包含却始终失败。
临时文件竞争的核心是时间,不是字符串。上传请求的开始、临时文件写入、phpinfo 生成输出、响应被读完、包含请求到达和清理动作之间,都存在先后关系。课程使用时间线图展示这些节点,是为了说明为什么"先上传一次,再慢慢包含"几乎必然失败。只要包含请求在清理之后到达,正确路径也没有意义;只要路径在响应尚未完整返回前被截断,正则也不会得到完整结果。
phpinfo 的价值在于把不可见的临时路径变成响应中的可检索文本。它不是延长生命周期的机制,延迟来自大响应和输出缓冲;路径获取来自页面内容。两者要在脚本中分开验证:先确认正则能从保存的响应样本中得到路径,再确认请求头填充确实改变响应大小,最后才测试并发。否则,正则错误、缓冲未开启和竞争速度不足会同时表现为"没有找到临时文件"。
课程源码中按 4096 字节循环读取并在读到结束时退出,这个实现细节对应的是网络读取可能分段返回。一次 recv 得到的内容不保证包含完整 HTML,也不保证边界落在正则需要的字符串两侧。因此,把每块立即交给正则会出现偶发失败;先拼接到缓冲区再匹配,虽然占用更多内存,却能避免分块边界造成的漏检。
线程数量的调整也不是越大越好。上传线程过多会让服务端更快进入资源瓶颈,包含线程过多则可能把成功请求淹没在连接错误中。课程现场使用不同线程数反复测试,发现有时提示不出现但文件已生成,说明线程数只影响概率和观测方式,并不改变文件生命周期规则。合理的做法是记录线程数、请求次数、命中次数和服务端资源,再比较成功率。
请求头填充的作用需要与上传内容区分。填充字符用于拖慢 phpinfo 页面处理,上传内容中的 PHP 片段用于在包含命中后写出持久文件。二者混淆会导致错误的调试方向:如果临时文件已经被成功包含,却没有生成持久文件,应检查写入路径和运行用户;如果页面返回太快、路径尚未匹配,应检查填充位置、输出缓冲和响应读取逻辑。
脚本参数错误是现场无回显的重要来源。目标主机、端口和线程数的默认值虽然方便,但位置参数一旦缺失,后续值可能被解释成错误的字段。课程中把线程数误当成端口的案例说明,执行前应先打印最终解析出的参数,再建立连接。参数打印属于低成本验证,能够在请求尚未发出前排除大量假故障。
"文件已生成但没有提示"说明观测点选择不完整。脚本的提示语依赖某个返回值或打印分支,文件生成则是目标端真实副作用。复盘时应把两者分开记为"程序输出"和"目标状态",并注明哪一项是成功判据。只要目标目录出现预期文件,就可以继续验证该文件是否能被正常包含;但不能因为文件存在,就忽略脚本可能还有连接泄漏或异常退出。
公开案例的技术价值来自条件组合,而不是平台名称。案例能够成立,是因为包含入口、phpinfo、上传临时文件和可写目录同时存在。若只看到某个平台曾经出现过漏洞,就推断同样的脚本在所有版本上都能用,会把历史事实误写成普遍规律。对外发布时保留条件比突出平台更重要,也更符合安全研究的授权边界。
Windows 通配符案例的关键是解析层。上传包由 PHP 按表单规范解析,包含参数最终是否经过 Windows 文件 API,则决定 FindFirstFile 的模式能否生效。课程专门对比 PHP 中的 *、? 与 Windows API 支持的模式,是为了避免把 shell 通配符、PHP 字符串和 Win32 路径规则混写在同一层。实验记录应标出每个字符由哪个组件解释。
Windows 临时目录配置的修改也有前置条件。先写入配置文件并重启服务,之后再发上传包;如果不重启,旧进程仍会使用旧目录,目录里看不到新文件并不说明上传失败。重启后要用 phpinfo 或目录时间戳确认配置生效,再继续测试。课程中"一次成功"之所以可信,是因为前面已经核对过临时目录、请求字段和生成文件三件事。
Session 上传进度案例依赖官方定义的字段名。表单中的名字少一个字符,PHP 就不会把进度写入 Session,最终看到的只会是空文件或普通 Session。课程建议在官方文档中核对 session.upload_progress.name、session.upload_progress.enabled 和清理选项,而不是凭经验猜测。配置值、表单字段和 Cookie 三者必须同时对应,才可能得到已知文件名。
Session 文件名可控并不意味着写入一定持久。PHPSESSID 只决定文件名,清理选项仍决定文件是否在请求结束后删除。课程因此继续使用两个线程:一个保持上传过程,另一个在 Session 文件仍有内容时尝试包含。与随机临时文件相比,这里省去了高成本的文件名爆破,但没有省去时间竞争。
异常退出路线的验证先从版本开始,而不是先发崩溃请求。课程先切换 PHP-FPM 版本,确认 php -v、服务名称、监听地址和 Nginx 转发链路,再测试过滤器条件。如果版本在修复范围之后,继续发送同样请求只会得到普通错误或稳定拒绝;如果上传文件没有进入请求,PHP 甚至不会创建临时文件。每次失败都对应一个缺失条件。
临时文件残留的验证也要记录数量和时间。单次请求留下一个文件,不能说明批量枚举可行;课程通过一次表单上传多个文件、多次提交,观察临时目录中残留数量,再讨论爆破概率。文件名同时包含大小写和数字时,搜索空间会显著扩大,生成更多候选只能提高概率,不能保证命中。这个结论必须与版本限制一起写出。
课程没有把异常退出技巧描述成稳定方案,原因是它受版本、过滤器行为、FPM 配置和请求格式共同影响。对外文章保留了安装旧版本、端口冲突、缺少上传字段、出现 502 和临时文件残留的完整链条,目的正是说明成功并非由一个神奇字符串直接带来。复现者应在自有实验机中承担恢复服务、清理文件和回滚版本的责任。
从学习角度看,最有效的练习不是重复发送同一个包,而是为每个案例写一张条件表。日志案例记录路径和权限,Docker 案例记录挂载和重定向,phpinfo 案例记录响应读取和线程关系,Windows 案例记录解析 API,Session 案例记录字段和文件名,异常退出案例记录版本和清理时机。条件表能帮助面试回答从"会不会"升级为"为什么在这个环境里会或不会"。
简历中的项目描述也应遵循证据链。可以写"在授权容器中验证临时文件竞争,并通过目录状态确认持久文件生成",不应只写"利用文件包含拿到 Shell"。前者说明环境、方法和证据,后者隐藏了权限、版本和失败边界。课程中关于考试的建议同样适用:先写出前置条件,再写操作和结果,最后说明限制。
网络隔离的讨论必须单独设边界。课程提到校园网与内部服务隔离不足,是为了提醒环境设计会放大漏洞影响;这不构成访问真实内部系统的授权。本文只把它转述为防守侧经验:实验网络应与生产网络分离,容器端口只绑定必要接口,日志和 Session 目录应使用最小权限。任何超出授权范围的扫描、上传或文件包含都不属于本文复盘。
把整节课串起来,可以看到每个案例都在回答同一组问题:文件到底在哪里,哪个进程创建它,哪个用户读取它,内容由谁解析,文件何时删除,失败时留下什么副作用。答案一旦明确,工具选择才有依据:目录和权限用 ls,链接用 readlink,进程用 ps 或 top,服务状态用 systemctl,请求细节用代理,脚本行为用源码和日志交叉验证。工具本身不是结论,证据链才是结论。
最终复盘应同时保存成功和失败样本。成功样本用于确认路径、参数和输出,失败样本用于证明某个前置条件不成立。把失败请求删除,会让文章看起来像一次直线成功,也会掩盖 Docker 日志、Session 清理和版本限制这些真正有价值的知识点。技术博客的可信度,来自它愿意展示"为什么第一次没有成功"。
十三、按原始推进顺序重做一次现场记录
课程开头先处理换行符,而不是直接进入文件包含,原因是前面的协议错误会污染后面的所有测试。如果请求在代理层已经被截断,后面看到的 400、无回显和命令报错都可能只是副作用。复盘记录应先写清客户端、代理工具和服务端各自负责什么,再记录哪一层吞掉了 \r,这样读者才能理解为什么只改一个字节就会改变结果。
进入基础包含后,讲解顺序先是函数,再是文件类型,最后才是权限。这个顺序体现了从语义到资源的判断:include 是否会把内容交给 PHP,是语言层问题;图片、TXT 和日志能否成为输入,是文件对象问题;Web 进程能否读取,是系统权限问题。三者混在一句"任何文件都能包含"里,会掩盖最重要的限制。
日志实验中清理旧日志的动作也有复盘意义。若不先清理,后续看到的记录可能来自旧请求,无法判断当前请求是否真的写入。课程先清空、再访问、再查看文件增长,实际上是在建立一个最小可观测实验。这个习惯可以迁移到所有安全测试:在改变状态前先把历史状态归零,避免把旧结果误当新结果。
日志路径的两条分支对应两种安装方式。APT 安装的目录结构稳定,适合在实验中快速验证;源码编译的前缀可能由安装者指定,路径推断必须回到服务配置。课程没有把"固定路径"说成漏洞本身,而是把它当作运维习惯带来的可预测性。对外文章保留这个差异,可以避免读者把某个发行版的目录当作所有系统的事实。
SSH 日志的讨论说明"理论可行"和"实践可行"之间有距离。理论上,日志中出现 PHP 片段就可能被解释;实践中,文件权限、日志拥有者和 Web 进程用户决定了读取是否发生。课程通过 sshd 和 www-data 一类用户的对比,强调不要只看文件名。权限检查必须包含拥有者、组、其他用户以及目录本身的执行权限。
图片载荷制作的重点是保留合法外观。课程先把 PHP 代码追加到图片,再用编辑器打开验证,随后才上传。若图片查看器无法打开,说明文件尾部或拼接方式已经破坏格式,后端可能在文件校验阶段拒绝。这个顺序把"上传失败"和"包含失败"分开,减少了把多个问题同时调试的风险。
Docker 环境切换时,课程先构建镜像,再配置映射和端口,最后处理服务状态。这个顺序不是形式要求,而是为了在每一步都留下可验证的中间结果:镜像存在、容器创建、端口监听、页面可访问、目录映射生效。任何一步失败都应该停在该层解决,而不是继续发送包含请求。
代理配置的排查体现了文档优先。课程展示了创建服务级配置文件、写入代理地址、重新加载 systemd 和重启 Docker 的流程,并指出零散网络文章可能与当前版本不匹配。对外复盘时可以保留"查官方文档"的经验,但不把某个培训机场地址写成固定依赖。代理是实验环境变量,文档中的字段和加载时机才是可迁移知识。
容器访问失败后重启成功,容易诱导人们得出"重启万能"的结论。课程随后进入容器查看 Apache 进程和映射目录,实际上是在补足根因证据。页面重新出现只能证明服务恢复,不说明为什么第一次退出。文章把这两个事实并列记录,保留不确定性,避免把偶然恢复夸大成确定机制。
日志重定向到 /dev/stdout 和 /dev/stderr 后,传统日志包含路线失效。课程继续查看符号链接和 /proc 文件描述符,是为了确认"看得见的路径"与"真正的数据流"之间的关系。特殊文件不能简单按普通文件处理,权限由进程状态和内核接口共同决定。这个结论也解释了为什么在容器里拥有目录列表并不等于拥有读取能力。
临时文件生命周期图把上传、写入、关闭、业务处理和删除排成顺序。课程特意指出关闭文件不等于删除文件,删除通常发生在请求处理完毕后。这个区分决定了竞争窗口的位置,也决定了为什么要把 phpinfo 页面处理变慢。若把关闭误当成删除,读者会错误地以为窗口在写入结束时立即消失。
phpinfo 返回页面的读取采用循环,而不是一次性接收。网络响应可能被拆成多个 TCP 数据段,页面中的临时路径可能跨越两个数据块。脚本把每次读取追加到缓冲区,再统一执行正则,正是为了避免分段造成的漏匹配。这个实现选择没有引入新的漏洞知识,只是把课程代码中出现的读取逻辑解释清楚。
线程一和线程二的职责不能调换。线程一掌握临时路径,但速度受 phpinfo 页面生成影响;线程二掌握包含请求,但必须等到路径出现才能工作。把两个职责放在一个串行循环中,会让上传请求结束后才开始包含,通常已经错过清理窗口。课程因此使用多个线程反复竞争,并把成功看成概率事件。
请求头中大量 a 字符的效果需要通过响应时间验证。若响应大小没有变化,或者服务端开启了即时输出,填充不会延长窗口。课程中的测试只说明这个策略可能提升成功率,并没有保证所有 PHP 配置都有效。复盘时应保留"依赖输出缓冲、效果存在差异"的限定,不能把它写成必然成立的优化。
脚本源码分析从命令行参数进入 main,再跳到 setup、请求拼接、响应读取和线程函数。讲解者逐段查看 gethostbyname、socket、正则匹配和可变参数,目的在于建立调用链。对博客而言,保留调用链比逐行翻译更有价值:读者能看出数据从目标地址进入,经过请求头构造,再流向两个 TCP 连接。
运行阶段出现磁盘爆满,是一次独立的应急响应练习。先用 top 找到高 CPU 进程,再看日志目录体积,确认错误循环导致空间快速消耗;停止服务后再删除日志,最后检查 CPU、磁盘和端口恢复。课程把这一过程与挖矿木马面试题联系起来,但没有把打印服务误判为木马。判断依赖进程名、日志内容和启动关系。
无回显成功的验证依赖副作用。课程检查临时目录中是否出现新的持久文件,再回到 include 参数确认它确实指向刚才生成的内容。这个交叉验证排除了"文件由其他操作生成"的可能。文章不把"脚本打印成功"作为唯一证据,也不把"脚本无输出"作为失败结论。
Windows 路线中,配置临时目录、重启服务和上传文件是三个不可省略的步骤。没有重启,旧配置仍在内存中;没有上传,临时目录不会出现文件;没有包含请求,通配模式也没有实际对象可以匹配。课程现场的成功发生在这些条件均满足后,不能被简化为"把星号换成小于号即可"。
Windows API 与 PHP 的边界还解释了 Linux 对照实验为什么失败。上传字段由 PHP 解析,不会自动调用 shell;Linux 下的通配符只有在特定解析层被展开。相同字符跨过不同组件时语义会改变,这是文件包含、命令执行和路径穿越调试中都必须关注的通用问题。
Session 上传进度路线的关键证据是 Session 文件内容,而不是页面上的进度条。先确认开关,再提交同名字段,随后查看 sess_ 文件是否创建并写入。若文件存在但为空,要检查清理选项和竞争时机;若文件根本不存在,要检查 Cookie、字段名和请求是否被 PHP 接收。每个现象都对应不同的排查分支。
异常退出路线必须先确认版本。课程安装 7.0 系列并切换 FPM,期间出现监听端口、服务名和 Nginx 重载问题,这些都是环境搭建的一部分,不应从成稿中删掉。若只保留最终 502 和残留文件,读者会误以为版本切换没有成本,也无法理解为什么同一请求在 Docker 的更高版本中不再生效。
临时文件批量残留后,枚举脚本仍然面对字符空间。小写、大写和数字组合会改变尝试次数;一次生成多个文件只能提升概率,不能把随机名称变成确定名称。课程最后把这条路线归为可能性最小的技巧,原因正是版本稀少和爆破成本同时存在。复盘应保留这种否定性结论,而不是只展示成功截图。
考试和简历建议与技术过程相互对应。中级实操题的前置条件、路径和参数往往已经通过题面给出,真正区分能力的是能否识别它属于哪条文件包含路线。简历中的案例也要提供证据:环境是 Docker 还是虚拟机,使用了哪种竞争方式,如何确认成功,哪些条件导致失败。这样的写法比堆叠"高危漏洞""拿到 Shell"更可信。
课程最后提到网络隔离不足,是为了提醒安全边界的重要性。实验地址和内部数据库不能被当作公开目标,文章也不再重复可用于访问真实系统的细节。对外发布时,最有价值的结论是把实验放在隔离网络,限制容器端口,分离日志目录,并在测试结束后恢复服务和删除临时文件。
如果把整节课压缩成一张排查表,第一列应是对象:日志、图片、临时文件、Session 文件或进程描述符;第二列是创建者:Nginx、Apache、PHP、PHP-FPM 或内核;第三列是解析者:PHP、Windows API、shell 或浏览器;第四列是删除时机;第五列是成功证据。课程里的每一次转向,都是因为其中一列与预期不一致。
因此,文章的最终结论并不是"哪一种包含方法最好",而是"先识别环境,再选择与生命周期匹配的证据"。日志路线适合路径和权限稳定的普通主机,phpinfo 路线适合可以读取返回信息的环境,Windows 路线依赖 Win32 通配规则,Session 路线依赖配置和字段,异常退出路线受版本严格限制。只有把这些条件写全,技术复盘才不会变成脱离场景的操作清单。
十四、对外发布前的事实边界与复现记录
对外文章需要把"课件中出现过的事实"和"复盘者的解释"分开。课件展示了若干终端、代理和编辑器截图,截图可以证明当时输入和输出的样子,但不能证明所有环境都相同。因此,正文使用"在课程实验环境中观察到""在该配置下验证成功"等限定语句,避免把一次运行结果扩展成普遍结论。
文件包含的第一层事实是函数语义。include 与 require 会把目标内容交给 PHP 处理,include_once 与 require_once 会记录已加载文件。第二层事实是文件路径和权限。第三层事实是内容是否包含可解析的 PHP 片段。文章在三个层次之间保持区分,读者可以据此定位失败,而不是把所有错误都归为"漏洞不存在"。
在日志实验中,访问一次站点、看到一条新的访问记录,只能证明日志写入成功;它不能证明包含点能够读取日志。继续检查日志所属用户、组、权限位和父目录,才能判断 PHP 进程是否可读。课程中 SSH 日志的权限不足,就是"文件确实存在,但包含仍然失败"的完整反例。
在图片实验中,图片能被查看器打开,只能证明图片格式仍然可识别;它不能证明 PHP 解释器会执行追加内容。要完成验证,还需要确认上传后的路径、包含点的参数以及执行后生成的文件或返回结果。每一步都有独立证据,任何一步缺失都应在文章中标为未确认,而不是用最终截图替代全部过程。
Docker 迁移后,目录、端口和日志策略是三个新的变量。宿主机映射目录解决了文件编辑问题,不能自动解决容器内进程权限;端口监听解决了网络可达性,不能自动解决应用进程退出;日志符号链接解决了输出收集问题,不能自动把标准输出变成可包含文件。课程通过多个截图逐一确认这些变量,成稿也按相同顺序叙述。
/dev/stdout、/dev/stderr 和 /proc/<pid>/fd/* 属于特殊对象,读取它们依赖进程生命周期和权限。即使路径可以在目录列表中显示,PHP 进程也可能无法安全地把它们当作普通文件打开。这个限制解释了 Docker 下日志路线的失败,不需要再假设某个隐藏日志目录。对外文章保留这一点,是为了让读者理解容器日志和传统主机日志的差异。
临时文件案例的第一项证据是文件创建,第二项证据是路径提取,第三项证据是包含后的副作用。只看到 phpinfo 页面返回,不代表路径已经成功正则匹配;只看到正则匹配,不代表包含线程及时发起请求;只看到包含请求返回,也不代表临时文件中的代码被执行。课程中最终通过持久文件出现来确认命中,成稿沿用这一证据标准。
脚本使用 socket、分块读取和正则匹配,说明它并非依赖代理工具的"重放按钮"。代理适合观察请求和验证单次结果,多线程脚本则负责制造上传与包含的并发。两者用途不同:前者帮助定位字段、边界和返回值,后者帮助争取时间窗口。复盘时把工具职责写清楚,比简单罗列工具名称更有帮助。
输出缓冲与垃圾数据的关系也需要谨慎表述。课程测试发现大量请求头可能让页面处理时间变长,但不同配置下效果有差异。文章不把"填充 5000 个字符"写成固定阈值,而是说明它用于尝试增加响应处理时间,实际效果要通过响应长度、返回时间和成功率测量。这样既保留了课堂操作,也没有凭空增加适用范围。
线程数的变化同样只影响概率。线程少时,包含请求可能在路径返回前启动;线程多时,服务端可能出现连接拒绝、资源消耗和大量失败日志。课程使用多次竞争观察结果,而不是用一次运行宣称必然成功。文章把"成功提示未出现但文件生成"列为观测差异,把"完全没有文件"列为条件未满足或竞争失败,两种结果不混写。
公开案例的叙述保留了技术前置条件,隐去了对未知目标的行动导向。文章只说明案例同时具备文件包含、phpinfo、上传临时文件和可写目录,并把它放在授权研究或公开披露语境下。这样既保留了讲师关于真实案例和简历价值的判断,也避免读者把文章理解为对任意网站的测试指南。
Windows 通配符部分的关键事实是 FindFirstFile 支持模式匹配,而 PHP 参数本身不负责展开。文章因此把解析责任归到调用 API 的层级,避免写成"PHP 的星号可以直接匹配文件"。Linux 对照实验的失败也被保留,用来说明相同字符在 shell、PHP 和 Win32 API 中的语义不同。
临时目录配置的修改必须配合服务重启。配置文件已经保存但进程尚未重载时,phpinfo 仍会显示旧值,目录中也不会出现预期文件。课程中通过重启后再上传验证配置生效,文章把这一步列为必要条件。对读者而言,这比记住某个目录字符串更重要,因为不同安装方式可能使用不同临时目录。
Session 上传进度路线的事实链从三个配置项开始:是否启用上传进度、进度字段名称是什么、清理是否开启。然后是请求层的 PHP_SESSION_UPLOAD_PROGRESS 字段和 PHPSESSID Cookie,最后才是 Session 文件的写入与包含。课程中先发现文件为空,再定位到 cleanup,说明配置项之间的组合决定了现象,不能只检查一个开关。
Session 文件名已知并不等于没有竞争。清理动作仍可能在包含请求到达前执行,因而仍要保持上传和包含并发。它的优势只是省去随机后缀枚举,降低了搜索成本。文章把这条路线评价为课程列出的高成功率方法,同时保留"必须满足配置和字段条件"的限制,没有把成功率写成保证。
异常退出路线的版本限制来自课程明确提到的修复版本。文章使用"早于 7.1.20 的版本可能存在条件"而不是断言所有旧版本都可用,因为现场还涉及过滤器、FPM 和请求格式。安装 7.0 系列只是搭建实验条件,不代表生产环境应该降级。对外发布时,这种版本限定是安全边界的一部分。
502 在异常退出案例中是一个可观察信号,但它本身不能证明临时文件残留。必须同时检查 PHP-FPM 进程状态和临时目录;只有看到进程退出、请求包含上传字段、目录出现未清理文件,才能把它们串成因果链。课程中第一次没有上传文件,虽然也出现错误,却没有临时文件,这正是排除误判的关键对照。
临时文件数量增加后,爆破成功率可能提高,但字符空间仍然存在。文件名含小写、大写和数字时,尝试次数会显著增长;多生成文件只是增加候选集合,不能改变单个名称的随机性。文章把这一方法放在可能性最小的章节,并强调版本、配置和清理行为共同决定是否值得尝试。
学习建议部分保留了讲师的实际取舍:官方文档适合核对 PHP、Docker 和 Windows API 的定义,AI 可以辅助改写 Python 语法或生成实验脚本,但关键结论仍需用文档和实验验证。课程中出现 AI 解释不准确的经历,说明工具输出只能作为线索,不能替代版本、配置和源码证据。
面试建议也与前文的证据链对应。面对"如何处理挖矿进程"一类问题,应先说资源现象、进程定位、服务关系和恢复动作;面对文件包含问题,应先说路径、权限、生命周期和成功证据。只报出一个命令而不解释为什么执行,无法证明对环境有判断能力。
文章最后保留课程关于网络隔离的提醒,但不复述可用于访问真实内网的地址、数据库或文件。校园网与内部服务未隔离,是防守侧的配置风险;授权实验应使用独立网段、虚拟机和容器,测试结束后清理临时文件、恢复服务版本并删除测试账户。复盘的价值在于改善实验和防守,而不是扩大未授权操作范围。
从写作角度,所有图片都放在对应技术步骤之后,而不是集中在文末。图片标题采用阶段、对象和验证动作重新描述,避免复制课堂口语。重复图片引用在本附件中没有出现,所有 216 个媒体对象均按正文首次出现顺序提取为 image001.png 至 image216.png,这也是最终自检数字的来源。
字数统计只计算最终 Markdown 正文中的中文字符,不把自检数字当作凑字数内容。补充段落围绕原稿已有的参数、路径、函数、报错、权限、版本和讲师判断展开,没有引入新的工具能力、案例结果或外部事实。这样既满足篇幅要求,也保持复盘文章与原始授课材料的事实边界。