Всем доброго времени суток, наверняка многие уже слышали про недавнюю уязвимость в роутерах Mikrotik, позволяющую извлечь пароли всех пользователей. В этой статье я бы хотел подробно показать и разобрать суть данной уязвимости.
Весь материал предоставляется лишь в ознакомительных целях, поэтому кода, эксплуатирующего уязвимость, тут не будет. Если вам не интересно узнать о причинах и внутреннем устройстве той или иной уязвимости, можете не читать дальше.
Начнём
Первое, с чего стоит начать, это анализ трафика между клиентом Winbox и устройством
Снимаем галочку Secure Mode. Запускаем Wireshark и пробуем авторизоваться на устройстве:
Как можно заметить ниже, после авторизации идёт запрос файла list и затем его содержимое нам полностью передаётся, может показаться, что всё хорошо, но взглянем на самое начало этой сессии:
В самом начале Winbox отправляет точно такой же пакет с запросом файла list:
Рассмотрим его структуру:
Так! А вот это уже странно… Мы помним, что ERROR_NOTALLOWED появляется если не прошла проверка в path_filter(), иначе мы бы ещё получили сообщение об отсутствии прав доступа, но в последнем случае, получается, что поиск файла производился в директории верхнего уровня?
Попробуем такой способ:
$ ./untitled.py -t 192.168.88.1 -f //./.././.././../etc/passwd
xvM2����� � 1Enobody:*:99:99:nobody:/tmp:/bin/sh
root::0:0:root:/home/root:/bin/sh
И это сработало. Но почему? Давайте взглянем на код функции path_filter():
По коду отлично видно, что действительно происходит поиск вхождения ".. ", в полученный массив строк. Но дальше самое интересное, я выделил красным этот фрагмент.
Суть этого кода в том, что: Если предыдущий элемент так же является "..", то проверка считается проваленной. В противном случае — считать, что всё хорошо.
Т.е. чтобы всё сработало, нужно просто чередовать "/./" и "/../" чтобы успешно перемещаться по любым каталогам и спускаться на любой уровень ФС.
Давайте посмотрим, как разработчики Mikrotik это /
Теперь выход из цикла проверки происходит при первом же обнаружении ".. ". Правда мне не совсем понятно, зачем добавили проверку вхождения одной точки. А из-за изменения механизма активации пользователя devel, к сожалению, нет возможности посмотреть это в динамике.
Подведём итог
Так что не удивительно, что имея доступ на чтение любых файлов без авторизации, первым что было сделано, это чтение файла с паролями пользователей. Благо в сети предостаточно информации о том, где он расположен, и как извлечь из него данные.
Так же данная уязвимость может стать отличной заменой для известной ранее возможности активации режима разработчика, ведь перезагружать устройство, делать backup\restore файла конфигурации теперь не нужно.
Весь материал предоставляется лишь в ознакомительных целях, поэтому кода, эксплуатирующего уязвимость, тут не будет. Если вам не интересно узнать о причинах и внутреннем устройстве той или иной уязвимости, можете не читать дальше.
Начнём
Первое, с чего стоит начать, это анализ трафика между клиентом Winbox и устройством
Перед началом стоит отключить шифрование трафика в Winbox. Делается это следующим образом: нужно включить галочку Tools -> Advanced Mode. После этого интерфейс изменится следующим образом:Winbox — приложение для ОС WIndows, которое в точности повторяет веб-интерфейс и предназначено для администрирования и конфигурирования устройства с Router OS на борту. Поддерживается 2 режима работы, по протоколу TCP и UDP
Снимаем галочку Secure Mode. Запускаем Wireshark и пробуем авторизоваться на устройстве:
Как можно заметить ниже, после авторизации идёт запрос файла list и затем его содержимое нам полностью передаётся, может показаться, что всё хорошо, но взглянем на самое начало этой сессии:
В самом начале Winbox отправляет точно такой же пакет с запросом файла list:
Рассмотрим его структуру:
- 37010035 — размер пакета
- M2 — константа, обозначающая начало пакета
- 0500ff01 — переменная 0xff0005 в значении True
- 0600ff09 01 — переменная 0xff0006 в значении 1 (Номер передаваемого пакета)
- 0700ff09 07 — переменная 0xff0007 в значении 7 (Открыть файл в режиме чтения)
- 01000021 04 6с967374 — переменная 0x01000001 строка list размером 4 байта (Обычно данная переменная отвечает за название файла)
- 0200ff88 02… 00 — массив 0xff0002 размером 2 элемента
- 0100ff88 02… 00 — массив 0xff0001 размером 2 элемента
Теперь зная, как устроен протокол, мы можем произвольно генерировать нужные нам пакеты и смотреть, как на них реагирует девайс.
На стороне устройства, за обработку пакетов отвечает исполняемый файл /nova/bin/mproxy. Так как названия функций не были сохранены, я назвал функцию, которая обрабатывает пакет и принимает решения о том что делать с файлом file_handler(). Взглянем на саму функцию:
P.S. Код который нас будет интересовать отмечен стрелочками.
Шаг 1
При получении пакета на открытие файла для чтения, он начинает обработку с этого блока:
В самом начале из пакета, с помощью функции nv::message::get<nv::string_id>() извлекается название файла.
Далее функция tokenize() разбивает полученную строку на отдельные части, используя в качестве разделителя символ "/".
Полученный массив строк передаётся в функцию path_filter(), которая проверяет полученный массив строк на наличие "..", и в случае ошибок возвращает ошибку ERROR_NOTALLOWED (0xFE0009)
Шаг 2
Если всё прошло успешно, открывается файл и его дескриптор сохраняется в глобальный объект.
Если файл открыть не удалось, то в ответе мы получаем ошибку: cannot open source file.
Таким образом, чтобы получить содержимое файла, должно быть соблюдено 3 условия:
Теперь давайте попробуем отправить несколько пакетов для проверки работоспособности этой функции:
На стороне устройства, за обработку пакетов отвечает исполняемый файл /nova/bin/mproxy. Так как названия функций не были сохранены, я назвал функцию, которая обрабатывает пакет и принимает решения о том что делать с файлом file_handler(). Взглянем на саму функцию:
P.S. Код который нас будет интересовать отмечен стрелочками.
Шаг 1
При получении пакета на открытие файла для чтения, он начинает обработку с этого блока:
В самом начале из пакета, с помощью функции nv::message::get<nv::string_id>() извлекается название файла.
Далее функция tokenize() разбивает полученную строку на отдельные части, используя в качестве разделителя символ "/".
Полученный массив строк передаётся в функцию path_filter(), которая проверяет полученный массив строк на наличие "..", и в случае ошибок возвращает ошибку ERROR_NOTALLOWED (0xFE0009)
P.S. ERROR_NOTALLOWED так же будет получен в ответе, если нет прав доступа к файлу
Если же всё нормально, то к началу названия файла конкатенируется путь, к директории webfig или pckgШаг 2
Если всё прошло успешно, открывается файл и его дескриптор сохраняется в глобальный объект.
Если файл открыть не удалось, то в ответе мы получаем ошибку: cannot open source file.
Таким образом, чтобы получить содержимое файла, должно быть соблюдено 3 условия:
- Путь к файлу не содержит "..";
- Имеются права на доступ к файлу;
- Файл существует и может быть успешно открыт.
Теперь давайте попробуем отправить несколько пакетов для проверки работоспособности этой функции:
$ ./untitled.py -t 192.168.88.1 -f /etc/passwd
Error: SYS_ERRNO => ERROR_FAILED
Error: SYS_ERRSTR => cannot open source file
$ ./untitled.py -t 192.168.88.1 -f /../../../etc/passwd
Error: SYS_ERRNO => ERROR_NOTALLOWED
$ ./untitled.py -t 192.168.88.1 -f //./././././../etc/passwd
Error: SYS_ERRNO => ERROR_FAILED
Error: SYS_ERRSTR => cannot open source file
Так! А вот это уже странно… Мы помним, что ERROR_NOTALLOWED появляется если не прошла проверка в path_filter(), иначе мы бы ещё получили сообщение об отсутствии прав доступа, но в последнем случае, получается, что поиск файла производился в директории верхнего уровня?
Попробуем такой способ:
$ ./untitled.py -t 192.168.88.1 -f //./.././.././../etc/passwd
xvM2����� � 1Enobody:*:99:99:nobody:/tmp:/bin/sh
root::0:0:root:/home/root:/bin/sh
И это сработало. Но почему? Давайте взглянем на код функции path_filter():
По коду отлично видно, что действительно происходит поиск вхождения ".. ", в полученный массив строк. Но дальше самое интересное, я выделил красным этот фрагмент.
Суть этого кода в том, что: Если предыдущий элемент так же является "..", то проверка считается проваленной. В противном случае — считать, что всё хорошо.
Т.е. чтобы всё сработало, нужно просто чередовать "/./" и "/../" чтобы успешно перемещаться по любым каталогам и спускаться на любой уровень ФС.
Давайте посмотрим, как разработчики Mikrotik это /
Теперь выход из цикла проверки происходит при первом же обнаружении ".. ". Правда мне не совсем понятно, зачем добавили проверку вхождения одной точки. А из-за изменения механизма активации пользователя devel, к сожалению, нет возможности посмотреть это в динамике.
Подведём итог
- Router OS без проблем обрабатывает входящие пакеты ещё до авторизации пользователя
- Из-за некорректного фильтра мы получаем доступ к любому файлу
Так что не удивительно, что имея доступ на чтение любых файлов без авторизации, первым что было сделано, это чтение файла с паролями пользователей. Благо в сети предостаточно информации о том, где он расположен, и как извлечь из него данные.
Так же данная уязвимость может стать отличной заменой для известной ранее возможности активации режима разработчика, ведь перезагружать устройство, делать backup\restore файла конфигурации теперь не нужно.
Nodir7565