✔Команда упасть. Эксплуатируем критическую уязвимость в почтовике Exim 4 - «Новости»
Содержание статьи
- Работа с кучей
- Эксплуатация
- Выводы
Обнаруженная проблема — это своеобразное продолжение предыдущего бага, который нашел тот же исследователь под ником 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
[/code]
Пробрасываем из Docker стандартный порт, на котором висит SMTP.
Готовый стенд для эксплуатации Exim
Если нужна поддержка дебаггера, то не забудь его установить и перекомпилировать Exim с отладочными символами.
$ apt-get update && apt-get install -y gdb
$ cd exim-4.89
$ printf "CFLAGS += -gn" Local/Makefile
$ make
[/code]
Также нам понадобится 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
[/code]
Все готово, вперед к победам!
Работа с кучей
Для начала взглянем на саму провинившуюся функцию.
/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
В общем случае, когда передаем строку размером 4n – 1, Exim зарезервирует 3n + 1 байт, но после декодирования получится строка, итоговый размер которой будет равен 3n + 2 байта, и это вызовет переполнение при попытке записи в выделенный буфер.
Где используется кодировка Base64? Да практически везде. Начиная от разных типов авторизаций и заканчивая файлами, которые прикрепляются к письмам. Все эти вещи потенциально уязвимы. Авторизация нам подходит, так как для отправки сообщений чаще всего потребуется валидный логин и пароль. На тестовом стенде уже включен механизм аутентификации CRAM-MD5, но подойдет и любой другой, который работает с Base64.
Теперь немножко поговорим о работе с памятью. Как я уже писал, в Exim существует самописный набор функций для этих целей. Функция
store_malloc
— вызов malloc
прямиком из библиотеки glibc. Она занимается выделением блока памяти нужного размера.
Источник новости - google.com