Когда софтина попадает под пристальный взгляд экспертов по безопасности, велика вероятность, что за одним багом найдутся и другие. Так и случилось с агентом пересылки сообщений Exim: вслед за прошлогодней уязвимостью в нем найдена новая опасная дыра, действующая во всех версиях вплоть до последней (4.90.1). Поскольку Exim — штука популярная, список потенциально уязвимых целей просто огромен. Давай посмотрим, как эксплуатировать эту новую находку.

Обнаруженная проблема — это своеобразное продолжение предыдущего бага, который нашел тот же исследователь под ником Meh. На этот раз он раскопал возможность переполнения буфера в функции для работы с кодировкой Base64.

Уязвимость уже обзавелась своим идентификатором CVE-2018-6789 и получила статус критической, потому что приводит к удаленному выполнению любых команд на целевой системе с правами пользователя, от имени которого работает Exim. Причем не нужна ни авторизация, ни какой-либо другой уровень доступа. Нужен только коннект к порту SMTP.

 

Готовим инструменты

Под эту уязвимость существует добротно настроенный докер-контейнер, так что говорим спасибо товарищу под ником Skysider и запускаем:

$ docker run -it --rm --name exim -p 25:25 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined skysider/vulndocker:cve-2018-6789

Пробрасываем из Docker стандартный порт, на котором висит SMTP.

Готовый стенд для эксплуатации Exim
Готовый стенд для эксплуатации Exim

Если нужна поддержка дебаггера, то не забудь его установить и перекомпилировать Exim с отладочными символами.

$ apt-get update && apt-get install -y gdb
$ cd exim-4.89
$ printf "CFLAGS += -g\n" >> Local/Makefile
$ make

Также нам понадобится Python с установленным pwntools для написания и тестирования эксплоита. Я просто разверну еще один докер-контейнер на основе Debian.

$ docker run -it --rm --link=exim debian /bin/bash
$ apt-get update && apt-get install -y python python-pip
$ pip install pwntools

Все готово, вперед к победам!

 

Работа с кучей

Для начала взглянем на саму провинившуюся функцию.

/src/base64.c
153: b64decode(const uschar *code, uschar **ptr)
154: {
155: int x, y;
156: uschar *result = store_get(3*(Ustrlen(code)/4) + 1);
157:
158: *ptr = result;

За выделение требуемого количества памяти отвечает store_get — кастомная функция из набора для менеджмента памяти, который используется в составе Exim.

/src/store.h
30: #define store_extend(addr,old,new) \
31:   store_extend_3(addr, old, new, __FILE__, __LINE__)
32:
33: #define store_free(addr)     store_free_3(addr, __FILE__, __LINE__)
34: #define store_get(size)      store_get_3(size, __FILE__, __LINE__)
35: #define store_get_perm(size) store_get_perm_3(size, __FILE__, __LINE__)
36: #define store_malloc(size)   store_malloc_3(size, __FILE__, __LINE__)
37: #define store_release(addr)  store_release_3(addr, __FILE__, __LINE__)
38: #define store_reset(addr)    store_reset_3(addr, __FILE__, __LINE__)
...
43: extern BOOL    store_extend_3(void *, int, int, const char *, int);
44: extern void    store_free_3(void *, const char *, int);
45: extern void   *store_get_3(int, const char *, int);
46: extern void   *store_get_perm_3(int, const char *, int);
47: extern void   *store_malloc_3(int, const char *, int);
48: extern void    store_release_3(void *, const char *, int);
49: extern void    store_reset_3(void *, const char *, int);

Во время работы функции выделяется буфер размером 3*(len/4)+1 байт для хранения декодированных данных, где len — длина передаваемых данных. Такая формула не случайна, так как в стандарте Base64 каждые три исходных байта кодируются четырьмя символами. В идеальных условиях размер переданных данных всегда кратен четырем, но, к счастью, мы живем не в них, и если передать невалидную кодированную строку, то функция store_get получит неверное значение размера выделяемой памяти.

Размеры выделяемой памяти для валидной и невалидной строки Base64
Размеры выделяемой памяти для валидной и невалидной строки Base64

В общем случае, когда передаем строку размером 4n – 1, Exim зарезервирует 3n + 1 байт, но после декодирования получится строка, итоговый размер которой будет равен 3n + 2 байта, и это вызовет переполнение при попытке записи в выделенный буфер.

Где используется кодировка Base64? Да практически везде. Начиная от разных типов авторизаций и заканчивая файлами, которые прикрепляются к письмам. Все эти вещи потенциально уязвимы. Авторизация нам подходит, так как для отправки сообщений чаще всего потребуется валидный логин и пароль. На тестовом стенде уже включен механизм аутентификации CRAM-MD5, но подойдет и любой другой, который работает с Base64.

Теперь немножко поговорим о работе с памятью. Как я уже писал, в Exim существует самописный набор функций для этих целей. Функция store_malloc — вызов malloc прямиком из библиотеки glibc. Она занимается выделением блока памяти нужного размера.

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

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Check Also

Вторник обновлений: Microsoft исправила 0-day уязвимость в Internet Explorer

В этом месяце Microsoft устранила 74 уязвимости в своих продуктах, 13 из которых были оцен…

3 комментария

  1. Аватар

    konyakov

    08.05.2018 at 09:48

    Что за музыка в видосе?

  2. Аватар

    konyakov

    08.05.2018 at 09:49

    Нашё, спасибо. Signals — Dance With The Dead

  3. Аватар

    enc

    24.05.2018 at 01:31

    Побольше бы таких статей! Спасибо.

Оставить мнение