Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2020-28018 — Exploit para Exim Use-After-Free (CVE-2020-28018) que permite execução remota de código através de primitivas de corrupção de memória, incluindo leitura/escrita arbitrária e vazamento de heap, com cadeia opcional de escalonamento de privilégio local. | Kitploit
Ferramentas/GitHubGitHub/dorkerdevil/cve-2020-28018
Escalada de PrivilégiosForensia de MemóriaAnálise de VulnerabilidadesExploraçãoFerramenta de Acesso RemotoDesenvolvimento de PayloadsExploração de Binários
GitHub

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
dorkerdevil/cve-2020-28018

CVE-2020-28018

Exploit para Exim Use-After-Free (CVE-2020-28018) que permite execução remota de código através de primitivas de corrupção de memória, incluindo leitura/escrita arbitrária e vazamento de heap, com cadeia opcional de escalonamento de privilégio local.

Ver Repositório
7112há 5 anosAinda não revisado

CVE-2020-28018: Use-after-free (UAF) no Exim levando a RCE

Introdução

Existe uma vulnerabilidade de Use-after-free (UAF) em tls-openssl.c que permite que atacantes remotos não autenticados corrompam dados internos da memória, eventualmente alcançando execução remota de código.

Primitivas:

  • Vazamento de Memória
  • Primitiva de leitura arbitrária
  • Primitiva Write-What-Where

Com o uso de todas essas primitivas encadeadas, é possível contornar completamente todas as mitigações de exploração disponíveis, resultando em execução remota de código como o usuário exim.

Esta vulnerabilidade foi divulgada em uma grande lista de vulnerabilidades; o relatório oficial da Qualys encadeia o Use-After-Free com CVE-2020-28008 para realizar uma Escalação de Privilégio Local (LPE) uma vez que a RCE foi alcançada.

Pré-requisitos

O exim deve ser configurado/compilado da seguinte forma:

  • TLS está habilitado
  • OpenSSL é usado (em vez de GnuTLS)
  • O Exim está em uma das versões vulneráveis
  • X_PIPE_CONNECT está desabilitado

Você pode usar o script checker.py para verificar se um servidor remoto está em uma versão vulnerável e possui alguns requisitos necessários para ser explorável.

[!] checker.py NÃO aciona a vulnerabilidade, apenas verifica a versão vulnerável, verifica se PIPELINING e TLS estão ativados. Isso significa que este verificador não verifica o patch, o que pode gerar falsos positivos.

Código vulnerável

Como já sabemos, a vulnerabilidade está localizada em 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() é a função principal que realiza a leitura da mensagem do cliente.

Em situações específicas, smtp_reset() é chamada, que realiza uma limpeza de todos os buffers e valores.

Isso pode acontecer em situações como:

  • HELO/EHLO é recebido
  • STARTTLS é recebido
  • RSET é recebido
  • No início de smtp_setup_msg()

No final de smtp_reset(), uma chamada a store_reset() é realizada.

store_reset é uma macro que envolve a função store_reset_3().

As funções de store são apenas funções que gerenciam a memória dinâmica.

O Exim usa um alocador de pool em blocos recebidos do malloc.

Existe também uma funcionalidade interessante que é uma implementação de string expansível.

Struct 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;

Quando precisa de mais espaço para concatenar uma nova string, ele chama gstring_grow().

Essa função primeiro tenta chamar store_extend_3(), que tenta estender a memória dentro do mesmo bloco do pool.

Isso pode ser útil quando o comprimento da entrada não é conhecido, mas se mais memória foi alocada depois dela, não conseguiremos estendê-la.

Então gstring_grow() chama store_newblock_3() que apenas retorna uma nova memória e copia os bytes já presentes na anterior para a nova.

Em seguida, o ponteiro g->s é restaurado a partir de gstring_catn().

Na função tls_write(), podemos ver que há um BOOL chamado more.

Ele indica se há mais coisas a serem copiadas para o buffer de string antes de retornar os dados ao usuário.

Se sim, o ponteiro não é anulado.

Se não, então os dados contidos no buffer de string são retornados ao usuário.

Essa funcionalidade abre algumas maneiras interessantes de desencadear um Use-After-Free.

Primeiro, o ponteiro para a struct gstring é armazenado em uma variável estática, o que significa que em futuras chamadas para tls_write() poderemos usá-lo.

Como podemos liberar o buffer e depois ser capazes de usá-lo?

Precisamos fazer com que smtp_setup_msg() chame smtp_reset() depois que um de nossos buffers ainda estiver em server_corked (não anulado).

Após o reset, se chamarmos tls_write() de alguma forma, o ponteiro ainda estará lá, permitindo-nos usá-lo após a memória ter sido liberada.

smtp_reset() libera toda a memória de POOL_MAIN, na qual nosso buffer está contido.

Acionando o Use-After-Free

Para controlar o Use-After-Free, primeiro precisamos inicializar uma nova conexão.

Como queremos explorar o tls_write(), primeiro precisamos iniciar uma nova sessão TLS.

Então, primeiro enviamos um comando EHLO, seguido de um STARTTLS para iniciar a conexão TLS.

Em seguida, para fazer more ser 1, enviamos um pipeline de comandos, e o último será a metade de um NOOP.

Fechamos a conexão TLS e enviamos o restante do comando NOOP.

Agora enviamos EHLO novamente, o que fará com que smtp_reset seja chamado e libere nosso buffer.

Agora precisamos iniciar outra conexão TLS para poder usar tls_write() novamente.

Enviamos STARTTLS.

Agora, enviar qualquer comando para o servidor resultará na chamada de tls_write() para retornar uma resposta.

Mas... server_corked ainda contém um ponteiro para algum lugar na memória liberada.

E esses dados podem ser usados por outras funções, pois estão liberados... então nossa struct gstring será corrompida com dados binários aleatórios.

Este é o resultado de acionar o 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➤  
Baixar ferramenta