O Mailcow é um servidor de e-mail de código aberto e auto-hospedado amplamente utilizado, que inclui tudo o que é necessário para gerir as caixas de correio por conta própria. Para avaliar a sua segurança, configurámos uma instância local e executámos o nosso pentest de IA nele. Encontrámos três vulnerabilidades XSS, incluindo uma vulnerabilidade crítica que permitia a atacantes não autenticados assumir o controlo de contas de administrador enquanto visualizavam os seus registos na interface do utilizador.
O acesso a uma caixa de correio eletrónico pode ter graves consequências para a segurança. Os dados confidenciais contidos nos e-mails podem ser capturados, mas esse acesso também permite que os atacantes utilizem a funcionalidade de redefinição de palavra-passe para comprometer outras contas associadas da vítima. Era exatamente isso que teria sido possível se alguém tivesse explorado estas vulnerabilidades.
Todas as vulnerabilidades foram comunicadas de forma responsável à Mailcow e foram corrigidas a partir da versão 2026-03b (lançada a 31 de março de 2026). Gostaríamos de agradecer ao mantenedor, FreddleSpl0it, pelo processo tranquilo e pela correção rápida.
Registos de Autodiscover sem caracteres de escape
Relatada no GitHub como GHSA-f9xf-vc72-rcgm, esta vulnerabilidade permitia que atacantes não autenticados enviassem um pedido de Autodiscover contendo um endereço de e-mail malicioso, que aparecia nos registos. Quando um administrador visualiza posteriormente esses registos, o campo do endereço de e-mail é apresentado sem ser escapado, permitindo a injeção de HTML e XSS.
Vamos começar pelo lavatório. O Mailcow permite visualizar os registos do Autodiscover numa tabela no painel de administração, que é apresentada utilizando o DataTables no ficheiro dashboard.js. Esta biblioteca tem a armadilha comum de interpretar, por predefinição, quaisquer valores de dados como HTML. Todos os valores têm de 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() A função tenta escape os dados introduzidos escape em cada linha. No caso dos registos do Autodiscover, item.ua (User Agent) é corretamente escapado utilizando 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 está protegida. Trata-se do endereço de e-mail enviado na solicitação de Autodiscover.
Qual é a parte mais perigosa? Por definição, um pedido de Autodiscover não é autenticado e, neste caso, o endereço de e-mail não é validado. Acabará por constar nos registos e, assim que um administrador os consultar, será processado por estas funções vulneráveis para ser apresentado como HTML arbitrário.
Segue-se um exemplo de pedido que insere <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><img src=x onerror=alert(origin)></EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
Quando um administrador consulta agora os registos do Autodiscover (disponíveis em Painel -> Registos -> Autodiscover), o JavaScript é executado e exibe uma janela pop-up de alerta, comprovando a existência de XSS na origem do Mailcow.

Uma vez que isto afeta apenas os administradores, os atacantes podem aceder à caixa de correio de qualquer utilizador e reconfigurar a instância. Isto conferiu à vulnerabilidade um nível de gravidade crítico.
Esta edição foi corrigido adicionando um escapeHtml() chamar o item.user ao processar as linhas.
Inserir nomes de ficheiros de anexos em quarentena
Registada como GHSA-2xjc-rg88-jvpp, esta vulnerabilidade reside na funcionalidade «Quarentena» do Mailcow, onde os administradores podem investigar anexos sinalizados. Os nomes dos ficheiros desses anexos eram apresentados sem codificação HTML, abrindo a porta a ataques XSS contra qualquer administrador que os visualizasse. A exploração desta vulnerabilidade não exigia autenticação por parte do atacante, embora fosse necessário que a funcionalidade «Quarentena» estivesse ativada na instância alvo.
Desta vez, o problema está resolvido. No arquivo quarentine.js, há um trecho de código que concatena 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 destes parâmetros for controlado pelo utilizador, isso poderá resultar novamente numa injeção de HTML. Estes valores são , respetivamente , o nome do ficheiro, o tipo MIME, o tamanho do ficheiro e o SHA-256 do VirusTotal. Destes, o nome do ficheiro parece ser o que tem maior probabilidade de conter dados introduzidos pelo utilizador, uma vez que o atacante pode enviar um e-mail com um anexo que tenha um nome de ficheiro especial composto por 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 sequência EICAR como conteúdo e uma carga XSS como nome do ficheiro (consulte o aviso para obter o 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)
Depois de enviar o e-mail para o Mailcow e de este ter sido colocado em quarentena, o administrador pode aceder à página de quarentena para o visualizar. Assim que clicar em «Mostrar item», o nome do nosso ficheiro é apresentado e o XSS é acionado:

Note-se que, para explorar esta vulnerabilidade, também não é necessária autenticação no Mailcow. Basta enviar um e-mail malicioso para o domínio do Mailcow e que o administrador o analise. A partir daí, é possível assumir o controlo de toda a instância utilizando JavaScript, aceder novamente às caixas de correio e configurar a instância.
Este problema foi resolvido através da adição de chamadas à função escapeHtml() em torno dos valores dos anexos.
Aplicação de um ataque Self-XSS num endereço IP constante do histórico de inícios de sessão
A última vulnerabilidade XSS, identificada como GHSA-jprq-w83q-q62h, revelou-se tecnicamente mais interessante de explorar. A carga útil XSS é armazenada na própria conta do atacante, o que significa que, normalmente, por si só, apenas o atacante a ativaria (um «Self-XSS» sem impacto real). O ataque torna-se perigoso quando combinado com um CSRF de login, que força o navegador da vítima a iniciar sessão na conta do atacante, expondo a carga útil armazenada à pessoa errada.
A vulnerabilidade começa com outro mecanismo de concatenação HTML simples no ficheiro 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) é apresentado na tabela de inícios de sessão recentes sem ser codificado. Normalmente, isto não deveria constituir um problema; os endereços IP seguem um formato muito rigoroso, sem margem para cargas de XSS.
No entanto, os endereços IP não são validados pelo Mailcow e provêm diretamente do X-Real-IP: cabeçalho por predefinição. Alguns proxies definem este cabeçalho com o IP remoto do utilizador, mas, caso não exista tal proxy, qualquer valor que lhe seja atribuído será considerado válido. Por conseguinte, pode iniciar sessão definindo X-Real-IP: "><img src onerror=alert(origin)> que guarda o valor nas entradas de inícios de sessão recentes. Quando visualizado (através de /utilizador), a carga útil XSS é renderizada 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, a história não acaba aqui. Porque uma vítima não vai iniciar sessão com este cabeçalho de IP falso, nem vai iniciar sessão na conta do atacante do nada. Por enquanto, trata-se de um Self-XSS.
Há dois aspetos desta vulnerabilidade que tornam possível a escalada de privilégios. Em primeiro lugar, a carga maliciosa é armazenada de forma persistente na conta do atacante. Em segundo lugar, o pedido de início de sessão não possui proteção contra CSRF, o que significa que pode ser acionado entre sites. Um atacante pode hospedar um formulário no seu próprio domínio que envie automaticamente as suas credenciais, iniciando a sessão da vítima sem qualquer interação além da visita à página.
Um atacante pode hospedar o seguinte formulário no 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, é automaticamente iniciada sessão na conta do atacante. Podemos então redirecioná-la para /utilizador para acionar o XSS que armazenámos anteriormente. Mas espere um pouco, a vítima está agora conectada à conta do atacante, então como é que o atacante pode roubar da vítima dados?
Neste caso, o atacante pode recorrer a um truque engenhoso: manter a caixa de correio da vítima aberta até depois do ataque CSRF e XSS de login e, em seguida, ler o seu conteúdo através do acesso da mesma origem.
O novo fluxo ficará assim:
- A vítima acede à nossa página maliciosa (separador 1)
- O separador 1 abre uma segunda página maliciosa (separador 2) que contém o formulário CSRF de início de sessão, com um ligeiro atraso antes de ser executado
- O separador 1 redireciona para a caixa de correio da vítima, carregando os dados que o atacante pretende roubar
- O separador 2 envia o formulário CSRF de início de sessão, abrindo um novo separador 3 que inicia sessão na conta do atacante em nome da vítima
- O separador 2 redireciona-se para
/utilizador, onde o XSS armazenado é acionado - O XSS no separador 2 utiliza uma referência a abertura para ler e extrair
document.body.innerHTMLno separador 1, onde a caixa de correio da vítima ainda está aberta

Isto permite ler os e-mails da conta da vítima, uma vez que foram obtidos enquanto esta ainda estava conectada à sua conta!
Consulte o aviso para obter todos os detalhes sobre como o fluxo da exploração funciona na prática. Vários ficheiros HTML com controlo preciso sobre o fluxo permitem que um atacante roube e-mails com apenas dois cliques (necessários para abrir as duas abas adicionais).
Este problema foi resolvido através da adição de chamadas à função escapeHtml() na renderização do IP.
Remediação
Atualize o Mailcow para a versão 2026-03b ou posterior, lançada a 31 de março de 2026. Todas as três vulnerabilidades XSS foram corrigidas nesta versão. Se utilizar Aikido, as instâncias vulneráveis do Mailcow são automaticamente sinalizadas no seu feed de monitorização de superfície como uma detecção crítica.

Aikido não está no Aikido ? Crie uma conta gratuita para começar — não é necessário cartão de crédito.
Conclusão
Uma tendência que temos observado durante estas revelações é que a concatenação de cadeias de caracteres HTML continua a ser uma má ideia. Os motores de modelos HTML modernos introduziram grandes melhorias na maioria das aplicações, mas as aplicações mais antigas, sem frameworks seguros, não dispõem desse luxo. Por vezes, recorrem à concatenação de cadeias de caracteres em JavaScript, o que, como se vê aqui, leva rapidamente a que se esqueça de escape os dados escape .
As aplicações de correio eletrónico contêm informações altamente confidenciais, pelo que devem ser tratadas com muito cuidado. Essas aplicações devem ser rigorosamente auditadas, uma vez que a violação de uma delas pode acionar mecanismos de redefinição de palavra-passe, resultando na violação de muitas outras contas associadas.
Aikido (pentest de IA) identifica este tipo de vulnerabilidades nas aplicações, de forma totalmente automatizada. Se estes resultados o levaram a questionar-se sobre a existência de vulnerabilidades XSS nas suas próprias aplicações, inscreva-se ou entre em contacto connosco!

