本文为记录个人信安小白的刷题路程,大佬勿喷,也同时希望文章能对您有所帮助

打开靶机,看到一个笑脸没有什么有用的信息,F12看看源码,

发现隐藏文件source.php,通过URL访问文件,

看到一大段源码,题目为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
48
49
 <?php
highlight_file(__FILE__); //会高亮显示当前PHP文件的源代码,显示方面
class emmm //定义一个类emmm
{
public static function checkFile(&$page) //emmm类中checkFile()方法,用于检查用户请求的文件是否合法
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//白名单设置,创建一个关联数组,白名单中值允许两个文件:"source.php" 和"hint.php"
if (! isset($page) || !is_string($page)) { //检查参数$page是否存在(isset($page))和是否为字符串类型(is_string(&page))
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) { //第一次白名单检查,直接检查整个$page是否在白名单($whitmlist数组)中
return true; //如果 $page 直接等于 source.php 或 hint.php,允许访问
}

$_page = mb_substr( //截取$page从开头到第一次出现?的位置,例如:$page="source.php?aaaa"$_page="source.php"
$page,
0,
mb_strpos($page . '?', '?') //mb_strpos() 查找字符串中第一次出现 ? 的位置
);
if (in_array($_page, $whitelist)) { //第二次白名单检查,截取?之前的部分($_page)检查是否在白名单中
return true;
}

$_page = urldecode($page); //URL解码后再次检查,防止URL编码绕过,检查逻辑和上面一样
$_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']) //获取用户通过URL传参(GET请求或POST表单)提交的file参数
&& is_string($_REQUEST['file']) //检查是否为字符串类型
&& emmm::checkFile($_REQUEST['file']) //调用之前定义的检查函数checkFile()
) {
include $_REQUEST['file']; //include 是 PHP 的文件包含函数,将指定文件的内容嵌入到当前脚本中执行,
exit; //如果用户导入的文件通过checkFile()函数,则执行文件中脚本
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";//未通过的话显示这张图片
}
?>

代码基本了解完毕,
主程序逻辑:
1.检查是否有file参数且为字符串,并通过checkFile()验证
2.如果验证通过,则包含该文件(include $_REQUEST[‘file’])
3.否则显示一张图片

构筑payload

我们要获得flag就必须利用这个脚本的漏洞去包含一个文件,但是那个文件是什么呢,回头看脚本中白名单whiteline中有一个”hint.php”文件还未访问,
访问获得

提示很明显,flag在ffffllllaaaagggg文件中。
payload

1
?file=hint.php?../../../../../../ffffllllaaaagggg


获得flag

为什么能获得flag

白名单绕过

检查步骤:
1.直接检查(in_array($page, $whitelist)):
hint.php?../../../../../ffffllllaaaagggg 不在白名单中,不通过。

2.截取 ? 之前的部分检查:
$page = “hint.php?../../../../../ffffllllaaaagggg”
mb_strpos() 找到第一个 ? 的位置,截取后得到 $_page = “hint.php”。
“hint.php” 在白名单中,检查通过!

include文件包含机制

PHP 的 include 在包含文件时,会按照以下规则处理路径:
file=hint.php?../../../../../../ffffllllaaaagggg
PHP 的 include 在加载文件时,会尝试解析路径:
如果路径包含 ?,PHP 会将其视为 HTTP 查询参数,并尝试加载 ? 之前的部分(即 hint.php)。
但某些操作系统(如 Linux)的文件系统仍然会解析 ? 后面的部分,导致路径穿越生效。

include(“hint.php?../../../../../ffffllllaaaagggg”);

在某些环境下,PHP 会尝试加载:

/current_directory/../../../../../ffffllllaaaagggg
即:
../../../../../ 会返回到根目录 /
然后访问 ffffllllaaaagggg。

服务器文件结构:
通常 CTF 题目会把 flag 放在根目录或深层次目录,例如:
/flag
/var/www/html/flag
/tmp/flag

../../../../../ 可以返回到根目录 /,再访问目标文件。

另外发现一个有趣的东西

这个payload不在source.php文件下进行,但照样会获得flag,看网上很多文章都要在source.php文件下执行payload。

include文件包含机制这块是我查很多文章和AI中我觉得比较能说服我的解释(个人实力有限),推荐另一位师傅的文章:
https://blog.csdn.net/2401_86598628/article/details/148697327
这篇文章会更加详细和include文件包含机制这块有不同的理解。