Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2020-28018 — Эксплойт для Exim Use-After-Free (CVE-2020-28018), обеспечивающий удаленное выполнение кода с помощью примитивов повреждения памяти, включая произвольное чтение/запись и утечку кучи, с опциональной цепочкой локального повышения привилегий. | Kitploit
Инструменты/GitHubGitHub/dorkerdevil/cve-2020-28018
Повышение привилегийКриминалистика памятиАнализ уязвимостейЭксплуатацияИнструмент Удаленного ДоступаРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHub

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
dorkerdevil/cve-2020-28018

CVE-2020-28018

Эксплойт для Exim Use-After-Free (CVE-2020-28018), обеспечивающий удаленное выполнение кода с помощью примитивов повреждения памяти, включая произвольное чтение/запись и утечку кучи, с опциональной цепочкой локального повышения привилегий.

Репозиторий
71105 лет назадЕщё не проверено

CVE-2020-28018: Use-after-free (UAF) в Exim, приводящий к RCE

Введение

Существует уязвимость Use-after-free (UAF) в tls-openssl.c, которая позволяет удаленным неаутентифицированным злоумышленникам повредить внутренние данные памяти, что в конечном итоге приводит к удаленному выполнению кода.

Примитивы:

  • Утечка памяти
  • Произвольное чтение
  • Примитив "Запись-куда-угодно"

Используя все эти примитивы в цепочке, можно полностью обойти все доступные механизмы защиты от эксплуатации, что в конечном итоге приводит к удаленному выполнению кода от имени пользователя exim.

Эта уязвимость была опубликована в составе большого списка уязвимостей; в официальном отчете Qualys Use-After-Free объединяется с CVE-2020-28008 для выполнения локального повышения привилегий (LPE) после достижения RCE.

Предварительные требования

Exim должен быть настроен/скомпилирован следующим образом:

  • TLS включен
  • Используется OpenSSL (вместо GnuTLS)
  • Exim является одной из уязвимых версий
  • X_PIPE_CONNECT отключен

Вы можете использовать скрипт checker.py, чтобы проверить, находится ли удаленный сервер на уязвимой версии и имеет ли необходимые требования для эксплуатации.

[!] checker.py НЕ вызывает уязвимость, а только проверяет наличие уязвимой версии, а также включены ли PIPELINING и TLS. Это означает, что данный чекер не проверяет наличие исправления, что может приводить к ложным срабатываниям.

Уязвимый код

Как мы уже знаем, уязвимость находится в tls-openssl.c.

/*************************************************
*         Write bytes down TLS channel           *
*************************************************/

/*
Arguments:
  ct_ctx    client context pointer, or NULL for the one global server context
  buff      buffer of data
  len       number of bytes
  more	    further data expected soon

Returns:    the number of bytes after a successful write,
            -1 after a failed write

Used by both server-side and client-side TLS.
*/

int
tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
{
int outbytes, error, left;
SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
static gstring * corked = NULL;

DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
  buff, (unsigned long)len, more ? ", more" : "");

/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
"more" is notified.  This hack is only ok if small amounts are involved AND only
one stream does it, in one context (i.e. no store reset).  Currently it is used
for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
/*XXX + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
a store reset there. */

if (!ct_ctx && (more || corked))
  {
#ifdef EXPERIMENTAL_PIPE_CONNECT
  int save_pool = store_pool;
  store_pool = POOL_PERM;
#endif

  corked = string_catn(corked, buff, len);

#ifdef EXPERIMENTAL_PIPE_CONNECT
  store_pool = save_pool;
#endif

  if (more)
    return len;
  buff = CUS corked->s;
  len = corked->ptr;
  corked = NULL;
  }

for (left = len; left > 0;)
  {
  DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
  outbytes = SSL_write(ssl, CS buff, left);
  error = SSL_get_error(ssl, outbytes);
  DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
  switch (error)
    {
    case SSL_ERROR_SSL:
      ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
      log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
      return -1;

    case SSL_ERROR_NONE:
      left -= outbytes;
      buff += outbytes;
      break;

    case SSL_ERROR_ZERO_RETURN:
      log_write(0, LOG_MAIN, "SSL channel closed on write");
      return -1;

    case SSL_ERROR_SYSCALL:
      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
	sender_fullhost ? sender_fullhost : US"<unknown>",
	strerror(errno));
      return -1;

    default:
      log_write(0, LOG_MAIN, "SSL_write error %d", error);
      return -1;
    }
  }
return len;
}

smtp_setup_msg() — это основная функция, выполняющая чтение сообщения от клиента.

В определенных ситуациях вызывается smtp_reset(), которая очищает все буферы и значения.

Это может происходить в таких ситуациях, как:

  • Получен HELO/EHLO
  • Получен STARTTLS
  • Получен RSET
  • В начале smtp_setup_msg()

В конце smtp_reset() вызывается store_reset().

store_reset — это макрос, оборачивающий функцию store_reset_3().

Функции store — это просто функции, управляющие динамической памятью.

Exim использует пуловой аллокатор для блоков, получаемых от malloc.

Также существует интересная функциональность — реализация расширяемых строк.

Структура gstring:

typedef struct gstring {
  int   size;           /* Current capacity of string memory */
  int   ptr;            /* Offset at which to append further chars */
  uschar * s;           /* The string memory */
} gstring;

Когда требуется больше места для конкатенации новой строки, вызывается gstring_grow().

Эта функция сначала пытается вызвать store_extend_3(), которая пытается расширить память в рамках текущего блока пула.

Это может быть полезно, когда длина входных данных неизвестна, но если после нее было выделено больше памяти, расширить ее не удастся.

Затем gstring_grow() вызывает store_newblock_3(), которая просто возвращает новую память и копирует уже присутствующие байты из старой в новую.

Затем указатель g->s восстанавливается из gstring_catn().

В функции tls_write() мы видим BOOL с именем more.

Он указывает, нужно ли скопировать еще данные в строковый буфер перед возвратом данных пользователю.

Если да, указатель не обнуляется.

Если нет, то данные, содержащиеся в строковом буфере, возвращаются пользователю.

Эта функциональность открывает некоторые интересные способы вызвать Use-After-Free.

Во-первых, указатель на структуру gstring хранится в статической переменной, что означает, что при последующих вызовах tls_write() мы сможем его использовать.

Как мы можем освободить буфер, а затем иметь возможность его использовать?

Нам нужно, чтобы smtp_setup_msg() вызвала smtp_reset() после того, как один из наших буферов все еще находится в server_corked (не обнулен).

После сброса, если мы каким-то образом вызовем tls_write(), указатель все еще будет там, что позволит нам использовать его после освобождения памяти.

smtp_reset() освобождает всю память POOL_MAIN, в которой содержится наш буфер.

Вызов Use-After-Free

Чтобы контролировать Use-After-Free, нам сначала нужно инициализировать новое соединение.

Так как мы хотим эксплуатировать tls_write(), нам сначала нужно начать новую TLS-сессию.

Итак, сначала мы отправляем команду EHLO, за которой следует STARTTLS для запуска TLS-соединения.

Затем, чтобы more стал 1, мы конвейеризируем команду, и последней будет половина NOOP.

Мы закрываем TLS-соединение и отправляем оставшуюся часть команды NOOP.

Теперь мы снова отправляем EHLO, что вызовет smtp_reset и освободит наш буфер.

Теперь нам нужно запустить другое TLS-соединение, чтобы снова иметь возможность использовать tls_write().

Мы отправляем STARTTLS.

Теперь отправка любой команды серверу приведет к вызову tls_write() для возврата ответа.

Но... server_corked все еще содержит указатель на какое-то место в освобожденной памяти.

И эти данные могут быть использованы другими функциями, так как они освобождены... таким образом, наша структура gstring будет повреждена случайными двоичными данными.

Вот результат срабатывания UAF:

gef➤  p *corked
$1 = {
  size = 0x54595c9c, 
  ptr = 0xa7e800ba, 
  s = 0x7e35043433160bd3 <error: Cannot access memory at address 0x7e35043433160bd3>
}
gef➤  p corked
$2 = (gstring *) 0x555ad3be1b58
gef➤  

Эта структура находится в таком состоянии как раз при входе в tls_write() для нашей команды после STARTTLS.

Скачать инструмент