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