攻防世界20-warmup-CTFWeb

看源码,提示source.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
highlight_file(__FILE__);
class emmm {
public static function checkFile(&$page) {
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_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'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

代码审计

预定义的 $_REQUEST 变量包含了 $_GET、$_POST 和 $_COOKIE 的内容。

$_REQUEST 变量可用来收集通过 GET 和 POST 方法发送的表单数据。

$_REQUEST 变量既可以收集GET方法发送的数据,也可以接受POST方法接受的数据。

还提到了hint.php,可以看看,显示flag not here, and flag in ffffllllaaaagggg

$whitelist = [“source”=>”source.php”,”hint”=>”hint.php”]; //数组:索引=>字符串

再回到代码

  • 第一个if,判断page变量是否为空,是否是字符串

  • 第二个if,in_array($page, $whitelist):page参数要在这个whitelist里

  • 构造payload:?file=hint.php试试,

为什么 source.php?file=hint.php 不行?: 如果你只是传递 source.php?file=hint.php,那么白名单验证确实会通过,服务器也会包含 hint.php 文件。但这是题目设计的正常路径,而我们希望包含的是其他文件,比如 flag,所以需要利用路径拼接或截断的方式进行绕过。

注意到:include $_REQUEST[‘file’];,我们的目的不是只为了让他返回true,还要让他包含file(也就是Flag)

  • 第三个if 判断page是否在白名单上并且mb_substr函数还会截断page中问号前面的部分

    解释:

    mb_strpos($_page . ‘?’, ‘?’) #会查找解码后的字符串 $page 中第一个 ?出现的位置。

    • 例如,假如 $page'hint.php?flag',那么 mb_strpos($_page . '?', '?') 的结果是 8('?' 的位置)。

    • 然后 mb_substr($_page, 0, 8) 会从第 0 个字符开始,截取到第 8 个字符,结果是 'hint.php'

下面重要的来了,先要去了解include这个东西,可能比较抽象:

image-20241014104142458

关键是第三段,我在这细细解读:

1.绝对路径的解析:

如果你使用绝对路径来包含文件(例如,include("/path/to/file.php")),PHP 会直接忽略 include_path 的设定,它会根据你提供的路径在文件系统中寻找文件。

2.相对路径的解析:

在 PHP 中,相对路径一般以 .或..开头:

如果使用相对路径(如 include("../path/to/file.php")),PHP 会根据相对路径的层级去寻找文件。就像 .. 会跳出当前目录,去上一级目录寻找文件,而 . 表示在当前目录寻找。

==注意下一点==

假设include ‘file.php’;PHP 会依次在当前目录和 include_path 设定的路径中寻找 file.php 文件。

然而,如果你明确指定了文件的绝对路径相对路径(例如使用 ..//),**include_path 会被完全忽略**。PHP 只会根据你指定的路径寻找文件,而不会去 include_path 里找。

也就是说hint.php?/…/…/…/…/…/…/ffffllllaaaagggg,当它检测到了指定了相对路径,就会去相对路径,而忽略hint.php

hint.php?/…/…/…/…/…/…/ffffllllaaaagggg 这种情况下,由于路径中包含了 ../,PHP 解析时会将 ../ 识别为相对路径的跳跃符号,用来跳转到上一级目录。因此,include 函数会将 /…/…/…/…/…/…/ffffllllaaaagggg 作为文件路径的一部分去解析,而不会去寻找 hint.php

所以最终的payload为?file=hint.php?/…/…/…/…/…/…/ffffllllaaaagggg

注意上面应该是只有两个.的,最后可能复制下来有3个。

image-20241014110232735