文件包含(LFI/RFI):日志投毒与 PHP 伪协议利用
文件包含漏洞有个不太好听、但很准确的外号它不像注入那样“改语句”更像是应用主动说——“你告诉我读哪个文件我帮你加载进当前执行上下文”。在 PHP 的老项目里这句“帮你加载”一旦碰上用户可控路径轻则任意文件读取重则代码执行。国内安全圈常把它和LFI本地文件包含、RFI远程文件包含、日志投毒、伪协议放在同一条故事线里讲。另外先泼冷水现代 PHP 默认往往关掉了远程包含框架也很少再写裸include $_GET[page]。但存量业务、插件、模板主题、错误的下载/预览接口里这类洞仍在。学会它更多是学会**“用户输入绝不进入文件包含/加载 API”**这条铁律。一、文件包含是什么病以 PHP 为例其它语言也有“动态加载模块/模板”的近亲问题include ($_GET[file] . .php); require、include_once、require_once 同理本意可能是?fileabout加载about.php。一旦file可控攻击者就可能改成路径穿越去包含系统文件或在配置不当时包含远程资源。关键点在于被包含的文件内容会按 PHP 代码执行上下文处理若其中出现可执行的 PHP 代码。因此包含/etc/passwd这类纯文本 → 常常是读取源码/配置泄露包含带有?php ... ?的内容 → 可能执行若攻击者能控制“被包含内容里出现 PHP 代码”就接近 GetShell。这就是为什么上传漏洞、日志写入口、会话文件、缓存文件常和 LFI 绑在一起。二、LFI 与 RFI别混着背LFI 本地文件包含RFI 远程文件包含目标服务器本地文件远程 URL 上的资源典型条件路径可控 包含函数额外需要allow_url_include一类危险配置为开现代默认仍常见于老代码相对少因默认常关闭远程包含危害读敏感文件、组合执行直接拉远程脚本执行往往更直接人话LFI打的是“读谁、包谁”RFI打的是“远程下载并执行”配置允许时。测试时先看 PHP 配置与代码别一上来假设 RFI 可行。很多报告把普通路径穿越读文件也写成 RFI这不严谨。三、LFI 的常见长相1. 页面路由式index.php?pagehelp→include $page . .php攻击者尝试page../../../../etc/passwd是否截断.php后缀、是否有过滤决定手法。2. 模板 / 语言包 / 主题langen→ 加载languages/en.php看起来无害一旦拼接不严谨一样穿越。3. 下载与预览“读文件”接口有的不是include而是readfile/file_get_contents然后输出。那是任意文件读取和包含近亲。若再把内容喂给危险函数可能升级。分类时写准确读取 ≠ 包含执行。4. 框架里的意外某些错误的视图加载、插件机制、调试开关也会引入动态包含。审计时搜include、require、include_once、file_get_contents、readfile、fopen与用户输入的交集。四、从“读文件”到“执行”攻击者缺的那一块纯 LFI 经常只能读到配置、密钥、源码——已经很严重。若还想执行代码攻击者需要让“被包含的本地文件”里出现可执行脚本内容。常见思路概念级包括文件上传先把带脚本内容的文件传到服务器某处再 LFI 包含它扩展名不一定是.php取决于包含方式日志投毒让访问日志/错误日志里记下一段脚本特征再包含日志文件其它写入点会话文件、缓存、邮件スプール、图片处理残留——凡是攻击者能写入可控内容的路径伪协议不一定写文件而用 PHP 封装协议在包含/读取时做转换或借道见后文。防守方要切断的不只是“禁止../”而是用户输入永不进包含函数写入点可控内容不可被包含执行上传目录不可执行且路径不可预测。五、日志投毒把日志变成“临时源码”1. 原理用故事说Web 服务器通常把请求行、User-Agent、Referer 等写入访问日志。若应用存在 LFI且攻击者能定位到日志路径就可能发送一个请求让日志里留下可控字符串甚至 PHP 代码形态 → 再用包含漏洞加载该日志文件 → 日志中的代码被 PHP 解释执行这叫日志投毒Log Poisoning是 LFI 经典组合技。它不神秘条件却很硬要有可用的本地包含要猜对或读到日志路径日志内容要真能进 PHP 引擎并被当成代码现代环境可能日志不可读、路径隔离、PHP 不解析该路径、或禁用了危险函数。2. 授权测试时怎么理解“证明”在靶场里教学演示会展示“UA 里写入标记 → 包含日志 → 标记被执行/被包含显示”。在真实授权项目里我更建议先证明可包含已知文件如公开的无害文件或配置中允许的路径再评估日志是否可读、是否在 web 用户权限下慎用真实恶意载荷用无害唯一探针证明“可控输入进入了被包含文件”往往足够写进报告生产环境优先推动修复而不是把日志变成 shell。3. 防守专克日志投毒根治消除 LFI参数化路由白名单禁止用户拼路径Web 用户对日志目录不可读权限分离日志放在 webroot 外限制 PHP open_basedir容器场景下只读文件系统与最小挂载监控异常包含路径、异常../访问应用日志与 WAF。不要幻想“过滤 User-Agent 里的 php 关键字”能当主防线——绕过面太大且日志源不止 UA。六、PHP 伪协议包含与读取的“另一扇门”PHP 提供多种封装协议wrappers可在file_get_contents、include、require等场景使用。对开发很方便对安全很危险——因为用户若能控制路径字符串就可能喂进伪协议。下面按安全含义介绍常见协议便于审计与防护具体行为与 PHP 版本、配置如allow_url_fopen有关。1.php://filter——读源码的常客常被用来在包含/读取时做过滤转换。安全讨论里最著名的用途是把源码以某种编码方式读出便于在页面回显受限时仍证明任意文件读取例如读配置文件、读业务源码找密钥。对防守方的含义即便没有“执行”读到数据库密码、云密钥、会话配置也足够致命过滤../却没禁伪协议等于拦了穿越、没拦 wrapper修复必须是白名单而不是黑名单拦php://。授权测试报告里写清通过伪协议读取了哪些文件的哪类敏感信息注意脱敏展示。2.php://input——请求体当文件在特定配置与用法下请求体可被当成流读取。历史上与包含组合时风险很高。现代默认配置与写法下要具体分析审计见到用户可控进include且可能触及 input 流应按高危看。3.data://等数据包装在允许时可能把数据直接放进“路径”里。依赖allow_url_include等配置现代多默认关闭。审计仍要看配置落地别只看代码。4.phar://、zip://、phar反序列化相关phar://等可把归档当文件系统访问。安全上它还与Phar 反序列化等问题有过大量讨论某些文件操作碰 Phar 元数据时触发反序列化。这已超出“纯包含”范畴但同属“路径可控 封装协议”家族。防守升级 PHP限制危险文件操作的用户输入不信任用户上传的 phar/zip 进入敏感文件函数。5.file://与其它明确指定本地文件。有时用于绕过只拦了相对路径的弱过滤。白名单面前同样应失效。6. 伪协议防御总纲对“文件路径”类参数只允许枚举值about/help/contact映射到内部路径 禁止用户字符串直接进入 include / file_get_contents / fopen 关闭不必要的 allow_url_include 收紧 open_basedir 上传文件不进可包含路径黑名单拦php://、phar://、zip://可以当辅不能当主。七、RFI为什么现在讲得少但仍要会查远程文件包含依赖危险配置如allow_url_includeOn以及代码把 URL 丢进包含函数。若条件成立攻击者可让服务器拉取远程脚本并执行——链路短、危害大。运维检查php.ini 中 allow_url_include allow_url_fopen影响面不同但同属 URL 当文件相关配置代码检查是否有include $_GET[...]且未白名单。发现 RFI 条件优先级按紧急漏洞处理先关配置、再改代码。八、和上传、解析、会话的组合回顾本系列上传篇上传不一定直接 GetShell但可为 LFI 提供“可包含文件”。会话文件若可预测路径且含用户可控数据也可能成为包含目标视存储方式而定。日志投毒则是“不经过上传功能”的写入。画链路时建议写入点上传/日志/缓存 包含点LFI 执行条件PHP 解析缺任何一环危害可能降级为“仅读取”或“不可用”。修复任意一环都有价值修包含点是根。九、授权测试方法务实版1. 发现参数名像page、file、path、template、lang、doc报错泄露include/require路径白盒搜包含函数与用户输入。2. 确认 LFI尝试包含公开可知文件或应用自身静态文件观察回显差异、报错、时间慎用暴力证明路径穿越或伪协议读取成功。3. 评估升级面是否可读配置与密钥是否存在可控写入点日志是否可读配置是否允许远程包含。4. 报告分类写任意文件读取 / 本地文件包含 / 可打到代码执行的条件。修复给白名单示例与配置项。避免在报告附件里留真实后门。十、代码与配置层修复1. 白名单映射推荐用户传 pageabout → 仅当 about 在允许集合 → 内部映射到 /views/about.php → include 固定内部路径用户字符串永不拼接进文件系统路径。2. 禁止动态包含用户输入能改路由就改路由遗留代码用映射表兜住。3. 配置加固allow_url_include Off合理open_basedir日志与 web 用户权限分离关闭不必要的危险函数按业务评估4. 显示与错误生产关闭详细错误路径回显减少“帮攻击者找包含位置与日志路径”。5. 检测WAF/应用层对../、php://、phar://可告警RASP/HIDS 对异常文件读取、异常 include 路径做行为检测仍须源码修复。十一、开发评审口头禅这个include/require/file_get_contents的路径里有用户输入吗是白名单映射还是字符串拼接allow_url_include是否关闭上传/日志/缓存是否可能被同一进程包含报错会不会泄露绝对路径十二、收尾文件包含漏洞的核心不是伪协议名单背得多全而是一句话文件系统路径与包含/读取 API不能直接吞用户输入用户若还能控制“被加载内容”读取就会升级成执行。日志投毒教你重视写入点与权限分离PHP 伪协议教你重视封装协议与任意文件读取RFI 教你重视危险配置开关。今晚若只做一件事在代码库里全局搜索include/require/file_get_contents对每一个命中问“路径从哪来”。问完你对 LFI/RFI 的防守就已经比只收藏伪协议列表的人更接近实战。