fix potential dos attack and fix sanitation not being done
This commit is contained in:
@@ -537,7 +537,7 @@ async function startServer() {
|
|||||||
takePendingNotificationScreenshot: takePendingNotificationScreenshotForClient,
|
takePendingNotificationScreenshot: takePendingNotificationScreenshotForClient,
|
||||||
storeNotificationScreenshot: storeNotificationScreenshotForPending,
|
storeNotificationScreenshot: storeNotificationScreenshotForPending,
|
||||||
handleNotificationScreenshotResult: notificationPluginHandlers.handleNotificationScreenshotResult,
|
handleNotificationScreenshotResult: notificationPluginHandlers.handleNotificationScreenshotResult,
|
||||||
handleConsoleOutput,
|
handleConsoleOutput: (clientId: string, payload: any) => handleConsoleOutput(clientId, payload),
|
||||||
handleFileBrowserMessage: (clientId: string, payload: any) =>
|
handleFileBrowserMessage: (clientId: string, payload: any) =>
|
||||||
forwardFileBrowserMessage(clientId, payload, {
|
forwardFileBrowserMessage(clientId, payload, {
|
||||||
pendingHttpDownloads,
|
pendingHttpDownloads,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ type WsLifecycleDeps = {
|
|||||||
height?: number,
|
height?: number,
|
||||||
) => void;
|
) => void;
|
||||||
handleNotificationScreenshotResult: (clientId: string, payload: any) => void;
|
handleNotificationScreenshotResult: (clientId: string, payload: any) => void;
|
||||||
handleConsoleOutput: (payload: any) => void;
|
handleConsoleOutput: (clientId: string, payload: any) => void;
|
||||||
handleFileBrowserMessage: (clientId: string, payload: any) => void;
|
handleFileBrowserMessage: (clientId: string, payload: any) => void;
|
||||||
handleProxyTunnelData: (clientId: string, connectionId: string, data: Uint8Array) => void;
|
handleProxyTunnelData: (clientId: string, connectionId: string, data: Uint8Array) => void;
|
||||||
handleProxyTunnelClose: (clientId: string, connectionId: string) => void;
|
handleProxyTunnelClose: (clientId: string, connectionId: string) => void;
|
||||||
@@ -290,20 +290,24 @@ export async function handleWebSocketMessage(
|
|||||||
const countryRaw = geo?.country || (payload as any).country || "ZZ";
|
const countryRaw = geo?.country || (payload as any).country || "ZZ";
|
||||||
const country = /^[A-Z]{2}$/i.test(countryRaw) ? countryRaw.toUpperCase() : "ZZ";
|
const country = /^[A-Z]{2}$/i.test(countryRaw) ? countryRaw.toUpperCase() : "ZZ";
|
||||||
|
|
||||||
|
const _s = (v: unknown, max = 256): string | undefined => {
|
||||||
|
if (typeof v !== "string") return undefined;
|
||||||
|
return v.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").slice(0, max);
|
||||||
|
};
|
||||||
upsertClientRow({
|
upsertClientRow({
|
||||||
id: resolvedId,
|
id: resolvedId,
|
||||||
hwid: (payload as any).hwid || resolvedId,
|
hwid: _s((payload as any).hwid) || resolvedId,
|
||||||
role: "client",
|
role: "client",
|
||||||
ip: ip || undefined,
|
ip: ip || undefined,
|
||||||
host: (payload as any).host || undefined,
|
host: _s((payload as any).host),
|
||||||
os: (payload as any).os || undefined,
|
os: _s((payload as any).os),
|
||||||
arch: (payload as any).arch || undefined,
|
arch: _s((payload as any).arch, 32),
|
||||||
version: (payload as any).version || undefined,
|
version: _s((payload as any).version, 64),
|
||||||
user: (payload as any).user || undefined,
|
user: _s((payload as any).user),
|
||||||
monitors: (payload as any).monitors || undefined,
|
monitors: (payload as any).monitors || undefined,
|
||||||
cpu: (payload as any).cpu || undefined,
|
cpu: _s((payload as any).cpu),
|
||||||
gpu: (payload as any).gpu || undefined,
|
gpu: _s((payload as any).gpu),
|
||||||
ram: (payload as any).ram || undefined,
|
ram: _s((payload as any).ram, 64),
|
||||||
country,
|
country,
|
||||||
lastSeen: Date.now(),
|
lastSeen: Date.now(),
|
||||||
online: 0 as any,
|
online: 0 as any,
|
||||||
@@ -319,17 +323,17 @@ export async function handleWebSocketMessage(
|
|||||||
deps.notifyDashboard();
|
deps.notifyDashboard();
|
||||||
deps.notifyDashboardClientEvent("client_purgatory", {
|
deps.notifyDashboardClientEvent("client_purgatory", {
|
||||||
id: resolvedId,
|
id: resolvedId,
|
||||||
host: (payload as any).host || undefined,
|
host: _s((payload as any).host),
|
||||||
user: (payload as any).user || undefined,
|
user: _s((payload as any).user),
|
||||||
os: (payload as any).os || undefined,
|
os: _s((payload as any).os),
|
||||||
ip: ip || undefined,
|
ip: ip || undefined,
|
||||||
country,
|
country,
|
||||||
});
|
});
|
||||||
deps.broadcastClientEvent("client_purgatory", {
|
deps.broadcastClientEvent("client_purgatory", {
|
||||||
id: resolvedId,
|
id: resolvedId,
|
||||||
host: (payload as any).host || undefined,
|
host: _s((payload as any).host),
|
||||||
user: (payload as any).user || undefined,
|
user: _s((payload as any).user),
|
||||||
os: (payload as any).os || undefined,
|
os: _s((payload as any).os),
|
||||||
ip: ip || undefined,
|
ip: ip || undefined,
|
||||||
country,
|
country,
|
||||||
});
|
});
|
||||||
@@ -468,7 +472,7 @@ export async function handleWebSocketMessage(
|
|||||||
deps.handleNotificationScreenshotResult(client.id, payload);
|
deps.handleNotificationScreenshotResult(client.id, payload);
|
||||||
break;
|
break;
|
||||||
case "console_output":
|
case "console_output":
|
||||||
deps.handleConsoleOutput(payload);
|
deps.handleConsoleOutput(client.id, payload);
|
||||||
break;
|
break;
|
||||||
case "file_list_result":
|
case "file_list_result":
|
||||||
case "file_download":
|
case "file_download":
|
||||||
|
|||||||
@@ -911,11 +911,12 @@ export function handleConsoleViewerMessage(ws: ServerWebSocket<SocketData>, raw:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleConsoleOutput(payload: any) {
|
export function handleConsoleOutput(clientId: string, payload: any) {
|
||||||
const sessionId = payload.sessionId as string;
|
const sessionId = payload.sessionId as string;
|
||||||
if (!sessionId) return;
|
if (!sessionId) return;
|
||||||
const session = sessionManager.getConsoleSession(sessionId);
|
const session = sessionManager.getConsoleSession(sessionId);
|
||||||
if (!session) return;
|
if (!session) return;
|
||||||
|
if (session.clientId !== clientId) return;
|
||||||
const data = payload.data ? textDecoder.decode(payload.data as Uint8Array) : "";
|
const data = payload.data ? textDecoder.decode(payload.data as Uint8Array) : "";
|
||||||
safeSendViewer(session.viewer, {
|
safeSendViewer(session.viewer, {
|
||||||
type: "output",
|
type: "output",
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ import {
|
|||||||
import { upsertClientRow } from "./db";
|
import { upsertClientRow } from "./db";
|
||||||
import { metrics } from "./metrics";
|
import { metrics } from "./metrics";
|
||||||
|
|
||||||
|
/** Strip control chars and clamp length on client-supplied info strings. */
|
||||||
|
function sanitizeInfoString(val: unknown, maxLen = 256): string | undefined {
|
||||||
|
if (typeof val !== "string") return undefined;
|
||||||
|
return val.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").slice(0, maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_PING_RTT_MS = 15_000;
|
const MAX_PING_RTT_MS = 15_000;
|
||||||
const CLIENT_DB_SYNC_INTERVAL_MS = Number(process.env.OVERLORD_CLIENT_DB_SYNC_MS || 5000);
|
const CLIENT_DB_SYNC_INTERVAL_MS = Number(process.env.OVERLORD_CLIENT_DB_SYNC_MS || 5000);
|
||||||
const lastClientDbSync = new Map<string, number>();
|
const lastClientDbSync = new Map<string, number>();
|
||||||
@@ -80,23 +86,23 @@ export function handleHello(
|
|||||||
if (ip) {
|
if (ip) {
|
||||||
info.ip = ip;
|
info.ip = ip;
|
||||||
}
|
}
|
||||||
info.hwid = (payload as any).hwid as string | undefined;
|
info.hwid = sanitizeInfoString((payload as any).hwid);
|
||||||
info.host = payload.host;
|
info.host = sanitizeInfoString(payload.host);
|
||||||
info.os = payload.os;
|
info.os = sanitizeInfoString(payload.os);
|
||||||
info.arch = payload.arch;
|
info.arch = sanitizeInfoString(payload.arch, 32);
|
||||||
info.version = payload.version;
|
info.version = sanitizeInfoString(payload.version, 64);
|
||||||
info.user = payload.user;
|
info.user = sanitizeInfoString(payload.user);
|
||||||
info.monitors = payload.monitors;
|
info.monitors = payload.monitors;
|
||||||
info.monitorInfo = (payload as any).monitorInfo || info.monitorInfo;
|
info.monitorInfo = (payload as any).monitorInfo || info.monitorInfo;
|
||||||
info.inMemory = !!(payload as any).inMemory;
|
info.inMemory = !!(payload as any).inMemory;
|
||||||
info.isAdmin = !!(payload as any).isAdmin;
|
info.isAdmin = !!(payload as any).isAdmin;
|
||||||
info.elevation = typeof (payload as any).elevation === "string" ? (payload as any).elevation : info.elevation;
|
info.elevation = sanitizeInfoString((payload as any).elevation, 32) ?? info.elevation;
|
||||||
if ((payload as any).permissions && typeof (payload as any).permissions === "object") {
|
if ((payload as any).permissions && typeof (payload as any).permissions === "object") {
|
||||||
info.permissions = (payload as any).permissions;
|
info.permissions = (payload as any).permissions;
|
||||||
}
|
}
|
||||||
info.cpu = (payload as any).cpu || info.cpu;
|
info.cpu = sanitizeInfoString((payload as any).cpu) || info.cpu;
|
||||||
info.gpu = (payload as any).gpu || info.gpu;
|
info.gpu = sanitizeInfoString((payload as any).gpu) || info.gpu;
|
||||||
info.ram = (payload as any).ram || info.ram;
|
info.ram = sanitizeInfoString((payload as any).ram, 64) || info.ram;
|
||||||
const geo = ip ? geoip.lookup(ip) : undefined;
|
const geo = ip ? geoip.lookup(ip) : undefined;
|
||||||
const countryRaw =
|
const countryRaw =
|
||||||
geo?.country || (payload as any).country || info.country || "ZZ";
|
geo?.country || (payload as any).country || info.country || "ZZ";
|
||||||
|
|||||||
Reference in New Issue
Block a user