首先还是函数介绍
函数介绍 $_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_pro\WWW\1.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