☰
DVWA文件包含漏洞实战:从LFI到日志投毒完整解析
2026/10/20 14:11:19 网站建设 项目流程

做Web安全测试,绕不开DVWA。它不是靠背题就能吃透的靶机,尤其文件包含这道题,很多新手三分钟看完源码、三小时解不出一道题,然后开始怀疑自己适不适合干安全。我当年也在这个页面上磨过很久,后来反复对比低中高三关的源码才想明白:文件包含的核心其实只有一句话,include就是“把另一个文件的内容塞进来执行”,而所有攻击与绕过方式,都是围绕“谁能决定塞哪个文件”展开的。

这里我就以DVWA靶机上的文件包含漏洞为例,把低级、中级、高级测试完整走一遍,并把每一步背后的绕过逻辑讲清楚。零基础可以照着顺序做,老手也可以用来查漏补缺。先收藏再对照着做,问题是真能解决的。

1. 为什么选DVWA练文件包含:从零搭建靶场环境

1.1 DVWA是什么,文件包含页面长什么样

DVWA是一套用PHP编写的开源漏洞练习靶机,专门把常见Web漏洞做成一个个可交互的实验模块。File Inclusion模块就是我们要打的那个页面,它模拟了一个典型的动态包含场景:页面通过page参数切换要显示的文件,默认提供include.php、file1.php、file2.php三个文件。

当你把page参数从include.php换成file1.php,页面内容会随之变化,看起来就是一个正常的文件切换功能。新手很难意识到问题出在哪,因为“参数可控”这件事太隐蔽了。直到你点开页面右下角的View Source,看到$_GET['page']被直接交给include函数,才会发现:用户传什么路径,服务器就包含什么文件,没有做任何校验。

DVWA每个漏洞模块都分了四个等级:Low、Medium、High、Impossible。Low到High是三种不同强度的缺陷写法,Impossible是官方实现的安全修复版,用来做对比。这也是我推荐用DVWA练手的原因,你能在同一个漏洞里看到“从最脆弱到逐步加固”的变化过程,这才是真实业务修复需要的思路。

1.2 本地靶场搭建的三种常用方式

第一种,Docker一键起靶机。本机有Docker的话,拉一个社区维护的DVWA镜像,把80或8080端口映射出来,访问首页就能进入操作界面。这种方式最省事,不到五分钟就能跑起来,缺点是日志路径和镜像内部结构跟手工搭建的环境略有差异,后面做高级测试时要注意。

第二种,本地集成环境部署。我最早用的是XAMPP这类集成环境,把DVWA源码解压到网站根目录,启动Apache和MySQL,访问/setup.php完成数据库初始化,然后用默认账号admin、默认密码password登录。这种方式更接近真实服务器结构,日志路径清晰,适合想深入理解文件包含逻辑的人。

第三种,手动部署到已有PHP环境。本质上跟第二种一样,只是需要自己改数据库配置、调整目录权限。对零基础来说,建议直接用第二种,可控性最强,出问题也好排查。

需要提醒的是,老版本DVWA在高版本PHP下有时会弹出兼容性警告,但只要主页面能正常加载,基本不影响漏洞测试。遇到奇怪现象,先看PHP错误提示,再怀疑代码缺陷。

1.3 测试前必须知道的三条边界

这里必须先说清楚三件事。第一,这套靶机只能在你自己的部署环境或获得书面授权的测试环境里操作,用同样的手法去测未经授权的线上系统,性质完全不同,搞安全的人要有这条底线。

第二,每次测试前,先到DVWA的Security页面把安全等级切到对应难度。文件包含这道题不会自动跟随你的预期变化,很多人在Low难度下试了半天没结果,最后发现当前等级还停在Medium,纯属浪费时间。

第三,准备好Burp Suite或者浏览器开发者工具。文件包含测试很多时候需要在发往服务器的原始请求里改参数,依赖Burp这类工具才能准确控制编码后的内容,只靠地址栏改参会被浏览器自动处理,结果有偏差。

2. 文件包含漏洞原理:把include机制讲透

2.1 include在PHP里到底做了什么

PHP里的include、include_once、require、require_once,核心动作都是同一个:读取指定文件的内容,并按PHP语法解析执行。说白了,就是把另一个文件“拼”进来一起运行。打个比方,你写文档时插入一个子文档,子文档里的内容会直接进入主文档;PHP的include就是干这件事的,而且它更彻底,不仅“插入”内容,还会“执行”内容。

所以漏洞能不能打,取决于一个关键问题:include后面跟着的文件路径,是开发者写死的,还是用户传进来的。如果是用户可控的路径,那用户就可以让服务器去包含任意文件,读源码、读系统文件、执行代码,全都有可能。

一个常见的危险写法长这样:

<?php $page = $_GET['page']; include($page); ?>

用户请求page=/etc/passwd,服务器就会把这个路径交给include,把系统用户文件读出来。DVWA的Low级别就是这个玩法,简直是把漏洞摆在你面前。

2.2 LFI与RFI的分界线

文件包含漏洞通常分两类。本地文件包含,也就是LFI,指的是只包含服务器本机已有的文件;远程文件包含,也就是RFI,指的是可以包含远程服务器上的文件,比如让目标服务器去加载你控制的URL。

两者的分界线在于PHP的配置项allow_url_include。PHP 5.2以后默认是关闭的,如果没开,远程URL包含会直接报错。本地包含不受这个限制,所以LFI在实际场景里出现频率远高于RFI。另一个容易混淆的点是伪协议,比如php://filter,它属于PHP内置的流包装器,即使allow_url_include关闭也能用,这也是为什么读源码常常优先选它而不选远程包含。

2.3 为什么审计源码比盲打更重要

DVWA每个难度都提供了源码查看入口,这是最大的学习便利。做题的第一步永远是点开View Source看服务端逻辑,而不是直接开始试各种Payload。Low级别的源码可能只有一行include,Medium级别出现了字符串替换,High级别换成了白名单校验。看懂每层过滤做了什么,才能精准设计绕过方案。

盲打不是不可以,但效率太低,而且你看不到失败背后的原因。先审计源码,等于先拿到地图再走迷宫,路会清楚很多。

3. Low难度实测:无过滤下的任意文件读取

3.1 看源码确认漏洞触发点

先把DVWA Security里的等级切到Low,再进入File Inclusion页面,点击View Source。低级别源码非常直接,过滤代码基本就是没有,page参数从请求里取得后,原封不动传给include。

我每次带人做这一步,都会让他停下来多看一分钟。不是让你欣赏这个漏洞,而是让你记住漏洞的“原始形态”:没有任何防护时,攻击面就是参数本身。这也解释了为什么后面的修复都围绕参数做文章。

3.2 三个必测Payload与预期结果

第一个必测,绝对路径文件读取。在page参数里传Linux的系统用户文件:

page=/etc/passwd

页面底部会直接回显文件内容。看到passwd内容,就证明任意文件读取已经成立。

第二个必测,目录遍历。如果当前页面位于/vulnerabilities/fi/这样的子目录,可以用../往上跳:

page=../../../etc/passwd

本质上跟绝对路径一样能读到内容,但这个思路在现实环境里更常见,因为很多应用是基于相对路径做包含的。注意层级要够,不够会出现路径解析到不存在的目录,页面空白。

第三个必测,PHP伪协议读源码。因为include会执行PHP文件,直接包含一个正常PHP文件时,页面只会显示执行结果,看不到源代码。要想读源码,得让文件内容先以base64形式输出:

page=php://filter/read=convert.base64-encode/resource=config.php

页面会返回一段base64编码,拿来解码就能看到config.php的完整内容。这个技巧在真实代码审计里非常实用,很多敏感配置就是这么泄露的。

预期结果汇总一下:绝对路径读取确认“能不能读”,目录遍历确认“路径拼接规则”,伪协议读源码确认“能不能绕过include的执行限制”。这三步走完,Low级别就算通关了。

3.3 从读取到执行:文件包含配合日志投毒

任意文件读取已经很严重,但文件包含的真正威力在于能变成代码执行。Low级别下最经典的路径是包含日志文件。

测试思路是先确认日志路径可读。Linux下Apache的访问日志通常在这个位置:

page=/var/log/apache2/access.log

如果能看到日志内容,说明服务器日志对Web服务用户可读。然后你构造一个请求,在User-Agent里写入一段PHP代码,发送给服务器。因为Apache会把User-Agent原样记录到access.log,这段PHP代码就被写进了日志文件。下一步再通过文件包含去读这个日志,include会执行其中的PHP代码,代码执行就成立了。

DVWA在Low级别下把读取和执行的链条完整演示了出来。搞懂这个链条,再去看高级难度会轻松很多。

3.4 Low难度修复思路

Low级别修复其实一句话:不要直接用用户参数当文件路径。最省事的方法是白名单,固定允许包含几个文件:

<?php $allowed = array('include.php', 'file1.php', 'file2.php'); if (in_array($_GET['page'], $allowed)) { include($_GET['page']); } else { exit('非法输入'); } ?>

黑名单过滤在这里基本没有意义,因为黑名单永远有绕过空间。看到这里,你也应该理解为什么DVWA会把Low定义成“零防护”:它存在的意义就是让你直观地看到问题的根因。

4. Medium难度实测:路径过滤的三种绕过思路

4.1 先看源码再谈绕过

切到Medium等级后再次打开View Source,会发现代码里加了两行字符串替换:一类是把http://、https://替换掉,另一类是把../和..\替换为空。目的很明确,既要阻止远程包含,也要阻止目录穿越。

按我之前说的流程,先不要急着传Payload,仔细想一下这两个替换到底挡了什么。挡了远程URL开头,挡了相对路径里的上级目录记号。接下来所有绕过动作,都是围绕“让过滤条件失效”来设计。

4.2 绕过思路一:双重编码,让过滤落空

既然../会被替换为空,那能不能在替换一次之后,剩下的路径仍然被服务器识别成../?这里就是经典的双重编码思路。

假设你传....//,代码执行第一次替换,把中间的../删掉,剩下../。过滤器只执行一次,不会再去检查替换后的结果,于是剩下的../被原样交给include。服务器解析路径时,它看到的就是两个点加斜杠,目录穿越照样成功。

用参数表示就是:

page=....//....//....//etc/passwd

替换完就变成../../../etc/passwd。这个方法对中等难度的字符串替换特别有效,原理就是“你过滤一次,我就藏一层”。

这里有个实操细节:浏览器地址栏有时会自动对特殊字符做编码或解码,直接输入可能得不到预期效果。建议用Burp Suite的Repeater模块发原始请求,确保到达服务端的确实是....//而不是被提前处理过的内容。

4.3 绕过思路二:绝对路径甩开相对路径过滤

换一个角度想,代码过滤的是../,而我只是想去读一个绝对路径,那路径里根本没有../,过滤规则自然就失效了。

传这个参数即可:

page=/etc/passwd

这个思路看起来很笨,但实用程度超出很多人的预期。真实业务里,开发者往往只盯着相对路径穿越做防护,忽略了绝对路径包含同样危险。做安全测试时不要被“绕过过滤”四个字框住思维,有时候直接换个上下文就解决问题了。

同理,Windows环境下的绝对路径也有效,比如C:\Windows\system.ini。中等难度的过滤只针对..\,不针对盘符开头的绝对路径。

4.4 绕过思路三:大小写与伪协议混用

如果源码用的是str_replace做字符串替换,它默认区分大小写。那么传HTTP://或者HtTp://,只要原始代码只精确匹配了小写的http://,大小写变形就能绕过。

不过这种绕过方式依赖具体实现,有些版本已经换成不区分大小写的正则替换,那就无效了。判断方法很简单:传一个变形后的URL,看服务器有没有尝试去请求它,如果报错信息里出现了远程请求痕迹,说明绕过成功。

另外,php://filter伪协议也要顺手试一下。它不依赖http://开头,也不涉及../,中等难度下经常可以直接用:

page=php://filter/read=convert.base64-encode/resource=config.php

我在本地测试中,中等难度用伪协议读源码的成功率很高,原理跟Low差不多,因为这类过滤往往没有覆盖本地流包装器。

4.5 Medium修复要点

中等难度的修复不能再靠黑名单了。正确做法是白名单加路径规范化。拿到参数后先做realpath,把相对路径和符号链接都解析成真实路径,再检查这个真实路径是否在允许目录内。

<?php $base = realpath(dirname(__FILE__)); $path = realpath($_GET['page']); if ($path && strpos($path, $base) === 0) { include($path); } else { exit('非法路径'); } ?>

strpos判断前缀的方式存在一点小坑,比如目录名重叠问题,更严谨的做法是统一加目录分隔符再比较。但思路是对的:从“堵几个特征”变成“限制允许范围”。

5. High难度实测:白名单校验与日志投毒

5.1 高级过滤到底做了什么

High级别切过来,再看源码,思路完全变了。代码不再是黑名单替换,而是直接做白名单判断:page参数只能是include.php,其他任何文件都会返回错误。少了那些眼花缭乱的替换规则,反而让很多新手无从下手,因为常规的../、绝对路径、伪协议全部失效。

这个设计很值得品味。前两个等级还在教你绕过,到了高级等级,它开始教你“强硬限制”的效果:只允许一个文件名,攻击面被压缩到极小。但安全测试要做的不只是防守视角,还得思考这个白名单能不能被绕过。

5.2 绕过白名单的两条真实路径

白名单限制的是“include函数最终加载的文件名”,但它限制不了“那个文件的内容来自哪里”。如果服务器上存在一个内容可控的文件,而它恰好不在白名单限制范围外?换个说法:白名单只允许include.php,那我能不能找另一个“内容由我控制但路径合法”的文件,让它被include时执行我要的代码?

第一条路径是日志投毒。Apache访问日志会记录请求的User-Agent,如果我把它写成PHP代码,日志文件本身不叫include.php,不能直接包含。但DVWA的高等级并没有把日志文件加入白名单,问题就出在这:实际的过滤只有文件名对比,并没有限制包含文件必须位于应用目录内。只要找到一个内容可控的文件,白名单就形同虚设。

第二条路径是上传文件马。如果应用里有文件上传功能,且上传后的文件保存在Web目录,那上传一个内容包含PHP代码的图片,再用文件包含去包含这个图片,就能触发执行。DVWA的File Inclusion模块本身不带上传点,但思路要记着,这套组合拳在真实业务里非常常见。

5.3 日志投毒的几步实操

只在自己的DVWA靶机里操作。先确认目标环境是Linux加Apache,然后尝试包含访问日志,看是否能读到日志内容:

page=/var/log/apache2/access.log

能读到日志,就可以进行下一步。用Burp Suite构造一个请求,在User-Agent里写入PHP代码:

GET /dvwa/vulnerabilities/fi/?page=/var/log/apache2/access.log HTTP/1.1 Host: 127.0.0.1 User-Agent: <?php phpinfo(); ?>

这里有个实操细节:如果你把包含日志的请求本身也带上恶意User-Agent,那么日志里会同时记录多条带PHP代码的请求,包含时PHP代码会被多次执行,输出非常杂乱。更干净的做法是分两步走。

第一步,用Burp发送一个访问不存在路径的请求,比如GET /hacktest.php,User-Agent写入那串PHP代码。这个请求会被Apache记录,日志里就多了一行带PHP代码的记录。

第二步,用正常的浏览器或Burp重新打开文件包含页面,传参page=/var/log/apache2/access.log。include读到日志,日志里的User-Agent内容被当成PHP执行,页面出现phpinfo的输出。

到这里,文件包含已经从“读文件”升级为“执行代码”。如果你想继续验证命令执行,可以把User-Agent里的PHP代码换成命令执行语句,在后续请求中通过参数传命令。这个测试我只建议在靶机里做,别动真实系统。

日志路径在不同环境差别很大,Windows下XAMPP常见路径是C:\xampp\apache\logs\access.log,Docker里可能是/var/log/apache2/access.log。判断方法很简单,先用绝对路径读取逐个试,能读出日志内容就停下。

5.4 High级别修复建议

White名单做得更彻底一点,不只比较文件名,还要做路径校验。把所有允许包含的文件放到一个固定目录,参数只接收文件名,不允许带路径分隔符。include之前先拼上固定目录前缀:

<?php $file = basename($_GET['page']); $path = __DIR__ . '/pages/' . $file; if (is_file($path)) { include($path); } else { exit('文件不存在'); } ?>

这样即使日志可读,因为日志不在pages/目录下,也无法被包含。更进一步,生产环境直接不要用动态include做页面切换,改用框架的路由分发或模板渲染机制,从设计上消灭这个问题。危险配置面也要缩小:allow_url_include保持关闭,php://这类不需要的伪协议能限制就限制。

6. 测试过程中最常见的坑与排查顺序

6.1 包含之后白屏、下载与乱码

文件包含测试最常见的表现是白屏。白屏通常意味着include执行了,但目标文件路径不对,或者目标文件内容被PHP解析后没有任何输出。先检查路径层级够不够,再多试几个绝对路径,排除路径问题。

第二个表现是下载。服务器把目标文件当成普通文件直接返回下载,这往往说明中间层配置有问题,比如把动态请求交给了静态处理,不是DVWA默认场景。

第三种表现是乱码。读取二进制文件或非文本内容时出现乱码是正常的,比如包含图片文件时,页面会出现二进制内容;如果包含的是UTF-8文本反而显示正常。乱码本身说明可读,但要进一步判断是否能执行,还是需要回到内容可控这个前提上。

6.2 浏览器自动解码导致的绕过失败

用地址栏直接测试编码后的路径,是Medium难度最常踩的坑。浏览器看到%2e%2e%2f会自动解码成../,然后这个../先被服务器端的过滤规则命中,替换为空,导致绕过失败。

解决方法是绕开浏览器这道工序。打开Burp Suite的Repeater,手动构造原始请求,把参数值设置成不经过URL解码的原始字符串。这样到达服务端的才是你真正想发送的内容,结果才可信。这个小问题在Low级别不明显,到Medium级别经常会让人怀疑人生,排查时先确认你看到的请求和到达服务端的请求是否一致。

6.3 日志路径因环境而异

High级别的日志投毒,很多人卡在“日志到底在哪”。同一个DVWA,Windows集成环境、Linux原生Apache、Docker容器,日志路径各不一样,甚至Apache和Nginx的路径也完全不同。

常见的几个位置:

环境常见路径
Linux Apache/var/log/apache2/access.log 或 /var/log/httpd/access_log
Windows XAMPPC:\xampp\apache\logs\access.log
Linux Nginx/var/log/nginx/access.log

如果都不对,可以先利用LFI去读配置文件,比如Apache的/etc/apache2/apache2.conf或Nginx的/etc/nginx/nginx.conf,从配置里找日志路径。靶机环境下允许这种“用漏洞打漏洞”的调研方式,真实环境中也要先摸清目标类型。

6.4 一套固定的排错顺序

我自己的排查顺序是固定的:先看源码,确认过滤逻辑;再判断服务器操作系统和Web中间件;然后测绝对路径与伪协议,确认基础可用性;最后才进入日志投毒等复杂利用。每失败一次,不要急着换Payload,先回退到源码层面想一想“这个过滤规则为什么能拦住我”,往往绕过的答案就藏在问题里。

7. 从靶场走向防护:文件包含的防御与检测

7.1 代码层修复三件套

代码修复按照优先级排,第一是白名单加精确匹配,第二是路径规范化和目录锁定,第三是关闭不必要的危险配置。这三层同时做,才能基本堵住文件包含的常见利用路径。

白名单不要只比对文件名,因为文件名可以被伪造,更可靠的是通过realpath拿到文件的真实物理路径,再判断它是否位于允许目录内。危险配置方面,allow_url_include和allow_url_fopen在不必要时保持关闭,能挡住大批RFI和伪协议尝试。

7.2 产品层检测与审计建议

只剩代码修复还不够,线上环境还需要检测手段。WAF可以对page=参数里的目录穿越特征、php://伪协议、对/etc/passwd和日志路径的敏感引用做拦截,但WAF只是兜底,不能替代修复。

日志审计要重点盯两类请求:一类包含读取系统文件的路径特征,另一类是请求参数里出现php://、data://等伪协议关键字。看到这种请求不要只当攻击拦截,要顺藤摸瓜排查是不是已经有文件被成功包含过,确认攻击是否已经发生了代码执行。渗透测试报告里,文件包含漏洞不要只写“任意文件读取”,一定要验证是否可复用为日志投毒或上传文件组合,升级成RCE后再定风险等级,危害完全不同。

7.3 三个难度与防护方案速查表

难度过滤方式主要绕过思路对应防护
Low无过滤绝对路径读取、伪协议、日志包含白名单文件列表
Medium替换http(s)和../双重编码、绝对路径、大小写变形白名单加路径规范化
High白名单限文件名日志投毒、可控文件执行固定目录加路径校验,关闭伪协议
Impossiblerealpath加白名单常规方式无效参考官方源码实现

8. 写在最后:我的实操体会与后续练习建议

8.1 先读源码再做测试的顺序感

回头看这套DVWA三关测试,我踩过最值得说的坑有两处。第一处是Medium难度,在浏览器地址栏直接测编码路径,被浏览器自动解码后导致过滤命中,白白浪费时间;直到改用Burp才发现问题根本不在服务端。第二处是High难度,一开始觉得白名单无解,后来意识到“include只能包含服务器能读的文件,那为什么不找一个内容可控的文件来包含”,思路一下子打开了。这两个坑其实指向同一件事:带着顺序去测试,比背Payload重要得多。

8.2 下一步可以拓展的方向

做完这份测试,建议再去源码里看一眼Impossible级别的实现,它会把白名单和路径校验结合在一起,那种简洁感比任何绕过技巧都更能说明问题。想继续加深理解,可以搭一个Nginx日志版本的靶机再打一遍,换掉中间件往往就得换绕过思路。还能在自建靶机里配合一个文件上传点,把“上传图片马加文件包含执行”的完整攻击链走通。

文件包含这个漏洞真正考验的不是记忆,而是理解“include到底做了什么”以及“谁能控制路径”这两件事。把这两点吃透,无论过滤规则怎么换,绕过的路总是能找到的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询