Когда софтина попадает под пристальный взгляд экспертов по безопасности, велика вероятность, что за одним багом найдутся и другие. Так и случилось с агентом пересылки сообщений 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. Оформи подписку на «Хакер», чтобы читать все материалы на сайте

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

Вариант 2. Купи один материал

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


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

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

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

Check Also

Хакеры скомпрометировали LabCorp, крупнейшую в США сеть диагностических лабораторий

Крупнейшая в США сеть диагностических лабораторий, LabCorp, в начале текущей недели была а…