Mailcow ist ein weit verbreiteter, selbst gehosteter und Open-Source-E-Mail-Server, der alles bietet, was Sie zur selbstständigen Verwaltung von Mailboxen benötigen. Um seine Sicherheit zu bewerten, haben wir eine lokale Instanz eingerichtet und unsere KI-Penetrationstest-Agenten darauf ausgeführt. Wir fanden drei XSS-Schwachstellen, darunter eine kritische Schwachstelle, die es nicht authentifizierten Angreifern ermöglichte, Administratorkonten zu übernehmen, während sie deren Logs in der Benutzeroberfläche betrachteten.
Der Zugriff auf eine Mailbox kann schwerwiegende Sicherheitsauswirkungen haben. Sensible Daten in E-Mails können abgefangen werden, aber der Zugriff ermöglicht es Angreifern auch, die Passwort-Reset-Funktionalität zu nutzen, um andere verbundene Konten des Opfers zu kompromittieren. Genau das wäre möglich gewesen, hätte jemand diese Schwachstellen ausgenutzt.
Alle Schwachstellen wurden Mailcow verantwortungsvoll offengelegt und wurden seit Version 2026-03b (veröffentlicht am 31. März 2026) behoben. Wir möchten dem Maintainer, FreddleSpl0it, für den reibungslosen Prozess und die schnelle Behebung danken.
Unmaskierte Autodiscover-Logs
Auf GitHub als GHSA-f9xf-vc72-rcgm gemeldet, ermöglichte diese Schwachstelle nicht authentifizierten Angreifern, einen Autodiscover-Request mit einer bösartigen E-Mail-Adresse zu senden, die in den Logs auftauchte. Wenn ein Administrator diese Logs später ansieht, wird das E-Mail-Adressfeld unmaskiert angezeigt, was HTML-Injection und XSS ermöglicht.
Beginnen wir bei der Senke. Mailcow ermöglicht die Anzeige von Autodiscover-Logs in einer Tabelle im Admin-Panel, die mittels DataTables in dashboard.js gerendert wird. Diese Bibliothek birgt die gängige Falle, Datenwerte standardmäßig als HTML zu interpretieren. Alle Werte müssen zuvor korrekt maskiert werden.
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');
}
},
Der process_table_data() Funktion versucht, Benutzereingaben in jeder Zeile zu maskieren. Für Autodiscover-Logs, item.ua (User Agent) wird korrekt maskiert mittels 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>';Eine Spalte, item.name, ist jedoch nicht maskiert. Dies ist die E-Mail-Adresse, die im Autodiscover-Request gesendet wird.
Der gefährlichste Teil? Ein Autodiscover-Request ist systembedingt nicht authentifiziert, und in diesem Fall wird die E-Mail-Adresse nicht validiert. Sie landet in den Logs, und sobald ein Administrator sie ansieht, wird sie durch diese anfälligen Funktionen geleitet, um als beliebiges HTML gerendert zu werden.
Unten ist ein Beispiel-Request, der injiziert <img src=x onerror=alert(origin)> in die Tabelle:
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>
Wenn ein Administrator nun die Autodiscover-Logs ansieht (zu finden unter Dashboard -> Logs -> Autodiscover), wird das JavaScript ausgeführt und zeigt ein Alert-Pop-up, was XSS im Mailcow-Ursprung beweist.

Da dies nur Administratoren betrifft, können Angreifer auf das Postfach jedes Benutzers zugreifen und die Instanz neu konfigurieren. Dies verlieh der Schwachstelle eine kritische Schwere.
Dieses Problem wurde behoben durch Hinzufügen eines escapeHtml() Aufrufs um item.user während der Verarbeitung der Zeilen.
Injektion von Quarantäne-Anhangsdateinamen
Gemeldet als GHSA-2xjc-rg88-jvpp, existiert diese Schwachstelle in Mailcows Quarantäne-Funktion, wo Administratoren markierte Anhänge untersuchen können. Die Dateinamen dieser Anhänge wurden ohne HTML-Maskierung angezeigt, was XSS für jeden Administrator ermöglichte, der sie ansah. Die Ausnutzung erforderte keine Authentifizierung seitens des Angreifers, obwohl die Quarantäne-Funktion auf der Zielinstanz aktiviert sein musste.
Die Senke ist diesmal klar. Innerhalb von quarentine.js gibt es einen Codeabschnitt, der HTML mit dynamischen Daten verkettet:
$.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>'
);
});
Wenn eine dieser Angaben benutzergesteuert ist, könnte dies erneut zu HTML-Injection führen. Diese Werte sind der Dateiname, der MIME-Typ, die Dateigröße bzw. der Virustotal SHA256. Von diesen scheint der Dateiname am ehesten Benutzereingaben zu enthalten, da der Angreifer eine E-Mail mit einem Anhang senden kann, der einen speziellen Dateinamen mit HTML-Tags besitzt.
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.
Der unten stehende Code generiert einen Anhang mit dem EICAR-String als Inhalt und einer XSS-Payload als Dateiname (siehe Advisory für ein vollständiges Skript):
# 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)
Nachdem die E-Mail an Mailcow gesendet und unter Quarantäne gestellt wurde, kann der Administrator die Quarantine-Seite öffnen, um sie einzusehen. Sobald er auf Show item klickt, wird unser Dateiname gerendert und der XSS ausgelöst:

Beachten Sie, dass dies auch keine Authentifizierung bei Mailcow zur Ausnutzung erfordert. Es genügt, eine bösartig präparierte E-Mail an die Mailcow-Domain zu senden und dass der Administrator diese untersucht. Von hier aus könnte die gesamte Instanz mittels JavaScript übernommen, Postfächer erneut gelesen und die Instanz konfiguriert werden.
Dieses Problem wurde behoben durch das Hinzufügen von escapeHtml()-Aufrufen um die Anhangswerte.
Eskalation eines Self-XSS in der im Login-Verlauf aufgeführten IP-Adresse
Die letzte XSS-Schwachstelle, gemeldet als GHSA-jprq-w83q-q62h, war technisch interessanter auszunutzen. Die XSS-Payload wird unter dem eigenen Konto des Angreifers gespeichert, was bedeutet, dass sie normalerweise nur vom Angreifer selbst ausgelöst würde (ein „Self-XSS“ ohne echte Auswirkungen). Der Angriff wird gefährlich, wenn er mit einem Login-CSRF kombiniert wird, der den Browser eines Opfers zwingt, sich in das Konto des Angreifers einzuloggen, wodurch die gespeicherte Payload der falschen Person präsentiert wird.
Die Schwachstelle beginnt mit einer weiteren einfachen HTML-Verkettungssenke in 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>";Der real_rip (Echte Remote-IP) wird in der Tabelle der letzten Logins gerendert, ohne escaped zu werden. Normalerweise sollte dies kein Problem sein; IP-Adressen folgen einem sehr strengen Format, das keinen Raum für XSS-Payloads lässt.
IP-Adressen werden jedoch von Mailcow nicht validiert und stammen direkt aus dem X-Real-IP: Header standardmäßig. Einige Proxys setzen diesen Header auf die Remote-IP des Benutzers, aber wenn kein solcher Proxy existiert, wird jeder ihm gegebene Wert vertraut. Daher können Sie sich anmelden, während Sie X-Real-IP: "><img src onerror=alert(origin)> was den Wert in den letzten Login-Einträgen speichert. Beim Anzeigen (über /user), wird die XSS-Payload gerendert und führt den Alert aus.
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
Das ist jedoch noch nicht das Ende der Geschichte. Denn ein Opfer wird sich nicht mit diesem gefälschten IP-Header anmelden oder sich aus dem Nichts in das Konto des Angreifers einloggen. Vorerst handelt es sich um Self-XSS.
Zwei Aspekte dieser Schwachstelle ermöglichen eine Eskalation. Erstens wird die Payload dauerhaft unter dem Konto des Angreifers gespeichert. Zweitens hat die Login-Anfrage keinen CSRF-Schutz, was bedeutet, dass sie Cross-Site ausgelöst werden kann. Ein Angreifer kann ein Formular auf seiner eigenen Domain hosten, das die Anmeldeinformationen des Angreifers automatisch übermittelt und das Opfer ohne weitere Interaktion als den Besuch der Seite anmeldet.
Ein Angreifer kann das folgende Formular auf seiner bösartigen Domain hosten:
<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>
Sobald ein Opfer dieses gehostete Formular auf der bösartigen Website öffnet, wird es automatisch in das Konto des Angreifers eingeloggt. Wir können sie dann umleiten zu /user um den zuvor gespeicherten XSS auszulösen. Aber Moment mal, das Opfer ist jetzt im Konto des Angreifers angemeldet, wie kann der Angreifer also die Daten des Opfers stehlen?
Hier kann der Angreifer einen cleveren Trick anwenden: das Postfach des Opfers offen halten bis nach dem Login-CSRF & XSS und dann mittels Same-Origin-Zugriff daraus lesen.
Der neue Ablauf sieht wie folgt aus:
- Opfer navigiert zu unserer bösartigen Seite (Tab 1)
- Tab 1 öffnet eine zweite bösartige Seite (Tab 2), die das Login-CSRF-Formular enthält, mit einer leichten Verzögerung, bevor es ausgeführt wird.
- Tab 1 leitet zur Mailbox des Opfers weiter und lädt die Daten, die der Angreifer stehlen möchte.
- Tab 2 sendet das Login-CSRF-Formular ab und öffnet einen neuen Tab 3, der das Opfer in das Konto des Angreifers einloggt.
- Tab 2 leitet sich selbst weiter zu
/user, wo das Stored XSS ausgelöst wird. - Das XSS in Tab 2 verwendet eine Referenz auf opener um zu lesen und zu exfiltrieren
document.body.innerHTMLin Tab 1, wo die Mailbox des Opfers noch geöffnet ist.

Dies ermöglicht das Lesen von E-Mails aus dem Konto des Opfers, da diese abgerufen wurden, während es noch in seinem Konto angemeldet war!
Weitere Details zur Funktionsweise des Exploit-Flows in der Praxis finden Sie im Advisory. Mehrere HTML-Dateien mit präziser Kontrolle über den Ablauf ermöglichen es einem Angreifer, E-Mails mit nur zwei Klicks (erforderlich zum Öffnen der beiden zusätzlichen Tabs) zu stehlen.
Dieses Problem wurde behoben, indem escapeHtml()-Aufrufe um das IP-Rendering herum hinzugefügt wurden.
Behebung
Aktualisieren Sie Mailcow auf Version 2026-03b oder höher, veröffentlicht am 31. März 2026. Alle drei XSS-Schwachstellen sind in dieser Version gepatcht. Wenn Sie Aikido verwenden, werden anfällige Mailcow-Instanzen automatisch in Ihrem Surface-Monitoring-Feed als kritischer Befund markiert.

Noch nicht bei Aikido? Erstellen Sie ein kostenloses Konto, um loszulegen — keine Kreditkarte erforderlich.
Fazit
Ein Muster, das wir bei diesen Offenlegungen beobachtet haben, ist, dass das Verketten von HTML-Strings weiterhin eine schlechte Idee ist. Moderne HTML-Templating-Engines haben in den meisten Anwendungen große Verbesserungen erzielt, aber ältere Anwendungen ohne sichere Frameworks haben diesen Luxus nicht. Manchmal greifen sie auf das Verketten von Strings in JavaScript zurück, was, wie hier zu sehen ist, schnell dazu führt, das Escaping von Benutzereingaben zu vergessen.
Mailbox-Anwendungen enthalten hochsensible Informationen und sollten daher mit großer Sorgfalt behandelt werden. Solche Anwendungen sollten rigoros geprüft werden, da die Kompromittierung einer Anwendung zu Passwort-Reset-Mechanismen führen und somit die Kompromittierung vieler anderer verbundener Konten nach sich ziehen kann.
Aikido Attack (KI-Penetrationstests) findet diese Art von Schwachstellen in Anwendungen, komplett automatisiert. Wenn Sie diese Ergebnisse sehen und sich über XSS-Schwachstellen in Ihren eigenen Anwendungen wundern, melden Sie sich an oder kontaktieren Sie uns!

