Repository: EspressoCake/DLL-Hijack-Search-Order-BOF Branch: main Commit: 09527e06bf4a Files: 10 Total size: 19.6 KB Directory structure: gitextract_0q2nakdm/ ├── README.md ├── dist/ │ ├── dll_hijack_hunter.x64.o │ ├── hijack_hunter.cna │ └── optional/ │ └── Proxy_Def_File_Generator.cna └── src/ ├── Makefile ├── headers/ │ ├── assembly.h │ ├── beacon.h │ ├── userdefs.h │ └── win32api.h └── main.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # DLL Hijack Search Order BOF ## What is this? - This is a `Cobalt Strike` `BOF` file, meant to use two arguments (path to begin, and a `DLL` filename of interest), that will traverse the `SafeSearch` order of `DLL` resolution - Optionally, this will also attempt to ascertain a `HANDLE` to the provided file (if found), and alert the operator of its mutability (`WRITE` access) ## What problem are you trying to solve? - There are tools (mostly in `.NET` or otherwise) that do this job (traditionally `Powershell`-based), but I hadn't seen similar in `C` at the time of writing. I may just be terrible at `dorking` ## How do I build this? 1. In this case, you have two options: 1. Use the existing, compiled object file, located in the `dist` directory (AKA proceed to major step two) 2. Compile from source via the `Makefile` 1. `cd src` 2. `make clean` 3. `make` 2. Load the `Aggressor` file, in the `Script Manager`, located in the `dist` directory ## How do I modify this `BOF` to **not** attempt to get a `HANDLE` on the provided `DLL` filename, if found? - Within `./src/main.c`, modify `dfsStruct.bCheckCreateFileA` and `dfsStruct.bResultCreateFileA` to `FALSE`. - Rebuild with the included build instructions ## How do I use this? - From a given `Beacon`: ```sh # For accessing the help menu prompt hijack_hunter help # Example usage hijack_hunter C:\Users\User\Desktop superLegit.dll ``` ## ## Any known downsides? - We're still using the `Win32` API and `Dynamic Function Resolution`. This is for you to determine as far as "risk". - You may attempt to incur a privileged action without sufficient requisite permissions. I can't keep you from burning your hand. ## ## What does the output look like? ![](https://i.ibb.co/P6TZYKp/image.png) ================================================ FILE: dist/hijack_hunter.cna ================================================ beacon_command_register( "hijack_hunter", "Get possible DLL hijack directories, provided a filepath.", "Synopsis: hijack_hunter help"); alias hijack_hunter { local('$handle $args $data'); local('$verboseHelperString'); $handle = openf(script_resource("dll_hijack_hunter.x64.o")); $data = readb($handle, -1); closef($handle); $verboseHelperString = ""; $verboseHelperString .= "Options:\n"; $verboseHelperString .= "========\n"; $verboseHelperString .= "Use DLL search resolution to attempt to find a DLL/path to hijack:\n\thijack_hunter FOLDER_CONTAINING_PE_FILE DLL_NAME_TO_HUNT.dll\n\n"; if ( size(@_) == 3 ) { $args = bof_pack($1, "zz", $2, $3); beacon_inline_execute($1, $data, "go", $args); } else { berror($1, $verboseHelperString); } } ================================================ FILE: dist/optional/Proxy_Def_File_Generator.cna ================================================ ############################################################################# # Author: Justin Lucas (@the_bit_diddler) # # Date: November 2, 2021 # # # # Usage: # # - Load in "Script Manager" # # - Get a list of imported DLL # # functions from https://github.com/EspressoCake/DLL_Imports_BOF # # e.g. process_imports_api C:\Windows\System32\cmd.exe ntdll.dll # # - generate_def_file name_of_dll.dll # # e.g. generate_def_file ntdll.dll # ############################################################################# global('%dSourceMap'); %dSourceMap = %(); beacon_command_register( "generate_def_file", "Generate a .DEF file for use with Cobalt Strike's Artifact Kit.", "generate_def_file DLL_NAME.dll"); alias generate_def_file { local('$key') if ( size(@_) == 2 ) { local('$handle'); if ( (binfo($1, 'internal') . '-' . binfo($1, 'computer') . '-' . $2) in (keys(%dSourceMap)) ) { blog($1, "Found existing data model for this host's DLL: " . $2); blog($1, "Generating appropriate .DEF file using extracted imports from prior execution of DLL_Imports_BOF."); local('$currentPath'); $currentPath = script_resource("/"); $currentPath .= (binfo($1, 'internal') . '-' . binfo($1, 'computer') . '-' . (split('\.', $2)[0])) . '.def'; $handle = openf('>' . $currentPath); writeb($handle, %dSourceMap[binfo($1, 'internal') . '-' . binfo($1, 'computer') . '-' . $2]); closef($handle); blog($1, "Finished writing proxy-style .DEF file: " . $currentPath); } else { berror($1, "Desired file not found."); berror($1, "Did you run process_imports_api PATH_TO_EXECUTABLE " . $2 . " first?"); } } else { berror($1, "Usage: generate_def_file NAME_OF_DLL.dll"); } } on beacon_output { local('@generalString'); local('$dllNameToMimic'); local('$exportDefTemplate'); $dllNameToMimic = ""; @generalString = @(); $exportDefTemplate = ""; $generalString = split('\n', $2); if ($generalString[size($generalString) - 1] hasmatch 'Closing handle to file.*Done!') { if ($generalString[2] hasmatch 'DLL inquired') { $exportDefTemplate = "EXPORTS\n"; $dllNameToMimic = split(' ', $generalString[2])[size(split(' ', $generalString[2])) - 1]; local('$index'); for ($index = 4; $index < size($generalString) - 4; $index += 1) { local('$fName'); $fName = split(' ', replace($generalString[$index], "\t", ""))[0]; if ($fName) { $exportDefTemplate .= " "; $exportDefTemplate .= $fName; $exportDefTemplate .= "="; $exportDefTemplate .= split('\.', $dllNameToMimic)[0]; $exportDefTemplate .= "." . $fName . "\n"; } } %dSourceMap[binfo($1, 'internal') . '-' . binfo($1, 'computer') . '-' . $dllNameToMimic] = $exportDefTemplate; } } } command showTemplates { local('$key'); foreach $key (keys(%dSourceMap)) { println(""); println($key); println(%dSourceMap[$key]); println(""); } } ================================================ FILE: src/Makefile ================================================ BOFNAME := dll_hijack_hunter CC_x64 := x86_64-w64-mingw32-gcc all: $(CC_x64) -Wno-unused-variable -Wno-multichar -Wno-comment -o ../dist/$(BOFNAME).x64.o -c main.c -masm=intel clean: rm -f ../dist/$(BOFNAME).x64.o ================================================ FILE: src/headers/assembly.h ================================================ #pragma once #define ___chkstk_ms ___chkstk_ms __asm__("___chkstk_ms: \n\ ret\n\ "); ================================================ FILE: src/headers/beacon.h ================================================ #pragma once #include typedef struct { char * original; /* the original buffer [so we can free it] */ char * buffer; /* current pointer into our buffer */ int length; /* remaining length of data */ int size; /* total size of this buffer */ } datap; DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); DECLSPEC_IMPORT int BeaconDataInt(datap * parser); DECLSPEC_IMPORT short BeaconDataShort(datap * parser); DECLSPEC_IMPORT int BeaconDataLength(datap * parser); DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); /* format API */ typedef struct { char * original; /* the original buffer [so we can free it] */ char * buffer; /* current pointer into our buffer */ int length; /* remaining length of data */ int size; /* total size of this buffer */ } formatp; DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); /* Output Functions */ #define CALLBACK_OUTPUT 0x0 #define CALLBACK_OUTPUT_OEM 0x1e #define CALLBACK_ERROR 0x0d #define CALLBACK_OUTPUT_UTF8 0x20 DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); /* Token Functions */ DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); DECLSPEC_IMPORT void BeaconRevertToken(); DECLSPEC_IMPORT BOOL BeaconIsAdmin(); /* Spawn+Inject Functions */ DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); /* Utility Functions */ DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); ================================================ FILE: src/headers/userdefs.h ================================================ #pragma once #include typedef struct _dfsStruct { BOOL bFoundFile; BOOL bCheckCreateFileA; BOOL bResultCreateFileA; int cDepth; int tDepth; int eVar; } DFSStruct, *PDFSTRUCT; // Forward declarations size_t internalstrlen (const char *str); int internalstrncmp (const char * s1, const char * s2, size_t n ); // Implementations size_t internalstrlen(const char *str) { const char *s; for (s = str; *s; ++s) { ; } return (s - str); } int internalstrncmp( const char * s1, const char * s2, size_t n ) { while ( n && *s1 && ( *s1 == *s2 ) ) { ++s1; ++s2; --n; } if ( n == 0 ) { return 0; } else { return ( *(unsigned char *)s1 - *(unsigned char *)s2 ); } } ================================================ FILE: src/headers/win32api.h ================================================ #pragma once #include #include WINBASEAPI void __cdecl MSVCRT$free(void* _Block); WINBASEAPI void* WINAPI MSVCRT$malloc(SIZE_T); WINBASEAPI int __cdecl MSVCRT$_snprintf(char* s, size_t n, const char* fmt, ...); WINBASEAPI char* __cdecl MSVCRT$strtok(char* _String, const char* _Delimiter); WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE hObject); WINBASEAPI HANDLE WINAPI KERNEL32$CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile); WINBASEAPI BOOL __stdcall KERNEL32$FindClose(HANDLE hFile); WINBASEAPI HANDLE __stdcall KERNEL32$FindFirstFileA(LPCSTR filename, LPWIN32_FIND_DATAA fileAttributes); WINBASEAPI BOOL __stdcall KERNEL32$FindNextFileA(HANDLE hFoundFile, LPWIN32_FIND_DATAA fileAttributes); WINBASEAPI DWORD __stdcall KERNEL32$GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize); WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(); ================================================ FILE: src/main.c ================================================ #include "headers/assembly.h" #include #include "headers/beacon.h" #include "headers/userdefs.h" #include "headers/win32api.h" // Forward declarations to keep things tidy. BOOL get_file_list (LPCSTR folderHunt, LPCSTR newb, PDFSTRUCT pdfStruct, formatp* beaconFormatStruct); void determinePath (const char* options[5], PDFSTRUCT dfsStruct, formatp* beaconFormatStruct); void displayVanityBanner (); void dumpEnvironmentSearch (formatp* environmentStructure, PDFSTRUCT dfsStruct); void go (char* args, int arglength); // Implementations of forward declarations. void displayVanityBanner() { char cVanityBanner[] = "==================================================\n" "= DLL HIJACK SEARCH BOF =\n" "= Author: Justin Lucas (@the_bit_diddler) =\n" "==================================================\n"; BeaconPrintf(CALLBACK_OUTPUT, "%s", cVanityBanner); return; } BOOL get_file_list(LPCSTR folderHunt, LPCSTR dllToHunt, PDFSTRUCT pdfStruct, formatp* beaconFormatStruct) { if (pdfStruct->bFoundFile == TRUE) { return pdfStruct->bFoundFile; } HANDLE hFind; WIN32_FIND_DATAA data; char cPath[MAX_PATH]; MSVCRT$_snprintf(cPath, MAX_PATH, "%s\\*", folderHunt); hFind = KERNEL32$FindFirstFileA(cPath, &data); if (hFind != INVALID_HANDLE_VALUE) { do { if ((internalstrncmp(".", data.cFileName, 1) != 0) && (internalstrncmp("..", data.cFileName, 1) != 0)) { if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { } else { if (internalstrncmp(dllToHunt, data.cFileName, internalstrlen(dllToHunt)) == 0 && internalstrlen(data.cFileName) == internalstrlen(dllToHunt)) { pdfStruct->bFoundFile = TRUE; BeaconFormatPrintf(beaconFormatStruct, "Found:\t\t\t%s\\%s\n", folderHunt, data.cFileName); if (pdfStruct->bCheckCreateFileA) { char fullFilePath[MAX_PATH] = { 0 }; MSVCRT$_snprintf(fullFilePath, MAX_PATH, "%s\\%s", folderHunt, data.cFileName); if (internalstrlen(fullFilePath) == (internalstrlen(folderHunt) + internalstrlen("\\") + internalstrlen(data.cFileName))) { HANDLE hResultCheckCreateFile = KERNEL32$CreateFileA(fullFilePath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hResultCheckCreateFile != INVALID_HANDLE_VALUE) { BeaconFormatPrintf(beaconFormatStruct, "Mutability:\t\tMutable in current context! (^v^)\n", fullFilePath); KERNEL32$CloseHandle(hResultCheckCreateFile); } else { BeaconFormatPrintf(beaconFormatStruct, "Mutability:\t\tImmutable in current context! (-.-)\n", fullFilePath); BeaconPrintf(CALLBACK_OUTPUT, "ERROR: %ld\n", KERNEL32$GetLastError()); } } } } else { pdfStruct->tDepth++; } } } } while (KERNEL32$FindNextFileA(hFind, &data) && pdfStruct->bFoundFile == FALSE); KERNEL32$FindClose(hFind); } return pdfStruct->bFoundFile; } void determinePath(const char* options[5], PDFSTRUCT dfsStruct, formatp* beaconFormatStruct) { char* outputString = NULL; int sizeOfObject = 0; if (!dfsStruct->bFoundFile) { BeaconFormatPrintf(beaconFormatStruct, "DLL search order exhausted, DLL file not found!\n"); outputString = BeaconFormatToString(beaconFormatStruct, &sizeOfObject); BeaconOutput(CALLBACK_OUTPUT, outputString, sizeOfObject); BeaconFormatFree(beaconFormatStruct); return; } for (int cIndex = 0; cIndex < dfsStruct->cDepth; cIndex++) { if (dfsStruct->cDepth > 1) { if (dfsStruct->cDepth != cIndex + 1) { BeaconFormatPrintf(beaconFormatStruct, "Not found (%d of 5):\t%s\n", cIndex + 1, options[cIndex]); } else { BeaconFormatPrintf(beaconFormatStruct, "Found in: (%d of 5):\t%s\n", cIndex + 1, options[cIndex]); } } } outputString = BeaconFormatToString(beaconFormatStruct, &sizeOfObject); BeaconOutput(CALLBACK_OUTPUT, outputString, sizeOfObject); // Free the data structure(s). BeaconFormatFree(beaconFormatStruct); return; } void dumpEnvironmentSearch(formatp* environmentStructure, PDFSTRUCT dfsStruct) { char* outputString = NULL; int sizeOfObject = 0; outputString = BeaconFormatToString(environmentStructure, &sizeOfObject); if (dfsStruct->bFoundFile == TRUE && dfsStruct->eVar) { BeaconOutput(CALLBACK_OUTPUT, outputString, sizeOfObject); } BeaconFormatFree(environmentStructure); return; } void go(char* args, int arglength) { datap parser; formatp formatObject; formatp environmentObject; // Allocate a buffer for the format data structure. BeaconFormatAlloc(&formatObject, 64 * 1024); BeaconFormatAlloc(&environmentObject, 64 * 1024); char* startingDirectory = NULL; char* dllToHunt = NULL; char* tokenParse = NULL; // Sanity checking that our allocation works as intended, bailing if unsuccessful. char* resultData = NULL; resultData = (char*)MSVCRT$malloc(4095 * sizeof(char)); if ( resultData == NULL ) { BeaconPrintf(CALLBACK_OUTPUT, "Failure in malloc, exiting before any further logic commences.\n"); BeaconFormatFree (&formatObject); BeaconFormatFree (&environmentObject); return; } // Initialize the members of the data structure. DFSStruct dfsStruct; dfsStruct.bFoundFile = FALSE; dfsStruct.bCheckCreateFileA = TRUE; dfsStruct.bResultCreateFileA = FALSE; dfsStruct.cDepth = 1; dfsStruct.tDepth = 1; dfsStruct.eVar = 0; // Parse user-supplied arguments. BeaconDataParse(&parser, args, arglength); startingDirectory = BeaconDataExtract(&parser, NULL); dllToHunt = BeaconDataExtract(&parser, NULL); // Create the variable only after the arguments have been successfuly parsed. const char* cSearchArray[5] = { startingDirectory, "C:\\Windows\\System32", "C:\\Windows\\System", "C:\\Windows", "ENVIRONMENT_VARS" }; // Vanity banner displayVanityBanner(); BOOL getOperatingDirectory = get_file_list(startingDirectory, dllToHunt, &dfsStruct, &formatObject); if (!getOperatingDirectory) { dfsStruct.cDepth++; } else { determinePath(cSearchArray, &dfsStruct, &formatObject); if (resultData != NULL) { MSVCRT$free(resultData); } dumpEnvironmentSearch(&environmentObject, &dfsStruct); return; } BOOL getSystem32Info = get_file_list("C:\\Windows\\System32", dllToHunt, &dfsStruct, &formatObject); if (!getSystem32Info) { dfsStruct.cDepth++; } else { determinePath(cSearchArray, &dfsStruct, &formatObject); if (resultData != NULL) { MSVCRT$free(resultData); } dumpEnvironmentSearch(&environmentObject, &dfsStruct); return; } BOOL getWindowsInfo = get_file_list("C:\\Windows", dllToHunt, &dfsStruct, &formatObject); if (!getWindowsInfo) { dfsStruct.cDepth++; } else { determinePath(cSearchArray, &dfsStruct, &formatObject); if (resultData != NULL) { MSVCRT$free(resultData); } dumpEnvironmentSearch(&environmentObject, &dfsStruct); return; } KERNEL32$GetEnvironmentVariableA("path", resultData, 4000); if (resultData != NULL) { tokenParse = MSVCRT$strtok(resultData, ";"); while (tokenParse != NULL && dfsStruct.bFoundFile == FALSE) { get_file_list(tokenParse, dllToHunt, &dfsStruct, &formatObject); dfsStruct.eVar++; BeaconFormatPrintf(&environmentObject, "Environment Var %03d:\t%s\n", dfsStruct.eVar, tokenParse); tokenParse = MSVCRT$strtok(NULL, ";"); } } dfsStruct.cDepth++; determinePath(cSearchArray, &dfsStruct, &formatObject); dumpEnvironmentSearch(&environmentObject, &dfsStruct); if (resultData != NULL) { MSVCRT$free(resultData); } return; }