Mailcow est un serveur de messagerie auto-hébergé et open source largement utilisé qui héberge tout ce dont vous auriez besoin pour gérer vous-même vos boîtes aux lettres. Pour évaluer sa sécurité, nous avons configuré une instance locale et y avons exécuté nos agents de pentest IA. Nous avons découvert trois vulnérabilités XSS, dont une vulnérabilité critique qui permettait à des attaquants non authentifiés de prendre le contrôle de comptes administrateur en consultant leurs logs dans l'interface utilisateur.
Obtenir l'accès à une boîte aux lettres peut avoir un impact sérieux sur la sécurité. Des données sensibles trouvées dans les e-mails peuvent être capturées, mais l'accès permet également aux attaquants d'utiliser la fonctionnalité de réinitialisation de mot de passe pour compromettre les autres comptes connectés de la victime. C'est exactement ce qui aurait été possible si quelqu'un avait exploité ces vulnérabilités.
Toutes les vulnérabilités ont été divulguées de manière responsable à Mailcow, et ont été corrigées depuis la version 2026-03b (publiée le 31 mars 2026). Nous tenons à remercier le mainteneur, FreddleSpl0it, pour le processus fluide et la correction rapide.
Logs Autodiscover non échappés
Signalée sur GitHub sous la référence GHSA-f9xf-vc72-rcgm, cette vulnérabilité permettait à des attaquants non authentifiés d'envoyer une requête Autodiscover contenant une adresse e-mail malveillante, qui apparaissait dans les logs. Lorsqu'un administrateur consultait ensuite ces logs, le champ de l'adresse e-mail était affiché sans être échappé, permettant l'injection HTML et le XSS.
Commençons par la source de la vulnérabilité. Mailcow permet de visualiser les logs Autodiscover dans un tableau du panneau d'administration, qui est rendu à l'aide de DataTables dans dashboard.js. Cette bibliothèque présente l'écueil courant d'interpréter toutes les valeurs de données comme du HTML par défaut. Toutes les valeurs doivent être correctement échappées au préalable.
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');
}
},
Le process_table_data() la fonction tente d'échapper les entrées utilisateur dans chaque ligne. Pour les logs Autodiscover, item.ua (User Agent) est correctement échappé en utilisant 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>';Cependant, une colonne, item.name, n'est pas échappée. Il s'agit de l'adresse e-mail envoyée dans la requête Autodiscover.
Le plus dangereux ? Une requête Autodiscover est non authentifiée par conception, et dans ce cas, l'adresse e-mail n'est pas validée. Elle se retrouvera dans les logs, et une fois qu'un administrateur les consultera, elle sera traitée par ces fonctions vulnérables pour être rendue comme du HTML arbitraire.
Ci-dessous se trouve un exemple de requête qui injecte <img src=x onerror=alert(origin)> dans le tableau :
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>
Lorsqu'un administrateur consulte maintenant les logs Autodiscover (situés dans Dashboard -> Logs -> Autodiscover), le JavaScript s'exécute et affiche une fenêtre d'alerte pop-up, prouvant une XSS dans l'origine Mailcow.

Puisque cela n'affecte que les administrateurs, les attaquants peuvent accéder à la boîte aux lettres de n'importe quel utilisateur et reconfigurer l'instance. Cela a conféré à la vulnérabilité une sévérité critique.
Ce problème a été corrigé en ajoutant un escapeHtml() appel autour de item.user lors du traitement des lignes.
Injection de noms de fichiers de pièces jointes en quarantaine
Signalée sous la référence GHSA-2xjc-rg88-jvpp, cette vulnérabilité réside dans la fonctionnalité de quarantaine de Mailcow, où les administrateurs peuvent examiner les pièces jointes signalées. Les noms de fichiers de ces pièces jointes étaient affichés sans échappement HTML, ouvrant la porte à une XSS pour tout administrateur qui les consultait. L'exploitation de cette vulnérabilité ne nécessitait aucune authentification côté attaquant, bien qu'elle exigeait que la fonctionnalité de quarantaine soit activée sur l'instance cible.
Le point d'injection est clair cette fois. Dans quarentine.js, il y a un morceau de code qui concatène du HTML avec des données dynamiques :
$.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>'
);
});
Si l'une de ces valeurs est contrôlée par l'utilisateur, cela pourrait entraîner une nouvelle injection HTML. Ces valeurs sont respectivement le nom du fichier, le type MIME, la taille du fichier et le SHA256 de Virustotal. Parmi celles-ci, le nom du fichier semble le plus susceptible de contenir une entrée utilisateur, car l'attaquant peut envoyer un e-mail avec une pièce jointe dont le nom de fichier est spécial et contient des balises 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.
Le code ci-dessous génère une pièce jointe avec la chaîne EICAR comme contenu, et une charge utile XSS comme nom de fichier (voir l'avis pour le script complet) :
# 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)
Après avoir envoyé l'e-mail à Mailcow et qu'il ait été mis en quarantaine, l'administrateur peut ouvrir la page Quarantaine pour le consulter. Une fois qu'il clique sur Afficher l'élément, notre nom de fichier est rendu et la XSS se déclenche :

Il est à noter que cela ne nécessite pas non plus d'authentification sur Mailcow pour être exploité. Tout ce qui est nécessaire est d'envoyer un e-mail malveillant au domaine Mailcow, et que l'administrateur l'examine. À partir de là, toute l'instance pourrait être compromise via JavaScript, permettant de lire à nouveau les boîtes aux lettres et de configurer l'instance.
Ce problème a été corrigé en ajoutant des appels à escapeHtml() autour des valeurs des pièces jointes.
Élévation d'une Self-XSS dans une IP listée dans l'historique de connexion
La dernière vulnérabilité XSS, répertoriée sous GHSA-jprq-w83q-q62h, était techniquement plus intéressante à exploiter. La charge utile XSS est stockée sous le propre compte de l'attaquant, ce qui signifie que normalement, seule l'attaquant pourrait la déclencher (un "Self-XSS" sans réel impact). L'attaque devient dangereuse lorsqu'elle est combinée à une Login CSRF, qui force le navigateur d'une victime à se connecter au compte de l'attaquant, plaçant ainsi la charge utile stockée devant la mauvaise personne.
La vulnérabilité commence par une autre simple injection par concaténation HTML dans 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>";Le real_rip (IP distante réelle) est affiché dans le tableau des connexions récentes sans être échappé. Normalement, cela ne devrait pas poser de problème ; les adresses IP suivent un format très strict ne laissant aucune place aux charges utiles XSS.
Cependant, les adresses IP ne sont pas validées par Mailcow et proviennent directement du X-Real-IP: en-tête par défaut. Certains proxys définissent cet en-tête à l'IP distante de l'utilisateur, mais si aucun proxy de ce type n'existe, toute valeur qui lui est donnée sera considérée comme fiable. Par conséquent, vous pouvez vous connecter en définissant X-Real-IP: "><img src onerror=alert(origin)> ce qui enregistre la valeur dans les entrées de connexion récentes. Lors de la consultation (via /user), la charge utile XSS est rendue et exécute l'alerte.
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
Cependant, ce n'est pas la fin de l'histoire. Car une victime ne se connectera pas avec ce faux en-tête IP, ni ne se connectera au compte de l'attaquant sans raison. Pour l'instant, il s'agit d'un Self-XSS.
Deux éléments de cette vulnérabilité rendent l'escalade possible. Premièrement, la charge utile est stockée de manière persistante sous le compte de l'attaquant. Deuxièmement, la requête de connexion n'a aucune protection CSRF, ce qui signifie qu'elle peut être déclenchée de manière inter-site. Un attaquant peut héberger un formulaire sur son propre domaine qui soumet automatiquement les identifiants de l'attaquant, connectant la victime sans aucune interaction au-delà de la visite de la page.
Un attaquant peut héberger le formulaire suivant sur son domaine malveillant :
<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>
Dès qu'une victime ouvre ce formulaire hébergé sur le site web malveillant, elle est automatiquement connectée au compte de l'attaquant. Nous pouvons ensuite la rediriger vers /user pour déclencher le XSS que nous avons stocké précédemment. Mais attendez une seconde, la victime est maintenant connectée au compte de l'attaquant, alors comment l'attaquant peut-il voler les données de la victime données ?
Ici, l'attaquant peut utiliser une astuce astucieuse : maintenir la boîte aux lettres de la victime ouverte jusqu'après le Login CSRF et le XSS, puis lire son contenu en utilisant l'accès de même origine.
Le nouveau flux se présentera comme suit :
- La victime navigue vers notre page malveillante (onglet 1)
- L'onglet 1 ouvre une deuxième page malveillante (onglet 2) qui contient le formulaire de Login CSRF, avec un léger délai avant son exécution
- L'onglet 1 redirige vers la boîte aux lettres de la victime, chargeant les données que l'attaquant souhaite voler
- L'onglet 2 soumet le formulaire de Login CSRF, ouvrant un nouvel onglet 3 qui connecte la victime au compte de l'attaquant
- L'onglet 2 se redirige vers
/user, où le XSS stocké est déclenché - Le XSS dans l'onglet 2 utilise une référence à opener pour lire et exfiltrer
document.body.innerHTMLdans l'onglet 1, où la boîte mail de la victime est toujours ouverte

Cela permet de lire les e-mails du compte de la victime car ils ont été récupérés alors qu'elle était toujours connectée à son compte !
Consultez l'avis pour tous les détails sur le fonctionnement pratique du flux d'exploitation. Plusieurs fichiers HTML avec un contrôle précis sur le flux permettent à un attaquant de voler des e-mails en seulement deux clics (nécessaires pour ouvrir les deux onglets supplémentaires).
Ce problème a été corrigé en ajoutant des appels à escapeHtml() autour du rendu de l'IP.
Correction
Mettez à jour Mailcow vers la version 2026-03b ou ultérieure, publiée le 31 mars 2026. Les trois vulnérabilités XSS sont corrigées dans cette version. Si vous utilisez Aikido, les instances Mailcow vulnérables sont automatiquement signalées dans votre flux de surveillance de surface comme une découverte critique.

Pas encore sur Aikido ? Créez un compte gratuit pour commencer — aucune carte de crédit requise.
Conclusion
Un schéma que nous avons observé lors de ces divulgations est que la concaténation de chaînes HTML reste une mauvaise idée. Les moteurs de templating HTML modernes ont apporté de grandes améliorations dans la plupart des applications, mais les applications plus anciennes sans frameworks sécurisés n'ont pas ce luxe. Parfois, elles reviennent à la concaténation de chaînes en JavaScript, ce qui, comme on le voit ici, conduit rapidement à oublier d'échapper les entrées utilisateur.
Les applications de messagerie contiennent des informations très sensibles, elles doivent donc être traitées avec beaucoup de soin. Ces applications doivent être auditées rigoureusement, car la compromission de l'une peut entraîner des mécanismes de réinitialisation de mot de passe, et par conséquent la compromission de nombreux autres comptes connectés.
Aikido Attack (pentest IA) détecte ce type de vulnérabilités dans les applications, de manière entièrement automatisée. Si ces résultats vous interrogent sur les vulnérabilités XSS dans vos propres applications, inscrivez-vous ou contactez-nous !

