CTFhub warmup过关攻略

我独自升级中
2026-10-03 / 0 评论 / 75 阅读 / 正在检测是否收录...

靶场地址

CTFhub → 历年真题 → Web → 2018-HCTF-Web-warmup

在题目列表里筛选 Web、难度困难,点进 warmup。

image.png

一句话漏洞点

checkFile() 校验的是 ? 截断后的字符串,include 用的却是原始字符串,校验与执行不一致 → 白名单绕过 + 目录穿越。

看页面源码:注释里留下了 source.php

view-source: 打开首页,第 10 行是 <!--source.php-->,这是审计入口。

image-2.png

访问 source.php:白名单只放行两个文件名

$whitelist = ["source"=>"source.php","hint"=>"hint.php"];

白名单里除了 source.php 还有 hint.php,而 hint 就是提示的意思 → 先去看 hint.php。

image-4.png

访问 hint.php,得到 flag 的文件名

flag not here, and flag in ffffllllaaaagggg

image-5.png

回来分析拦截逻辑:校验被 ? 截断,include 没截断

image-3.png

$_page = mb_substr($page, 0, mb_strpos($page . '?', '?'));   // 只取第一个 ? 之前的部分
if (in_array($_page, $whitelist)) { return true; }           // 校验的是这一段
...
include $_REQUEST['file'];                                    // 执行的是完整原始参数
  • mb_strpos($page . '?', '?'):在参数后补一个 ? 再找位置,所以一定能找到,返回值就是第一个 ? 的下标。
  • mb_substr($page, 0, 该下标):把 ? 及其后面全部砍掉,只拿前面送去比对。
  • 于是只要前半段是白名单文件名、? 后面接任意内容,校验就一定通过,而 include 照样带着后半段去加载文件。

构造 payload:前半段过校验,后半段穿越目录

http://challenge-055d9b46127e5bc0.sandbox.ctfhub.com:10800/source.php?file=source.php?../../../../../ffffllllaaaagggg

image-6.png

  • file=source.php?../../../../../ffffllllaaaagggg
  • 校验阶段:截到第一个 ? → source.php,命中白名单 → 返回 true;
  • 执行阶段:include 拿到完整字符串,../ 逐级上跳,命中上面从 hint.php 得到的文件名 ffffllllaaaagggg。

为什么是 ../../../../../ffffllllaaaagggg,而不是 ffffllllaaaagggg 或 ../ 开头

../ 不是装饰,它决定 include 最终去哪个目录找文件:

写法实际查找位置结果
ffffllllaaaagggg当前工作目录下,即 /var/www/html/ 里文件不在这,失败
../ffffllllaaaagggg只上跳 1 级(/var/www/)深度不够,失败
source.php?ffffllllaaaaggggsource.php?ffffllllaaaagggg 被当成目录名失败
../../../../../ffffllllaaaagggg先上跳 5 级再找成功

原因有三个:

  1. flag 文件不在网站根目录。hint.php 只说"flag in ffffllllaaaagggg",没给路径;直接请求 http://.../ffffllllaaaagggg 报 403 Forbidden,说明它位于 Web 根目录之外,必须靠目录穿越走出去。
  2. ../ 的层数是"猜深度",不是精确指定。../ 只能上一级,而当前工作目录的绝对路径并不知道;../../../../../ 是上跳够多次直到文件系统根 /,之后 /ffffllllaaaagggg 就是绝对路径。层数取多不取少——上跳超过根目录后仍停在 /,所以多写几层不会出错,5~7 层是覆盖常见镜像深度的通用写法。
  3. 前面的 source.php? 会变成一个不存在的目录名。因为校验要求开头必须是白名单文件名,payload 前半段被钉死成 source.php?,它在路径里等同于一个目录;这更说明必须依赖"上跳够多次回到根"来保证最终命中。

一句话概括:ffffllllaaaagggg 来自 hint.php(信息),../ 负责把它从"目录未知"变成"绝对路径可达"(利用),两者缺一不可。

拿到 flag

ctfhub{5517caa381efd9d6375a9a33}

校验与使用不一致是什么

漏洞根子是检查的字符串和执行时用的字符串不是同一个:校验前先按 ? 截断(只看文件名),执行时却把原始参数整串交给 include(路径部分照样生效)。判断特征很好认——源码里出现"截断后再 in_array 白名单"的写法,基本都能这样绕。

修复方式:校验和使用同一个变量,用 basename() 取纯文件名与白名单精确比对,再用 realpath() 把结果限制在指定目录内,不要依赖 ? 截断做安全判断。

补充

  • 截断条件不限于 ?:urldecode 之后还会再截一次,所以传 source.php%3f../../.. 也能过校验,效果相同。
  • 若白名单里没有 hint.php,就得另想办法猜文件名,这道题的 hint.php 相当于直接给了答案的一半。
0

评论 (0)

取消