WarmUp
进来一个滑稽表情包,查看页面源码,得到提示
<-- source.php -->
访问source.php,查看源码:
<?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\" />";
}
?>
审计一下php代码,需要使用GET方式提交一个
$file
变量,进行文件包含。看到白名单,随即访问hint.php,得到ffffllllaaaagggg文件名。
多次if语句判断,第一次判断是否存在
$page
变量且是否为字符串,简单绕过。第二层 if 用到了
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
函数:in_array($page, $whitelist)
如果满足了就
return true
。关于 bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
函数有它的设计缺陷,当进行弱比较的时候会进行弱类型转换,从而绕过对数字的审查。大致可以看这篇参考文章:PHP代码审计Day1 - in_array函数缺陷。在这里由于数组类型是字符串类型,所以不存在这样的弱类型转换漏洞,这里如果需要返回 true,则必须
$page
的值为 hint.php 或者 source.php。$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
这部分用到了两个比较新的 php 函数:
mb_substr ( string $str , int $start [, int $length = NULL [, string $encoding = mb_internal_encoding() ]] ) : string
与
substr()
函数不同的是,mb_substr()
通常用来分割多种字符,用于兼容字符集。例如:echo mb_substr("Hello, world!", 0, 2);
//echo:He
echo mb_substr("你好世界!", 0, 2);
//echo:你好
这看起来似乎更符合中国人的习惯。
mb_strpos ( string $haystack , string $needle [, int $offset = 0 [, string $encoding = mb_internal_encoding() ]] ) : int
该函数用来返回字符串
$needle
在 $haystack
字符串中第一次出现的位置。这里就存在绕过,只要题目中的字符串中出现问号,就可以对字符串进行截断,从而绕过白名单的审查。echo mb_strpos("Hello!" . "?", "?"); //echo: 6
echo mb_strpos("?Hello!") . "?", "?"); //echo: 0
于是只要构造这样的字符串:
?file=hint.php?
?file=source.php?
就可以绕过白名单审计,返回
true
。接下来就是简单的目录穿越完事,可以先读取 /etc/passwd:
source.php?file=hint.php?../../../../../etc/passwd
穿越回根目录经过五次,大概推测文件结构,然后读取flag。
source.php?file=hint.php?../../../../../ffffllllaaaagggg
Last modified 8mo ago