Beacon Command Behavior

An explanation on all the different command 'classes' of the agent

House-keeping commands

used to set config option or perform other tasks

  • help
  • sleep : changes Beacons sleep interval and jitter %
  • downloads : shows downloads in progress
  • jobs : shows running post-exploitation jobs
  • powershell-import : imports a powershell script into Beacon
  • spawnto : changes the sacrificial process for fork & run commands
  • checkin : instructs Beacon to send metadata (when using DNS)
  • clear : clears current Beacon console
  • cancel : stops a download
  • note : sets a custom note about current Beacon
  • ppid : sets the parent PID for fork & run commands

API-only commands

pwd: orints Beacon's current working directory

download: transfers file from target to team

ls: list files/ directories

make_token: creates and impersonates an access token

rev2self : drops token impersonation

exit: instructs Beacon to terminate

cd: change dir

upload: transfer file from attacker's machine to the target

getuid: gets the username of the current user

steal_token: duplicates and impersonates access token fom another process

kill : terminates a target process

Inline commands

commands implemented as Beacon Object Files (BOF) which are compiled C programs that execute within the Beacon session.

  • BOF and command arguments are sent down to the Beacon inside the tasking data
  • Once BOF has finsihed executing, it is cleared from memory.
  • Commands:
    • clipboard: montiors users clipboard content
    • jump psexec: lateral movement technique
    • reg: queries the registry
    • getsystem: elevates the Beacon session to SYSTEM privileges
    • kerberos_ticket_use: imports a Kerberos ticket into the Beacon session
    • runasadmin uac-cmstplua: is a UAC bypass technique

OPSEC considerations

The way BOFs are loded into memory is controlled with process-inject block in Malleable C2 profile

process-inject {
    # This sets the API used to allocate BOF memory. Options are VirtualAlloc, MapViewOfFile, and HeapAlloc.
    set bof_allocator "VirtualAlloc";
    
    # This controls what happens to the memory allocation after a BOF has run.
    # If true, allocation is kept but zero'd out. If false, allocation is freed (depending on bof_allocator).
    # If memory is not large enough for the next BOF, it's freed and re-allocated regardless.
    set bof_reuse_memory "true";
    
    # This controls the size of the initial memory allocation. Make sure this is large
    # enough to accommodate the BOFs you typically run if you want to avoid too many memory
    # freeing and allocation events.
    set min_alloc "8192";
    
    # This controls the initial/resting permission of the memory allocation.
    # If true, RWX is used. If false, RW is used.
    # If bof_reuse_memory is true, memory is restored to this permission when a BOF has run.
    set startrwx "false";
    
    # This controls the final permission of the memory allocation prior to calling a BOFs entry point.
    # If true, it's flipped to RWX.
    # If false, it's flipped to RX for the BOFs code section and RW for the data section.
    set userwx "false";
}

Fork & run comands

  • Beacon's post-exploitation features are implemented as Windows DLLs.
  • They are appended to a loader to produce shellcode package which is then injected into a target process.
  • Output is then provided to Beacon over SMB named pipe
  • Two subcategories of this pattern:
    • explicit: injects the post-ex capability into a target process that already exists
    • spawn: starts a new process and injects the post-ex capability into it

Spawn only commands

  • execute-assembly : runs a .NET assembly in memory
  • powerpick: runs PowerShell via an unmanaged runspace

Explicit only commands

  • psinject: same functionality as powerpick

Commands used by both

  • portscan
  • keylogger
  • printscreen
  • desktop : injects a full VNC session
  • mimikatz

OPSEC considerations

These commands carry the heaviest OPSEC burden because they must inject shellcode, start a new thread, and start a new named pipe. The spawn variant must also start a new sacrificial process. As with BOFs, Malleable C2's process-injectblock can influence how the shellcode is injected.

process-inject {
    # This sets the API used to allocate memory in the target process.
    # Options are VirtualAllocEx and NtMapViewOfSection.
    # Be aware that NtMapViewOfSection can only be used for same-arch injection.
    # VirtualAllocEx will always be used for cross-arch injection.
    set allocator "VirtualAllocEx";
    
    # These options behave in a similar way as they do for BOFs.
    set startrwx "false";
    set userwx "false";
    
    # This sub-block sets the APIs used to create the new thread. Multiple options are provided here
    # to cover various cases, mostly cross-arch injection. The following is a breakdown of each
    # option and technique:
    #
    # CreateThread        : for the current process only.
    # CreateRemoteThread  : supports x64 -> x86 injection.
    # NtQueueApcThread    : same arch injection only.
    # NtQueueApcThread-s  : this is the "early bird" injection pattern (also same arch only).
    # ObfSetThreadContext : same arch injection only.
    # RtlCreateUserThread : supports x86 -> x64 injection but must use RWX memory.
    # SetThreadContext    : also supports x64 -> x86 injection.
    # Beacon evaluates techniques top-to-bottom, so put your prefered techniques at the top
    # and 'backup' techniques as you go down. If an edge case is not covered, injection will fail.
    #
    # CreateThread, CreateRemoteThread, and ObfSetThreadContext can spoof the address of another
    # function using the "module!function+0x##" syntax. These threads are created in a suspended state
    # and are updated to point at the shellcode before they're resumed.
    # This can help avoid those memory scan triggers.
    execute {
        CreateThread "ntdll.dll!RtlUserThreadStart+0x2c";
        NtQueueApcThread-s;
        NtQueueApcThread;
        SetThreadContext;
    }
}

post-ex block can be used to control other elements of fork & run as well as to modify the post-ex DLLS themselves

post-ex {
    # These control default process paths used by the 'spawn' variant for x64 & x86 post-ex DLLs.
    # Environment variables are ok, but always use 'sysnative' and 'syswow64' instead of 'system32'.
    # Override these at runtime using the spawnto command.
    set spawnto_x64 "%windir%\\sysnative\\msiexec.exe";
    set spawnto_x86 "%windir%\\syswow64\\msiexec.exe";
    
    # This option tells the post-ex DLL to free the post-ex loader from memory.
    set cleanup "true";
    
    # This option changes the name of the post-ex named pipe from "postex_####".
    # A comma-separated list allows Cobalt Strike to pick a random one each time.
    # Each '#' will also be replaced with a random hex character.
    set pipename "dotnet-diagnostic-#####, ########-####-####-####-############";
    
    # Some post-ex DLLs (like the portscanner) create multiple threads to help it run faster.
    # This option tells the DLL to spawn new threads with the specificed spoofed start address.
    set thread_hint "ntdll.dll!RtlUserThreadStart+0x2c";
    
    # This disables AMSI in powerpick, execute-assembly, and psinject, using a memory patching technique.
    set amsi_disable "true";
    
    # This sub-block can be used to replace strings in the post-ex DLL.
    # Use transform-x86 for x86 DLLs.
    transform-x64 {
        # strrep will replace a string in any post-ex DLL. This is good for generic strings.
        strrep "This program cannot be run in DOS mode." "This is totally not a PE.";
        
        # strrepex will replace a string in a specific post-ex DLL. This is good for strings
        # that only appear in specific DLLs.
        #
        # The valid post-ex names are: BrowserPivot, ExecuteAssembly, Hashdump, Keylogger, Mimikatz,
        # NetView, PortScanner, PowerPick, Screenshot, and SSHAgent.
        strrepex "PowerPick" "CLRCreateInstance failed w/hr 0x%08lx" "CLRCreateInstance failed: 0x%08lx";
        strrepex "PowerPick" "Failed to get default AppDomain w/hr 0x%08lx" "Failed to get default AppDomain: 0x%08lx";
        strrepex "ExecuteAssembly" "Invoke_3 on EntryPoint failed." "Unhandled exception.";
        strrepex "ExecuteAssembly" "Failed to load the assembly w/hr 0x%08lx" "Failed to load the assembly: 0x%08lx";
    }
}

Process execution commands

used to run arbitrary program on disk

  • execute: runs a program without returning output
  • runas: executes program using alternate user
  • shell: passes provided arguments via cmd.exe
  • run : executes program and does return output
  • runu: is like run but attempts to spoof its parent PID
  • powershell: passes the provided arguments via powershell.exe

Service creations

commands that create a service ro run a command or beacon

  • elevate svc-exe: used to elevate from high-integroty to SYSTEM privs
  • remote-exec psexec: runs an arbitrary command
  • jump psexec, psexec64, psexec_psh : lat mvmnt techniques

OPSEC consderations:

The service binary payload will always use rundll32 as its default spawnto. The post-ex.spawnto directive in Malleable C2 cannot be used here because environment variables, such as %windir%, are not valid in a SYSTEM context. The artifact kit provides a command called ak-settings, which allows you to set the spawnto to an explicit path instead.