前言:

本文只分析发生漏洞得原因,具体pop链简略分析。

joomla中得session会被存入数据库中,这是以前版本得RCE就可以得知得事情。

/libraries/joomla/session/storage.php:

public function register()
    {
        // Use this object as the session handler
        session_set_save_handler(
            array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'),
            array($this, 'destroy'), array($this, 'gc')
        );
    }

通过这里得到目标注册得几个函数,但是此方法为抽象类,也就是说不能实例化的,所以我们需要寻找继承了此类的类进行分析,在JSessionStorageDatabase对象中,均重写了上面的几个函数。

0x01 入口:

根据github给出的payload得出路由为:/index.php/component/users
根据路由找到目标文件的真实文件为:/components/com_users/users.php
此文件获取了一个task参数,这个参数不做具体分析,我们只需要得知目标会根据此参数来找到最终提交的函数
payload中有如下:

'task': 'user.login',

也就是说会提交到 user控制器下面的login方法,直接追过去就好了,具体路径为:components/com_users/controllers/user.php
代码:

public function login()
    {
        JSession::checkToken('post') or jexit(JText::_('JINVALID_TOKEN'));

        $app    = JFactory::getApplication();
        $input  = $app->input;
        $method = $input->getMethod();

        // Populate the data array:
        $data = array();

        $data['return']    = base64_decode($app->input->post->get('return', '', 'BASE64'));
        $data['username']  = $input->$method->get('username', '', 'USERNAME');
        $data['password']  = $input->$method->get('password', '', 'RAW');
        $data['secretkey'] = $input->$method->get('secretkey', '', 'RAW');

        // Don't redirect to an external URL.
        if (!JUri::isInternal($data['return']))
        {
            $data['return'] = '';
        }

        // Set the return URL if empty.
        if (empty($data['return']))
        {
            $data['return'] = 'index.php?option=com_users&view=profile';
        }

        // Set the return URL in the user state to allow modification by plugins
        $app->setUserState('users.login.form.return', $data['return']);

        // Get the log in options.
        $options = array();
        $options['remember'] = $this->input->getBool('remember', false);
        $options['return']   = $data['return'];

        // Get the log in credentials.
        $credentials = array();
        $credentials['username']  = $data['username'];
        $credentials['password']  = $data['password'];
        $credentials['secretkey'] = $data['secretkey'];

        // Perform the log in.
        if (true === $app->login($credentials, $options))
        {
            // Success
            if ($options['remember'] == true)
            {
                $app->setUserState('rememberLogin', true);
            }

            $app->setUserState('users.login.form.data', array());
            $app->redirect(JRoute::_($app->getUserState('users.login.form.return'), false));
        }
        else
        {
            // Login failed !
            $data['remember'] = (int) $options['remember'];
            $app->setUserState('users.login.form.data', $data);
            $app->redirect(JRoute::_('index.php?option=com_users&view=login', false));
        }
    }

0x02 进入login中:

这里我们可以看下重点代码:

JSession::checkToken('post') or jexit(JText::_('JINVALID_TOKEN'));

进入checkToken函数中,具体看下

$session = JFactory::getSession();
            if ($session->isNew())

跟进后发现这句代码获取了现在的session对象:

public static function getSession(array $options = array())
    {
        if (!self::$session)
        {
            self::$session = self::createSession($options);
        }

        return self::$session;
    }

这里获取到的对象其实就是当前对象,因为我在下面发现了isNew函数:

public function isNew()
    {
        $counter = $this->get('session.counter');
        return (bool) ($counter === 1);
    }

然后在跟进get函数:

public function get($name, $default = null, $namespace = 'default')
点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖