Статьи Анализ PHP Object Injection в Joomla

Discussion in 'Статьи' started by eclipse, 14 Dec 2013.

  1. eclipse

    eclipse Member

    Joined:
    19 Dec 2010
    Messages:
    155
    Likes Received:
    74
    Reputations:
    85
    Оригинальная статья написана EgiX (c) Karma(In)Security
    February 27, 2013


    Анализ уязвимости PHP Object injection в Joomla.

    Сегодня я раскрыл информацию об уязвиомтси вида PHP Object injection в CMS Joomla (KIS-2013-03 ).
    Всего лишь несколько месяцев прошло с тех пор как я сообщил о ней в Joomla Security Strike Team,
    но если быть честным я обратил внимание на уязвимую функцию unserialize() давно до этого.
    Единственная причина по которой я не оповестил их ранее - это мое отношение к данной уязвимости как к непригодной для использования:
    я не знал о каких-либо "волшебных" методах которые можно было бы использовать для осуществления зловредной атаки, поэтому я пришел к выводу,
    что данная уязвимость собственно говоря, не является реальной уязвимостью.

    Некоторое время спустя, после выхода Joomla 3.0, мне пришло в голову посмотреть еще раз в исходный код Joomla,
    чтобы проверить появились-ли новые "волшебные" методы.
    По сравнению с Joomla 2.5 я не нашел использования каких-либо новых "волшебных" методов PHP, но я заметил маленькое изменение
    в классе деструктора метода которое явилось для меня ключевым моментом, чтобы понять что уязвимость на самом деле существует и
    может быть использования через данный (и не только) "волшебный" метод. Я говорю о методе деструктора класса plgSystemDebug,
    который в Joomla 2.5 начинается вот так:

    Code:
    74:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] __destruct()
    75:[COLOR=DarkOliveGreen]|[/COLOR] {
    76:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=Green]// Do not render if debugging or language debug is not enabled[/COLOR]
    77:[COLOR=DarkOliveGreen]|[/COLOR]   [COLOR=DeepSkyBlue]if[/COLOR] (!JDEBUG && !JFactory::getApplication()->getCfg('debug_lang'))
    78:[COLOR=DarkOliveGreen]|[/COLOR]    {
    79:[COLOR=DarkOliveGreen]|[/COLOR]       [COLOR=DeepSkyBlue]return;[/COLOR]
    80:[COLOR=DarkOliveGreen]|[/COLOR]    }
    
    Когда в Joomla 3.0 он был изменен на:

    Code:
    84:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] __destruct()
    85:[COLOR=DarkOliveGreen]|[/COLOR] {
    86:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=Green]// Do not render if debugging or language debug is not enabled[/COLOR]
    87:[COLOR=DarkOliveGreen]|[/COLOR]   [COLOR=DeepSkyBlue]if[/COLOR] (!JDEBUG && !$this->debugLang)
    88:[COLOR=DarkOliveGreen]|[/COLOR]    {
    89:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=DeepSkyBlue]return;[/COLOR]
    90:[COLOR=DarkOliveGreen]|[/COLOR]    }
    
    В конфигурации по умолчанию плагин System Debug активирован, но опции Debug System и Debug Language выключены.
    Это значит, что при проведении атаки в Joomla 2.5 этот метод может быть использован только тогда,
    когда одна или обе из вышеназванных опций будут включены,
    тогда как в Joomla 3.0, из-за проверки значения свойства объекта debugLang, он может быть благополучно использован с конфигурацией по умолчанию.
    Взглянув на метод plgSystemDebug::__destruct может показаться что в он никак не пригоден для осуществления зловредной атаки,
    но есть такая строка кода с вызовом метода get объекта хранимого в свойстве params:
    Code:
    [COLOR=LemonChiffon]$filterGroups[/COLOR] = ([COLOR=DeepSkyBlue]array[/COLOR]) [COLOR=LemonChiffon]$this[/COLOR]->params->get('[COLOR=MediumTurquoise]filter_groups[/COLOR]', [COLOR=DeepSkyBlue]null[/COLOR]);
    Это свойство - член класса JPlugin и по идее должен быть объектом JRegistry содержащим в себе параметры пллагина.
    Однако злоумышленник может присвоить свойству params произвольный PHP объект, так что я стал искать get методы и нашел два различных
    вектора проведения атак.

    Удаление произвольной директории

    Первый вектор атаки позволяет злоумышленнику удалить произвольную директорию для которого у web-сервера есть соответствующие права.
    Это возможно в результате злоупотребление двумя различными методами, первый из них - JInput::get:
    Code:
    157:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] get([COLOR=LemonChiffon]$name[/COLOR], [COLOR=LemonChiffon]$default[/COLOR] = [COLOR=DeepSkyBlue]null[/COLOR], [COLOR=LemonChiffon]$filter[/COLOR] = [COLOR=MediumTurquoise]'cmd'[/COLOR])
    158:[COLOR=DarkOliveGreen]|[/COLOR] {
    159:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=DeepSkyBlue]if[/COLOR]([COLOR=Magenta]isset[/COLOR]([COLOR=LemonChiffon]$this[/COLOR]->data[[COLOR=LemonChiffon]$name[/COLOR]]))
    160:[COLOR=DarkOliveGreen]|[/COLOR]    {
    161:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=DeepSkyBlue]return[/COLOR] [COLOR=LemonChiffon]$this[/COLOR]->filter->clean([COLOR=LemonChiffon]$this[/COLOR]->data[[COLOR=LemonChiffon]$name[/COLOR]], [COLOR=LemonChiffon]$filter[/COLOR]);
    162:[COLOR=DarkOliveGreen]|[/COLOR]    }
    В строке 161 вызывается метод clean объекта хранимого в свойстве filter, предпологается что это объект JFilterInput.
    Но как уже объяснялось ранее, данному свойству может быть присвоен произвольный PHP объект, что привело к том что я стал искать
    полезные clean методы. В результате я нашел метод JCacheStorageFile:clean:
    Code:
    182:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] clean([COLOR=LemonChiffon]$group[/COLOR], [COLOR=LemonChiffon]$mode[/COLOR] = [COLOR=DeepSkyBlue]null[/COLOR])
    183:[COLOR=DarkOliveGreen]|[/COLOR] {
    184:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=LemonChiffon]$return[/COLOR] = [COLOR=DeepSkyBlue]true[/COLOR];
    185:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=LemonChiffon]$folder[/COLOR] = [COLOR=LemonChiffon]$group[/COLOR];
    186:[COLOR=DarkOliveGreen]|[/COLOR] 
    187:[COLOR=DarkOliveGreen]|[/COLOR]   [COLOR=DeepSkyBlue]if[/COLOR] (trim([COLOR=LemonChiffon]$folder[/COLOR]) == [COLOR=MediumTurquoise]''[/COLOR])
    188:[COLOR=DarkOliveGreen]|[/COLOR]    {
    189:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=LemonChiffon]$mode[/COLOR] = [COLOR=MediumTurquoise]'notgroup'[/COLOR];
    190:[COLOR=DarkOliveGreen]|[/COLOR]    }
    191:[COLOR=DarkOliveGreen]|[/COLOR]     
    192:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=DeepSkyBlue]switch[/COLOR] ([COLOR=LemonChiffon]$mode[/COLOR])
    193:[COLOR=DarkOliveGreen]|[/COLOR]    {
    194:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=DeepSkyBlue]case[/COLOR] [COLOR=MediumTurquoise]'notgroup'[/COLOR];:
    195:[COLOR=DarkOliveGreen]|[/COLOR]            [COLOR=LemonChiffon]$folders[/COLOR] = [COLOR=LemonChiffon]$this[/COLOR]->_folders([COLOR=LemonChiffon]$this[/COLOR]->_root);
    196:[COLOR=DarkOliveGreen]|[/COLOR]            [COLOR=DeepSkyBlue]for[/COLOR] ([COLOR=LemonChiffon]$i[/COLOR] = 0, [COLOR=LemonChiffon]$n[/COLOR] = [COLOR=Magenta]count[/COLOR]([COLOR=LemonChiffon]$folders[/COLOR]); [COLOR=LemonChiffon]$i[/COLOR] < [COLOR=LemonChiffon]$n[/COLOR]; [COLOR=LemonChiffon]$i[/COLOR]++)
    197:[COLOR=DarkOliveGreen]|[/COLOR]            {
    198:[COLOR=DarkOliveGreen]|[/COLOR]               [COLOR=DeepSkyBlue]if[/COLOR] ([COLOR=LemonChiffon]$folders[$i][/COLOR] != [COLOR=LemonChiffon]$folder[/COLOR])
    199:[COLOR=DarkOliveGreen]|[/COLOR]                {
    200:[COLOR=DarkOliveGreen]|[/COLOR]                    [COLOR=LemonChiffon]$return[/COLOR] [COLOR=DarkOliveGreen]|[/COLOR]= [COLOR=LemonChiffon]$this[/COLOR]->_deleteFolder([COLOR=LemonChiffon]$this[/COLOR]->_root . [COLOR=MediumTurquoise]'/'[/COLOR] . [COLOR=LemonChiffon]$folders[$i][/COLOR]);
    201:[COLOR=DarkOliveGreen]|[/COLOR]                }
    202:[COLOR=DarkOliveGreen]|[/COLOR]            }
    203:[COLOR=DarkOliveGreen]|[/COLOR]            [COLOR=DeepSkyBlue]break[/COLOR];
    204:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=DeepSkyBlue]case[/COLOR] [COLOR=MediumTurquoise]'group'[/COLOR]:
    205:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=DeepSkyBlue]default[/COLOR]: 
    206:[COLOR=DarkOliveGreen]|[/COLOR]           [COLOR=DeepSkyBlue]if[/COLOR] ([COLOR=Magenta]is_dir[/COLOR]([COLOR=LemonChiffon]$this[/COLOR]->_root . [COLOR=MediumTurquoise]'/'[/COLOR] . [COLOR=LemonChiffon]$folder[/COLOR]))
    207:[COLOR=DarkOliveGreen]|[/COLOR]            {
    208:[COLOR=DarkOliveGreen]|[/COLOR]                [COLOR=LemonChiffon]$return[/COLOR] = [COLOR=LemonChiffon]$this[/COLOR]->_deleteFolder([COLOR=LemonChiffon]$this[/COLOR]->_root . [COLOR=MediumTurquoise]'/'[/COLOR] . [COLOR=LemonChiffon]$folder[/COLOR]);
    209:[COLOR=DarkOliveGreen]|[/COLOR]            }
    Если переменной $mode будет присвоено значение "cmd", оператор switch передаст управление условию по умолчанию, на строку 192,
    тогда, в строке 208 будет вызван метод JCacheStorageFile::_deleteFolder и как подсказывает имя, этот метод будет пытаться
    провести рекурсивное удалени директории. Из-за возможности использования относительных адресов,
    для того, чтобы удалить директорию у атакующего злоумышленниа даже нет необходимости в знании абсолютного пути,
    например он может привсвоить свойству _root инъекцированного объекта JCacheStorageFile, значение "./", и попытаться удалить корневой
    каталог Joomla, что может вылиться в ошибку отказа от сервиса (DoS).

    Слепая SQL инъекция

    Второй вектор атаки позволяет злоумышленнику провести SQL injection. Это становится возможным злоупотребив методом get класса JCategories:
    Code:
    166:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] get([COLOR=LemonChiffon]$id[/COLOR] = [COLOR=MediumTurquoise]'root'[/COLOR], [COLOR=LemonChiffon]$forceload[/COLOR] = [COLOR=DeepSkyBlue]false[/COLOR])
    167:[COLOR=DarkOliveGreen]|[/COLOR] {
    168:[COLOR=DarkOliveGreen]|[/COLOR]   [COLOR=DeepSkyBlue]if[/COLOR] ([COLOR=LemonChiffon]$id[/COLOR] !== [COLOR=MediumTurquoise]'root'[/COLOR])
    169:[COLOR=DarkOliveGreen]|[/COLOR]    {
    170:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=LemonChiffon]$id[/COLOR] = ([COLOR=DeepSkyBlue]int[/COLOR]) [COLOR=LemonChiffon]$id[/COLOR];
    171:[COLOR=DarkOliveGreen]|[/COLOR] 
    172:[COLOR=DarkOliveGreen]|[/COLOR]       [COLOR=DeepSkyBlue]if[/COLOR] ([COLOR=LemonChiffon]$id[/COLOR] == 0)
    173:[COLOR=DarkOliveGreen]|[/COLOR]        {
    174:[COLOR=DarkOliveGreen]|[/COLOR]            [COLOR=LemonChiffon]$id[/COLOR] = [COLOR=MediumTurquoise]'root'[/COLOR];
    175:[COLOR=DarkOliveGreen]|[/COLOR]        }
    176:[COLOR=DarkOliveGreen]|[/COLOR]    }
    177:[COLOR=DarkOliveGreen]|[/COLOR] 
    178:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=Green]// If this $id has not been processed yet, execute the _load method[/COLOR]
    179:[COLOR=DarkOliveGreen]|[/COLOR]   [COLOR=DeepSkyBlue]if[/COLOR] ((![COLOR=Magenta]isset[/COLOR]([COLOR=LemonChiffon]$this[/COLOR]->_nodes[[COLOR=LemonChiffon]$id[/COLOR]]) && ![COLOR=Magenta]isset[/COLOR]([COLOR=LemonChiffon]$this[/COLOR]->_checkedCategories[[COLOR=LemonChiffon]$id[/COLOR]])) [COLOR=DarkOliveGreen]|[/COLOR][COLOR=DarkOliveGreen]|[/COLOR] [COLOR=LemonChiffon]$forceload[/COLOR])
    180:[COLOR=DarkOliveGreen]|[/COLOR]    {
    181:[COLOR=DarkOliveGreen]|[/COLOR]        [COLOR=LemonChiffon]$this[/COLOR]->_load([COLOR=LemonChiffon]$id[/COLOR]);
    182:[COLOR=DarkOliveGreen]|[/COLOR]    }
    На строке 181 вызывается метод JCategories::_load, который по идее должен получить указанную категорию из хранимой на сервере базы данных.
    Внутри метода можно использовать произвольные SQL команды через различные свойства объекта, такие как _table,_field,_key и _statefield.

    Еще...

    Как я упомянал в начале данной статьи, метод plgSystemDebug::__destruct не является единственным "волшебным" методом который может
    быть использован для эксплуатации уязвимости. Есть еще один "волшебный" метод добавленный в Joomla 3.0 с его __toString методом класса
    JViewHtml, вызывающий метод JViewHtml::render:
    Code:
    135:[COLOR=DarkOliveGreen]|[/COLOR] [COLOR=DeepSkyBlue]public function[/COLOR] render()
    136:[COLOR=DarkOliveGreen]|[/COLOR] {
    137:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=Green]// Get the layout path.[/COLOR]
    138:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=LemonChiffon]$path[/COLOR] = [COLOR=LemonChiffon]$this[/COLOR]->getPath([COLOR=LemonChiffon]$this[/COLOR]->getLayout());
    139:[COLOR=DarkOliveGreen]|[/COLOR]  
    140:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=Green]// Check if the layout path was found.[/COLOR]
    141:[COLOR=DarkOliveGreen]|[/COLOR]    [COLOR=DeepSkyBlue]if[/COLOR] (![COLOR=LemonChiffon]$path[/COLOR])
    142:[COLOR=DarkOliveGreen]|[/COLOR]     {
    143:[COLOR=DarkOliveGreen]|[/COLOR]         [COLOR=DeepSkyBlue]throw new[/COLOR] RuntimeException([COLOR=MediumTurquoise]'Layout Path Not Found'[/COLOR]);
    144:[COLOR=DarkOliveGreen]|[/COLOR]     }
    145:[COLOR=DarkOliveGreen]|[/COLOR]  
    146:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=Green]// Start an output buffer.[/COLOR]
    147:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=Magenta]ob_start[/COLOR]();
    148:[COLOR=DarkOliveGreen]|[/COLOR]  
    149:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=Green]// Load the layout.[/COLOR]
    150:[COLOR=DarkOliveGreen]|[/COLOR]     [COLOR=DeepSkyBlue]include[/COLOR] [COLOR=LemonChiffon]$path[/COLOR];
    Метод JViewHtml::getPath может возвращать строку которая будет полностью подконтрольна атакующему злоумышленнику, это может повлечь
    за собой проведение атаки вида PHP File Inclusion на строке 150. Однако, JViewHtml
    является абстрактным классом который по идее должен быть использован сторонними
    расширениями, значит данный вектор атаки может быть проведен только в случае если
    в атакуемой системе Joomla установлено расширение использующее класс наследующий JViewHtml.
    Как бы там ни было, этот интересный кусок кода оказался для меня подсказкой к пониманию
    что на ряду с вышеописанными могут быть использованы и другие "волшебные" методы описываемые сторонних расширений.
    Это возможно благодаря уязвимости unserialize() вызываемой в методе onAfterDispatch который срабатывается когда Joomla перехватывает
    запрос и адресует его компонентам, так что все классы выбранного компонента декларируются при срабатывании метода. Это значит,
    что возможны другие векторы атак с использованием "волшебных" методов сторонних расширений.

     
    #1 eclipse, 14 Dec 2013
    Last edited: 14 Dec 2013