Когда софтина попадает под пристальный взгляд экспертов по безопасности, велика вероятность, что за одним багом найдутся и другие. Так и случилось с агентом пересылки сообщений 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 с отладочными символами.
$ apt-get update && apt-get install -y gdb $ cd exim-4.89 $ printf "CFLAGS += -gn" >> 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
Все готово, вперед к победам!
Для начала взглянем на саму провинившуюся функцию.
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.
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
получит неверное значение размера выделяемой памяти.
В общем случае, когда передаем строку размером 4n – 1, Exim зарезервирует 3n + 1 байт, но после декодирования получится строка, итоговый размер которой будет равен 3n + 2 байта, и это вызовет переполнение при попытке записи в выделенный буфер.
Где используется кодировка Base64? Да практически везде. Начиная от разных типов авторизаций и заканчивая файлами, которые прикрепляются к письмам. Все эти вещи потенциально уязвимы. Авторизация нам подходит, так как для отправки сообщений чаще всего потребуется валидный логин и пароль. На тестовом стенде уже включен механизм аутентификации CRAM-MD5, но подойдет и любой другой, который работает с Base64.
Теперь немножко поговорим о работе с памятью. Как я уже писал, в Exim существует самописный набор функций для этих целей. Функция store_malloc
— вызов malloc
прямиком из библиотеки glibc. Она занимается выделением блока памяти нужного размера.
Материалы из последних выпусков можно покупать отдельно только через два месяца после публикации. Чтобы продолжить чтение, необходимо купить подписку.
Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке
1 год6790 р. Экономия 1400 рублей! |
1 месяц720 р. 25-30 статей в месяц |
Уже подписан?
Читайте также
Последние новости