DVWA靶场通关笔记-文件包含(Impossible级别)
目录
一、源码分析
二、文件包含防范分析
1、明确指定允许包含的文件
2、拒绝所有未在白名单中的输入
3、总结
(1)白名单 (Allow List)
(2)硬编码/映射 (Hardcoding/Mapping)
(3)输入过滤 (Input Filtering)
(4)服务器配置防范 (Server Configuration)
本系列为通过《DVWA靶场通关笔记》的文件包含关卡(low,medium,high,impossible共4关)渗透集合,通过对相应关卡源码的代码审计找到讲解渗透原理并进行渗透实践。本文为文件包含 impossible关卡的原理分析部分,讲解相对于low、medium和high级别,为何对其进行渗透测试是Impossible的。
一、源码分析
进入DVWA靶场文件包含的源码目录,找到impossible.php源码,分析其为何能让这一关卡名为不可能实现命令执行渗透。
打开impossible.php文件,对其进行代码审计,这段PHP源码旨在通过一个简陋的白名单(检查page
参数是否为include.php
或file1-3.php
)来限制文件包含,但其并未实际使用include
函数,因此本身不构成安全风险,只是一个无效示例。真正防范文件包含安全风险的核心策略是采用严格的白名单机制,只允许包含预先定义的合法文件,并彻底避免用户输入直接映射到文件系统路径,同时应关闭allow_url_include
禁止远程文件包含,从而从根本上杜绝任意文件读取或代码执行的风险。
-
功能目的:根据用户通过
?page=
参数传入的值,来动态包含并显示相应的页面内容。 -
预期安全措施:它试图通过一个
if
条件语句来限制只能包含include.php
,file1.php
,file2.php
,file3.php
这四个文件。
详细注释后的impossible.php源码如下所示,主要实现严格白名单控制的文件包含功能。
<?php
// 从GET请求参数中获取名为"page"的参数值,并将其赋值给变量$file,
// 该变量预期表示要显示的页面文件名
$file = $_GET['page'];
// 进行输入验证,检查获取到的文件名是否符合要求,
// 仅允许文件名是"include.php"或者"file1.php"或者"file2.php"或者"file3.php"
// 如果文件名不符合这些条件
if ($file!= "include.php" && $file!= "file1.php" && $file!= "file2.php" && $file!= "file3.php") { // 输出错误信息,提示文件未找到echo "ERROR: File not found!"; // 终止脚本的执行,防止后续可能的错误操作exit;
}
// 代码执行到此处,说明$file变量的值是符合要求的文件名,
// 后续可能会根据该文件名进行文件包含等相关操作(但在当前代码中未体现)
?>
其中index.php文件中对文件包含逻辑的处理如下所示,这里直接包含一个名为 $file
的变量。注意,这个 $file
变量不是在index.php本地设置的,它来自之前通过 require_once
引入的 {low/medium/high/impossible}.php
文件。在这些文件中,$file
被设置为用户可控的输入,例如 $file = $_GET['page'];
。
<?php
// 初始化不同风险级别对应的文件名变量
$vulnerabilityFile = '';// 根据用户Cookie中的'security'值,决定使用哪个级别的处理文件
switch( $_COOKIE[ 'security' ] ) {case 'low':// 当安全级别为low时,使用low.php(低安全级别,几乎无防护)$vulnerabilityFile = 'low.php';break;// ... medium, high ... (省略的部分类似,分别对应medium.php和high.php,安全级别递增)default:// 默认情况下(如未设置或无效的security值),使用impossible.php(最高安全级别,完全防护)$vulnerabilityFile = 'impossible.php';break;
}// 引入对应安全级别的处理文件(该文件会定义$file变量,用于后续文件包含)
require_once DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/fi/source/{$vulnerabilityFile}";// ... (省略其他业务逻辑)// 检查$file变量是否已被定义(通常由上面引入的对应安全级别的处理文件定义)
if( isset( $file ) )// 包含$file对应的文件(核心风险点,具体风险由引入的处理文件对$file的处理逻辑决定)include( $file );
else {// 若$file未定义,重定向到默认页面include.phpheader( 'Location:?page=include.php' );exit;
}
?>
-
安全级别控制:通过 Cookie 中的
security
值动态选择不同的安全级别的处理文件(low.php/medium.php/high.php/impossible.php),实现不同安全策略的切换。 -
文件引入机制:不同安全级别的文件(如low.php)会对用户输入的
page
参数(通过$_GET['page']
获取)进行不同处理,最终生成$file
变量。low.php
直接将$_GET['page']
赋值给$file
(无防护);impossible.php则通过白名单严格限制$file
的取值。 -
风险点:
include( $file )
的安全性完全依赖于前面引入的安全级别处理文件对$file
的过滤逻辑:- 低级别(如
low.php
)可能直接使用用户输入,导致文件包含安全风险(LFI/RFI)。 - 不可能级别(如impossible.php)通过严格白名单验证确保
$file
为合法值,避免风险。
- 低级别(如
二、文件包含防范分析
这段 PHP 代码采取了严格的白名单验证机制,具体核心逻辑是直接限定允许包含的文件名,仅接受预设的合法文件,从而彻底堵死恶意文件包含的可能性。主要功能是通过获取 GET 参数page
的值来确定要包含的文件。代码中对page
参数进行了校验,仅允许其值为include.php
、file1.php
、file2.php
或file3.php
,若不符合规则则输出错误信息并退出。
- 明确指定允许包含的文件
- 拒绝所有未在白名单中的输入
1、明确指定允许包含的文件
核心原因在于,通过硬编码方式,仅允许包含 include.php、file1.php、file2.php、file3.php 这四个文件,其他任何文件都被拒绝
// 仅允许包含指定的合法文件
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {echo "ERROR: File not found!";exit;
}
2、拒绝所有未在白名单中的输入
无论用户传入的 page
参数值是什么(如恶意路径、远程文件地址等),只要不在白名单内,直接返回错误并终止执行,避免了目录遍历(如 ../
)、远程文件包含(如 http://
)等攻击。
- 合法请求:若传入
?page=file1.php
,由于 file1.php 在白名单中,会正常包含该文件。 - 恶意请求 1(目录遍历):若传入
?page=../../etc/passwd
,因不在白名单内,会被拒绝并提示 “ERROR: File not found!”。 - 恶意请求 2(远程文件包含):若传入
?page=http://xxx.com/shell.php
,因不在白名单内,同样被拒绝。 - 未授权本地文件:若传入
?page=file4.php
(即使该文件存在),因不在白名单中,也会被拦截。
3、总结
文件包含风险的防范方法主要包括白名单法、硬编码/映射法、输入过滤法、服务器配置防范,对比如下表所示。
方法 | 安全性 | 推荐度 | 说明 |
---|---|---|---|
白名单 | 极高 | ★★★★★ | 唯一允许用户输入的方法,只接受预定义值。 |
硬编码/映射 | 极高 | ★★★★★ | 完全避免用户输入,最安全。 |
输入过滤 | 中~低 | ★★☆☆☆ | 难以做到完美,存在被绕过的风险,不推荐。 |
服务器配置 | 高 | ★★★★☆ | 必须做的辅助措施,提供底层防护,但不能替代代码安全。 |
(1)白名单 (Allow List)
白名单机制通过预先定义一个绝对可信的允许文件列表来防御文件包含安全风险。开发者创建一个合法文件名的数组,在处理用户输入时,严格检查其值是否存在于该列表中。只有完全匹配的请求才会被放行,任何不在列表中的输入都会被立即拒绝并记录日志。这种方法的安全性极高,因为它从根本上切断了用户输入与文件系统的直接联系,攻击者无法通过构造特殊路径或文件名进行绕过。虽然维护列表需要一定成本,但其提供的安全保障使得它成为大多数场景下的首选方案,是实现安全文件包含的最佳实践之一。
(2)硬编码/映射 (Hardcoding/Mapping)
硬编码或映射是安全性最高的防御方法,它完全消除了用户对文件路径的控制。开发者通过switch-case语句或键值对数组,将用户提供的抽象标识符(如"home"、"about")在服务器端映射到固定的真实文件路径。用户输入仅作为选择器,绝不参与路径拼接。这种方法彻底杜绝了文件包含风险,因为攻击者无法通过输入注入任何路径遍历字符或恶意URL。虽然缺乏灵活性,需要修改代码才能增加新页面,但其提供的绝对安全性使其成为控制面板、固定功能模块等场景的理想选择。
(3)输入过滤 (Input Filtering)
输入过滤是一种试图净化用户输入的防御方式,通过移除或替换危险字符(如../、..\、空字节%00)来降低风险。常见做法包括使用str_replace函数过滤目录遍历符、用basename函数剥离路径信息、或强制添加文件后缀。然而,这种方法安全性较差,被认为是一种不可靠的黑名单机制,复杂的过滤规则可能被双写编码(....//)或其他变形方式绕过。它只能作为辅助性的深度防御措施,绝不能作为主要的安全依赖,否则会给予开发者错误的安全感,导致严重的防护风险。
(4)服务器配置防范 (Server Configuration)
服务器配置防范通过修改运行环境参数来构建底层安全防线,属于纵深防御策略。关键措施包括:设置php.ini中的open_basedir限制PHP可访问的目录范围,防止横向移动;确保allow_url_include=Off彻底禁用远程文件包含功能;以最低权限原则运行Web服务进程。这些配置从系统层面限制了攻击的影响范围,即使应用存在安全风险也能有效遏制危害。虽然不能替代应用层修复,但作为基础安全基线必须实施,为整个服务器环境提供 essential 的保护层。