一道include目录穿越

0x00 前言

写完题目后马上就逻辑清晰,但是在写题时又总是身在局中不知何往,还得加强对逻辑推理的培养。

0x01 题目

题目入口如图:

image-20240811160307154

查看源码:

image-20240811160343192

注释提示source.php文件。

当时没有重视这里的提示,臆想入口页面就是source.php,后来用dirsearch扫后才发现source.php另是其他,可见写题目时切忌主观臆断,要保持清晰的思维。

那么现在有入口点了。

0x02 冲突

source.php源码如下

image-20240811161236296

简单解释两个函数:

  • mb_substr() :用于从多字节字符串中提取子字符串,示例如下:
1
2
3
$string = "你好,世界!"; 
$sub = mb_substr($string, 1, 2, "UTF-8");
echo $sub; // 输出 "好,
  • mb_strpos() 用于查找多字节字符串中子字符串首次出现的位置,示例如下:
1
2
3
$string = "你好,世界!";
$position = mb_strpos($string, "世界", 0, "UTF-8");
echo $position; // 输出 4

知道了上述两个函数,那么源码的逻辑也就很清晰了。另外,hint.php文件给出提示:

image-20240811161859310

整理一下目前的思路:我们可以操控file参数进行文件包含,这不难让人联想到目录穿越。但是file参数要经过白名单检查(下图仅是一处检查):

image-20240811162155142

矛盾之处:

  • 如果file=source.php,那么显然无法访问到flag所在文件。
  • 如果file=../../../../ffffllllaaaagggg,那么白名单就无法绕过。

显然,正常的思路无法同时满足include和白名单。

0x03 深入

我们目前的思路非常明确,不用去想其他乱七八糟的,就是使参数file同时满足include目录穿越和白名单。

假定上述的思路是对的,那么突破点很可能就是可以兼容上述两个条件的漏洞——这样的漏洞往往就在条件本身。

细看这一处白名单检查:

image-20240811163451770

结合题目,该处代码会把参数file的值里的?前面的字符串提取出来并检测提取的字符串是否在白名单中。很明显,这段代码只要参数file的前一部分(以问号为分界线)。

再看include

image-20240811163946343

include的路径是参数file的全部。

怎么兼容呢,这里我想了很久。我最初的想法是前一半满足白名单,后一半满足include,这是极为局限的想法。

没有思路,不妨先写下自己能把握的,如下:

file=source.php?<没想出来的地方>

怎么让source.php?<没想出来的地方>合法呢,也就是可以被include正确解析呢?首先要知道哪些是可以变的,哪些是不变的。

很明显,source.php?是不变的,?一般不用于文件名,这样的文件名在include里肯定不会被正确解析,同时?include里也不能作为通配符使用。那么我就猜,?可以作为目录名的一部分——使source.php?作为目录名。source.php?要能被认作目录,需要在其后面添加一个/,变成source.php?/

很明显,source.php?作为一个虚假的目录是无法被正确解析的,再者我们也不需要包含source.php?目录,我们只需要包含flag所在文件。那么有没有一个办法可以允许source.php?字符串存在的情况下,不解析source.php?目录而正确解析fffflllaaaagggg文件呢?如下:

PHP里,include只会包含最终的文件路径,其中某一个目录是否存在并不影响,只要最终的文件路径存在即可。

那么目录source.php?不存在又何妨,不知道当前目录又何妨(source.php?目录基于当前目录),payload后面是完全可控的,使目录穿越到上游即可。payload如下:

1
file=source.php?/../../../../../../fffflllaaaagggg

结果如下:

image-20240811170118404

0x04 结语

本篇文件的知识点其实不多,更多的是一步一步的推理。