Aikido

Múltiplas Vulnerabilidades de cross-site scripting em Mailcow

Escrito por
Jorian Woltjer

Mailcow é um servidor de e-mail auto-hospedado e de código aberto amplamente utilizado que hospeda tudo o que você precisaria para gerenciar caixas de correio por conta própria. Para avaliar sua segurança, configuramos uma instância local e executamos nossos agentes de pentest de IA contra ela. Encontramos três vulnerabilidades XSS, incluindo uma vulnerabilidade crítica que permitia a atacantes não autenticados assumir contas de administrador ao visualizar seus logs na UI.

Obter acesso a uma caixa de correio pode ter um sério impacto na segurança. Dados sensíveis encontrados em e-mails podem ser capturados, mas o acesso também permite que atacantes usem a funcionalidade de redefinição de senha para comprometer outras contas conectadas da vítima. Isso é exatamente o que teria sido possível se alguém tivesse explorado essas vulnerabilidades.

Todas as vulnerabilidades foram divulgadas de forma responsável ao Mailcow e foram corrigidas desde a versão 2026-03b (lançada em 31 de março de 2026). Gostaríamos de agradecer ao mantenedor, FreddleSpl0it, pelo processo tranquilo e pela correção rápida.

Logs de Autodiscover não escapados

Reportada no GitHub como GHSA-f9xf-vc72-rcgm, esta vulnerabilidade permitia que atacantes não autenticados enviassem uma requisição Autodiscover contendo um endereço de e-mail malicioso, que apareceria nos logs. Quando um administrador visualiza esses logs posteriormente, o campo do endereço de e-mail é exibido sem escape, permitindo injeção de HTML e XSS.

Vamos começar pelo sink. O Mailcow permite a visualização de logs do Autodiscover em uma tabela no painel de administração, que é renderizada usando DataTables em dashboard.js. Esta biblioteca tem a armadilha comum de interpretar quaisquer valores de dados como HTML por padrão. Todos os valores precisam ser devidamente escapados previamente.

var table = $('#autodiscover_log').DataTable({
  ...
  ajax: {
    type: "GET",
    url: "/api/v1/get/logs/autodiscover/100",
    dataSrc: function(data){
      return process_table_data(data, 'autodiscover_log');
    }
  },

O process_table_data() função tenta escapar a entrada do usuário em cada linha. Para logs do Autodiscover,  item.ua (User Agent) é corretamente escapado usando escapeHtml (dashboard.js):

} else if (table == 'autodiscover_log') { 
   $.each(data, function (i, item) { 
     if (item.ua == null) { 
       item.ua = 'unknown'; 
     } else { 
       item.ua = escapeHtml(item.ua); 
     } 
     item.ua = '<span style="font-size:small">' + item.ua + '</span>';

No entanto, uma coluna, item.name, não é escapada. Este é o endereço de e-mail enviado na requisição Autodiscover.

A parte mais perigosa? Uma requisição Autodiscover é não autenticada por design, e neste caso, o endereço de e-mail não é validado. Ele acabará nos logs, e uma vez que um administrador os visualize, será passado por essas funções vulneráveis para ser renderizado como HTML arbitrário.

Abaixo está um exemplo de requisição que injeta <img src=x onerror=alert(origin)> na tabela:

POST /Autodiscover/Autodiscover.xml HTTP/2
Host: 127.0.0.1
Content-Type: text/xml
Content-Length: 384

<?xml version='1.0' encoding='utf-8'?>
<Autodiscover xmlns='http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006'>
  <Request>
    <EMailAddress>&lt;img src=x onerror=alert(origin)&gt;</EMailAddress>
    <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
  </Request>
</Autodiscover>

Quando um administrador agora visualiza os logs do Autodiscover (encontrados em Dashboard -> Logs -> Autodiscover), o JavaScript é executado e mostra um pop-up de alerta, provando XSS na origem do Mailcow.

Na cópia local do mailcow, há um pop-up que diz "localhost says https://localhost

Como isso afeta apenas administradores, atacantes podem acessar a caixa de correio de qualquer usuário e reconfigurar a instância. Isso conferiu à vulnerabilidade uma severidade crítica.

Este problema foi corrigido adicionando uma escapeHtml() chamada em torno de item.user ao processar as linhas.

Injeção de nomes de arquivo de anexos em quarentena

Reportada como GHSA-2xjc-rg88-jvpp, esta vulnerabilidade reside no recurso de Quarentena do Mailcow, onde os administradores podem investigar anexos sinalizados. Os nomes de arquivo desses anexos eram exibidos sem o Escape de HTML, abrindo a porta para XSS em qualquer administrador que os visualizasse. A exploração disso não exigia autenticação por parte do atacante, embora exigisse que o recurso de Quarentena estivesse habilitado na instância alvo.

O sink está claro desta vez. Dentro de quarentine.js, há um trecho de código concatenando HTML com dados dinâmicos:

$.each(data.attachments, function(index, value) { 
   qAtts.append( 
     '<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' + 
     ' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>' 
   ); 
 });

Se algum desses for controlado pelo usuário, isso poderá resultar em injeção de HTML novamente. Esses valores são nome do arquivo, tipo MIME, tamanho do arquivo e virustotal sha256, respectivamente. Desses, o nome do arquivo parece ser o mais propenso a conter entrada do usuário, já que o atacante pode enviar um e-mail com um anexo que possui um nome de arquivo especial consistindo em tags HTML. 

When testing, it turns out that, as expected, no strict validation takes place on this filename. It can contain <> characters, which is all that's needed to inject an XSS payload. The remaining challenge is getting the email into the quarantine queue in the first place, so an admin will open and inspect it. The attacker solves this by attaching an EICAR antivirus test file, which is a standardized string that every antivirus engine is guaranteed to flag. This reliably routes the email to quarantine without requiring the attacker to send actual malware.

O código abaixo gera um anexo com a string EICAR como conteúdo e um payload XSS como nome do arquivo (veja o aviso para um script completo):

# Malicious filename attachment with EICAR to trigger quarantine
att = MIMEApplication(b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*', _subtype='octet-stream')
att.add_header('Content-Disposition', 'attachment', filename='<img src=x onerror=alert(origin)>.exe')
msg.attach(att)

Após enviar o e-mail para o Mailcow e ele ser colocado em quarentena, o administrador pode abrir a página de Quarentena para vê-lo. Assim que ele clica em Mostrar item, nosso nome de arquivo é renderizado e o XSS é acionado:

Note que isso também não exige autenticação no Mailcow para ser explorado. Tudo o que é necessário é enviar um e-mail maliciosamente elaborado para o domínio do Mailcow, e que o administrador o investigue. A partir daqui, toda a instância poderia ser comprometida usando JavaScript, lendo caixas de correio novamente e configurando a instância.

Este problema foi corrigido adicionando chamadas escapeHtml() em torno dos valores dos anexos.

Elevando um Self-XSS em IP listado no Histórico de Login

A última vulnerabilidade XSS, reportada como GHSA-jprq-w83q-q62h, foi tecnicamente mais interessante de explorar. O payload XSS é armazenado na própria conta do atacante, o que significa que, normalmente, por si só, apenas o atacante o acionaria (um "Self-XSS" sem impacto real). O ataque se torna perigoso quando combinado com um Login CSRF, que força o navegador da vítima a fazer login na conta do atacante, colocando o payload armazenado na frente da pessoa errada.

A vulnerabilidade começa com outro sink de concatenação HTML simples em user.js:

var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.tools/prefix/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";

O real_rip (IP Remoto Real) é renderizado na tabela de logins recentes sem ser escapado. Normalmente, isso não deveria ser um problema; endereços IP seguem um formato muito rigoroso, sem espaço para payloads XSS.

No entanto, os endereços IP não são validados pelo Mailcow e vêm diretamente do X-Real-IP: cabeçalho por padrão. Alguns proxies definem este cabeçalho para o IP remoto do usuário, mas se nenhum proxy desse tipo existir, qualquer valor atribuído a ele será confiável. Portanto, você pode fazer login enquanto define X-Real-IP: "><img src onerror=alert(origin)> que salva o valor nas entradas de login recentes. Quando visualizado (via /user), o payload XSS é renderizado e executa o alerta.

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-Real-Ip: "><img src onerror=alert(origin)>

login_user=attacker&pass_user=attacker

No entanto, este não é o fim da história. Porque uma vítima não fará login com este cabeçalho IP falso, nem fará login na conta do atacante do nada. Por enquanto, é um Self-XSS.

Duas partes desta vulnerabilidade tornam a escalada possível. Primeiro, o payload é armazenado persistentemente na conta do atacante. Segundo, a requisição de login não possui proteção CSRF, o que significa que pode ser acionada entre sites. Um atacante pode hospedar um formulário em seu próprio domínio que envia automaticamente as credenciais do atacante, fazendo com que a vítima faça login sem qualquer interação além de visitar a página.

Um atacante pode hospedar o seguinte formulário em seu domínio malicioso:

<form action="http://mailcow.local/" method="POST">
    <input type="text" name="login_user" value="attacker">
    <input type="text" name="pass_user" value="attacker">
</form>
<script>
    document.forms[0].submit();
</script>

Assim que a vítima abre este formulário hospedado no site malicioso, ela é automaticamente logada na conta do atacante. Podemos então redirecioná-la para /user para acionar o XSS que armazenamos anteriormente. Mas espere um segundo, a vítima está agora logada na conta do atacante, então como o atacante pode roubar os dados da vítima dados?

Aqui, o atacante pode usar um truque inteligente: manter a caixa de entrada da vítima aberta até depois do Login CSRF & XSS, e então ler a partir dela usando acesso de mesma origem.

O novo fluxo será assim:

  1. A vítima navega para nossa página maliciosa (tab 1)
  2. A tab 1 abre uma segunda página maliciosa (tab 2) que contém o formulário de login CSRF, com um pequeno atraso antes de ser executada
  3. A tab 1 redireciona para a caixa de entrada da vítima, carregando os dados que o atacante deseja roubar
  4. A tab 2 submete o formulário de login CSRF, abrindo uma nova tab 3 que loga a vítima na conta do atacante
  5. A tab 2 redireciona-se para /user, onde o XSS Armazenado é acionado
  6. O XSS na tab 2 usa uma referência para opener para ler e exfiltrar document.body.innerHTML na tab 1, onde a caixa de entrada da vítima ainda está aberta

O diagrama mostra três caixas representando abas do navegador. 1. Mailbox 2. XSS 3. CSRF. As duas últimas pertencem ao atacante.

Isso torna possível ler e-mails da conta da vítima porque eles foram buscados enquanto ela ainda estava logada em sua conta!

Consulte o aviso para detalhes completos sobre como o fluxo de exploração funciona na prática. Múltiplos arquivos HTML com controle preciso sobre o fluxo permitem que um atacante roube e-mails em apenas dois cliques (necessários para abrir as duas abas extras).

Este problema foi corrigido adicionando chamadas escapeHtml() ao redor da renderização do IP.

Remediação

Atualize o Mailcow para a versão 2026-03b ou posterior, lançada em 31 de março de 2026. Todas as três vulnerabilidades XSS são corrigidas nesta versão. Se você usa o Aikido, instâncias vulneráveis do Mailcow são automaticamente sinalizadas em seu feed de monitoramento de superfície como uma descoberta crítica.

Dashboard da Aikido mostrando uma pontuação de 95: Crítico para Mailcow. Ele também contém um TL;DR e passos para corrigi-lo.

Ainda não usa o Aikido? Crie uma conta gratuita para começar — não é necessário cartão de crédito.

Conclusão

Um padrão que observamos durante essas divulgações é que a concatenação de strings HTML continua sendo uma má ideia. Engines de template HTML modernos fizeram grandes melhorias na maioria das aplicações, mas aplicações mais antigas sem frameworks seguros não têm esse luxo. Às vezes, elas voltam a concatenar strings em JavaScript, o que, como visto aqui, rapidamente leva ao esquecimento de escapar a entrada do usuário.

Aplicações de caixa de correio contêm informações altamente sensíveis, por isso devem ser tratadas com muito cuidado. Tais aplicações devem ser auditadas rigorosamente, pois o comprometimento de uma pode levar a mecanismos de redefinição de senha, resultando no comprometimento de muitas outras contas conectadas.

Aikido Attack (pentest de IA) encontra esses tipos de vulnerabilidades em aplicações, de forma completamente automatizada. Se ver esses resultados o faz questionar sobre vulnerabilidades XSS em suas próprias aplicações, cadastre-se ou entre em contato!

Compartilhar:

https://www.aikido.dev/blog/xss-vulnerabilities-in-mailcow

Assine para receber notícias

4.7/5
Cansado de falsos positivos?

Experimente Aikido como 100 mil outros.
Começar Agora
Obtenha um tour personalizado

Confiado por mais de 100 mil equipes

Agende Agora
Escaneie seu aplicativo em busca de IDORs e caminhos de ataque reais

Confiado por mais de 100 mil equipes

Iniciar Escaneamento
Veja como o pentest de IA testa seu aplicativo

Confiado por mais de 100 mil equipes

Iniciar Testes

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.