Следующая новость
Предыдущая новость

Брут для «Джумлы». Обходим авторизацию, чтобы получить пароли пользователей в Joomla CMS

28.09.2017 13:06
Брут для «Джумлы». Обходим авторизацию, чтобы получить пароли пользователей в Joomla CMS

Содержание статьи

  • Стенд
  • Детали уязвимости
  • PoC
  • Вывод

Популярнейшая многофункциональная CMS Joomla снова с нами. Несколько месяцев назад мы уже разбирали уязвимость в ней, но тогда это была SQL-инъекция, а теперь на повестке более экзотическая штука — LDAP Injection. Уязвимости такого типа встречаются нечасто, тем более в системах управления сайтами, но тем интереснее будет изучить ее.

Уязвимость называется CVE-2017-14596, и найдена она была аж 27 июля 2017 года. А нашел ее Йоханес Дасе из RIPS Technologies GmbH. Уязвимы все версии CMS, начиная с 1.5.0 и заканчивая 3.7.5 включительно. То есть ошибка оставалась в коде необнаруженной в течение восьми лет, до того как ее пофиксили 19 сентября 2017-го.

Стенд

Как устанавливать Joomla, я думаю, все в курсе. Чтобы не париться с базовой настройкой сервера, я слепил файл Docker, который ты можешь скачать из моего репозитория. После его запуска тебе останется только пройти по шагам установки CMS.

Теперь дело за LDAP. Мне совсем не хотелось разбираться с настройкой сервера под Linux, поэтому я решил использовать OpenLDAP для Windows и установить все это дело в три клика.

После установки, если ты оставил все параметры по умолчанию, реквизиты для коннекта будут следующими:

User: cn=Manager,dc=maxcrc,dc=com Password: secret

Для управления сервером я воспользуюсь утилитой LDAP Admin. По умолчанию на сервере уже имеется предустановленный OU (organisation unit) People. Туда я помещу нового пользователя.

Созданный юзер в программе LDAP Admin

Почти все готово. Теперь включаем в настройках Joomla плагин авторизации через LDAP.

Брут для «Джумлы». Обходим авторизацию, чтобы получить пароли пользователей в Joomla CMS
Активация плагина LDAP-авторизации

Затем переходим в настройки этого плагина и вбиваем наши данные.

Брут для «Джумлы». Обходим авторизацию, чтобы получить пароли пользователей в Joomla CMS
Настройка LDAP-авторизации

Обрати внимание на параметр Search String, его я взял из официальной документации по настройке этого плагина на сайте CMS.

Детали уязвимости

Переходим к изучению причин уязвимости. Наш тернистый путь начинается с класса LoginController.

/administrator/components/com_login/controller.php
17: class LoginController extends JControllerLegacy ... 48:     /** 49:      * Method to log in a user. 50:      * 51:      * @return  void 52:      */ 53:     public function login() 54:     { ... 60:         $model = $this->getModel('login'); 61:         $credentials = $model->getState('credentials');

Как видно из названия, он отвечает за процесс авторизации пользователей. Данные пользователей, переданные в форме логина, попадают в переменную $credentials.
После этого они передаются в метод login.

/administrator/components/com_login/controller.php
58:         $app = JFactory::getApplication(); ... 64:         $result = $app->login($credentials, array('action' => 'core.login.admin'));
/libraries/cms/application/cms.php
019: class JApplicationCms extends JApplicationWeb ... 859:    public function login($credentials, $options = array()) 860:    { 861:        // Get the global JAuthentication object. 862:        $authenticate = JAuthentication::getInstance(); 863:        $response = $authenticate->authenticate($credentials, $options);

В процессе обработки кредсов выполняется метод authenticate.

Дальше алгоритм работы зависит от активированных плагинов, которые отвечают за авторизацию. Отрабатывает метод onUserAuthenticate, и данные уходят на обработку соответствующим плагинам.

/libraries/joomla/authentication/authentication.php
017: class JAuthentication extends JObject ... 253:    public function authenticate($credentials, $options = array()) 254:    { 255:        // Get plugins 256:        $plugins = JPluginHelper::getPlugin('authentication'); ... 268:        foreach ($plugins as $plugin) 269:        { ... 283:            // Try to authenticate 284:            $plugin->onUserAuthenticate($credentials, $options, $response);

Так как у нас настроена авторизация через LDAP, то выполнение переходит к классу PlgAuthenticationLdap.

/plugins/authentication/ldap/ldap.php
014: /** 015:  * LDAP Authentication Plugin 016:  * 017:  * @since  1.5 018:  */ 019: class PlgAuthenticationLdap extends JPlugin ... 032:    public function onUserAuthenticate($credentials, $options, &$response) 033:    {

В этом плагине имя пользователя попадает в запрос к серверу LDAP, который мы указывали как опцию search_string. Документация Joomla говорит о том, что в строке запроса темплейт [search] напрямую изменяется на текст, переданный в поле login. Что ты и можешь наблюдать в сорцах.

/plugins/authentication/ldap/ldap.php
069:        switch ($auth_method) 070:        { 071:            case 'search': 072:            { ... 086:                    // Search for users DN 087:                    $binddata = $ldap->simple_search(str_replace('[search]', $credentials['username'], $this->params->get('search_string')));

Мы указали uid=[search] в параметре Search String в настройках плагина, так что после замены полученная строка уходит в метод simple_search.

/libraries/vendor/joomla/ldap/src/LdapClient.php
016: class LdapClient 017: { ... 275:    public function simple_search($search) 276:    { 277:        $results = explode(';', $search); 278:  279:        foreach ($results as $key => $result) 280:        { 281:            $results[$key] = '(' . $result . ')'; 282:        } 283:  284:        return $this->search($results); 285:    }

В качестве заключительного шага сгенерированная строка поиска уходит на LDAP-сервер с помощью search.

/libraries/vendor/joomla/ldap/src/LdapClient.php
298:    public function search(array $filters, $dnoverride = null, array $attributes = array()) 299:    { ... 313:        foreach ($filters as $search_filter) 314:        { 315:            $search_result = @ldap_search($resource, $dn, $search_filter, $attributes);

На данный момент у нас на руках LDAP-инъекция. Так как никакой фильтрации входных данных не происходит, мы можем влиять на отправляемую на сервер строку.

Сервер возвращает разные ответы в зависимости от результатов обработки запроса LDAP. Если пользователь найден, то система будет отвечать, что пароль неверен, а если пользователя вообще нет, то система известит нас об этом в соответствующем сообщении.

Брут для «Джумлы». Обходим авторизацию, чтобы получить пароли пользователей в Joomla CMS
Разные сообщения об ошибках в зависимости от переданных кредсов

Вызывать различные ошибки можно с помощью шаблонов поиска.

Продолжение статьи доступно только подписчикам

Cтатьи из последних выпусков журнала можно покупать отдельно только через два месяца после публикации. Чтобы читать эту статью, необходимо купить подписку.

Подпишись на журнал «Хакер» по выгодной цене!

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

1 год

2990 р.

Экономия 1400 рублей!

1 месяц

490 р.

25-30 статей в месяц

Уже подписан?

Источник

Последние новости