利用PHP伪协议实现Web安全中的文件包含漏洞利用
1. PHP伪协议与文件包含漏洞基础文件包含漏洞是Web安全中常见的漏洞类型之一它允许攻击者通过构造特殊的输入参数将服务器上的任意文件包含到当前执行的脚本中。在PHP环境中这个漏洞往往与PHP伪协议结合使用能够实现更灵活的利用方式。我第一次遇到这种漏洞是在一个CTF比赛中题目要求读取服务器上的flag.php文件。当时发现页面通过cookie中的language参数动态加载语言文件这立刻让我联想到可能存在文件包含漏洞。PHP伪协议就像是一把瑞士军刀它提供了多种文件处理方式其中最常用的就是php://filter协议。php://filter协议的工作原理是通过特定的过滤器对文件内容进行处理。举个例子当我们需要读取PHP文件源码时常规的文件包含会直接执行该PHP文件而通过base64编码过滤器我们可以将文件内容以base64编码形式输出这就相当于把源代码拍照而不是执行它。2. 漏洞利用场景分析让我们深入分析一个典型的利用场景。假设我们有一个页面index.php它的部分源码如下?php $language $_COOKIE[language]; include($language . .php); ?这段代码看起来简单却隐藏着严重的安全风险。开发者本意是通过cookie中的language参数加载对应的语言文件比如languageen会加载en.php。但攻击者可以构造恶意的language值比如php://filter/convert.base64-encode/resource/etc/passwd这样实际加载的就是php://filter/convert.base64-encode/resource/etc/passwd.php但过滤器会先对/etc/passwd文件进行base64编码输出。我在实际测试中发现即使目标文件不存在.php扩展名只要路径正确依然可以读取到内容。这种漏洞的利用效果取决于服务器的配置和环境。在Linux系统中常见的敏感文件包括/etc/passwd系统用户信息/etc/shadow用户密码哈希~/.bash_history命令历史记录网站配置文件如config.php3. 构造有效payload的技巧构造有效的payload需要考虑多个因素。首先是确定文件路径这通常需要一些信息收集工作。在我遇到的一个实际案例中通过页面错误信息泄露了网站的绝对路径/var/www/html/这为后续利用提供了关键信息。一个典型的payload构造过程如下确定目标文件路径比如/var/www/html/flag.php选择适当的伪协议和过滤器常用php://filter/convert.base64-encode/resource考虑文件扩展名处理如果代码会自动添加.phppayload中就要省略这个扩展名URL编码处理特殊字符可能需要编码# 使用curl发送恶意cookie的示例 curl -b languagephp://filter/convert.base64-encode/resource/var/www/html/flag http://target.com/index.php在实际操作中我经常遇到过滤器链的使用。PHP允许将多个过滤器串联起来比如同时进行base64编码和字符串旋转。这种高级用法可以绕过某些简单的安全检测。4. 防御措施与安全建议作为开发者如何防范这类漏洞呢我从防御角度总结了几点经验首先最根本的方法是避免使用动态文件包含或者至少对输入参数进行严格过滤。比如可以建立一个白名单只允许包含特定的文件$allowed_languages [en, zh, fr]; if(in_array($_COOKIE[language], $allowed_languages)) { include($_COOKIE[language] . .php); } else { include(en.php); // 默认语言 }其次可以设置open_basedir限制PHP脚本只能访问指定目录下的文件。在php.ini中配置open_basedir /var/www/html/另外及时更新PHP版本也很重要。新版本通常会修复一些伪协议相关的安全问题。我在生产环境中就遇到过因为PHP版本过旧导致的类似漏洞。最后对于必须使用动态包含的场景可以考虑使用realpath()函数检查文件路径确保不会包含到非预期的目录$file realpath($_COOKIE[language] . .php); if(strpos($file, /var/www/html/) 0) { include($file); }5. 实战案例分析让我们分析一个完整的CTF题目利用过程。题目提供了一个简单的页面显示当前语言并提示flag在flag.php中。首先查看页面源码发现以下关键代码?php $lan $_COOKIE[language] ?? en; include($lan . .php); echo Current language: . $lan; ?通过这个代码可以确认language参数通过cookie控制会自动添加.php扩展名没有对输入做任何过滤接下来构造payload由于会自动添加.php我们不需要在payload中包含这个扩展名。使用burpsuite拦截请求修改cookieCookie: languagephp://filter/convert.base64-encode/resource/var/www/html/flag服务器响应返回了一大串base64编码的数据解码后得到flag.php的源代码?php $flag CTF{php_filter_is_powerful}; ?这个案例展示了从发现漏洞到实际利用的完整过程。关键在于理解代码如何处理输入以及如何构造适当的伪协议payload。6. 高级利用技巧除了基本的文件读取PHP伪协议还能实现更多高级利用。比如在某些情况下我们可以利用php://input伪协议执行任意代码。这需要allow_url_include设置为On虽然这种情况较少见但在一些配置不当的服务器上仍然可能遇到。另一个有趣的技巧是使用压缩过滤器。当服务器禁止直接读取某些文件时可以尝试php://filter/zlib.deflate/convert.base64-encode/resource/etc/passwd这样返回的数据是经过压缩后再base64编码的可以绕过一些简单的关键词检测。我在一次渗透测试中还成功使用了string.rot13过滤器。这个过滤器会对文件内容进行ROT13编码虽然看起来没什么用但当服务器对输出有特殊处理时可以作为一种备选方案php://filter/string.rot13/resource/var/www/html/config.php7. 不同PHP版本的差异不同PHP版本对伪协议的支持和处理有所差异这也是实际利用中需要注意的。比如在PHP 5.6之前php://filter的某些过滤器行为与后续版本不同。一个常见的版本差异是对过滤器链长度的限制。在较新版本中过长的过滤器链可能会导致错误。我曾经遇到一个案例在PHP 7.2上能正常工作的payload在PHP 7.4上却失败了就是因为这个原因。另一个重要区别是某些过滤器在新版本中被移除或修改。比如convert.iconv.*过滤器在不同版本中的可用字符集可能不同。在实际利用前最好先确认目标PHP版本可以通过读取phpinfo()或查看HTTP响应头中的X-Powered-By信息。8. 日志文件与临时文件利用除了直接读取目标文件PHP伪协议还可以用来读取服务器日志或临时文件这为攻击提供了更多可能性。比如如果知道网站的访问日志路径可以尝试读取php://filter/convert.base64-encode/resource/var/log/apache2/access.log通过日志文件有时能发现其他有用的信息或漏洞。临时文件也是潜在的敏感信息源特别是在文件上传功能中即使上传失败临时文件可能仍然存在一段时间。我在一次安全评估中就是通过读取临时文件获得了数据库凭据。当时网站有一个上传功能虽然对文件类型做了检查但错误信息中泄露了临时文件的路径通过伪协议成功读取到了这个临时文件内容。