diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index f72415f7f2..42c24ece7e 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -49,24 +49,30 @@ module Msf # Requirements a browser module can define in either BrowserRequirements or in targets REQUIREMENT_KEY_SET = Set.new([ - :source, # Either 'script' or 'headers' - :ua_name, # Example: MSIE - :ua_ver, # Example: 8.0, 9.0 - :os_name, # Example: Windows 7, Linux - :os_device, # Example: iPad, iPhone, etc - :os_vendor, # Example: Microsoft, Ubuntu, Apple, etc - :os_sp, # Example: SP2 - :language, # Example: en-us - :arch, # Example: x86 - :proxy, # 'true' or 'false' - :silverlight, # 'true' or 'false' - :office, # Example: "2007", "2010" - :java, # Example: 1.6, 1.6.0.0 - :clsid, # ActiveX clsid. Also requires the :method key - :method, # ActiveX method. Also requires the :clsid key - :mshtml_build, # mshtml build. Example: "65535" - :flash, # Example: "12.0" (chrome/ff) or "12.0.0.77" (IE) - :vuln_test # Example: "if(window.MyComponentIsInstalled)return true;" + :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 ]) def initialize(info={}) @@ -105,68 +111,61 @@ module Msf super end - # + # Returns the custom 404 URL set by the user # # @return [String] - # def get_custom_404_url datastore['Custom404'].to_s end - # + # Allows a block of code to access BES resources in a thread-safe fashion # # @param block [Proc] Block of code to sync - # def sync(&block) (@mutex ||= Mutex.new).synchronize(&block) end - # + # Returns the resource (URI) to the module to allow access to on_request_exploit # # @return [String] URI to the exploit page - # def get_module_resource "#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/" end - # + # Returns the absolute URL to the module's resource that points to on_request_exploit # # @return [String] absolute URI to the exploit page - # def get_module_uri "#{get_uri.chomp("/")}/#{@exploit_receiver_page}" end - # + # Returns the current target - # def get_target @target end - # + # Returns a hash of recognizable requirements # # @param reqs [Hash] A hash that contains data for the requirements # @return [Hash] A hash of requirements - # def extract_requirements(reqs) tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)} # Make sure keys are always symbols Hash[tmp.map{|(k,v)| [k.to_sym,v]}] end - # + # 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. # # @param profile [Hash] The profile to check - # def try_set_target(profile) match_counts = [] target_requirements = {} @@ -195,30 +194,36 @@ module Msf end end - # + + # Returns true if there's a bad ActiveX, otherwise false. + # @param ax [String] The raw activex the JavaScript detection will return in this format: + # "{CLSID}=>Method=>Boolean;" + # @return [Boolean] True if there's a bad ActiveX, otherwise false + def has_bad_activex?(ax) + ax.split(';').each do |a| + bool = a.split('=>')[2] + if bool == 'false' + return true + end + end + + false + end + # Returns an array of items that do not meet the requirements # # @param profile [Hash] The profile to check # @return [Array] An array of requirements not met - # def get_bad_requirements(profile) bad_reqs = [] - # At this point the check is already done. - # If :activex is true, that means the clsid + method had a match, - # if not, then false. - if @requirements[:clsid] and @requirements[:method] - @requirements[:activex] = 'true' # Script passes boolean as string - end - @requirements.each do |k, v| - # Special keys to ignore because the script registers this as [:activex] = true or false - next if k == :clsid or k == :method - expected = k != :vuln_test ? v : 'true' vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}") - if k == :vuln_test + if k == :activex + bad_reqs << k if has_bad_activex?(profile[k.to_sym]) + elsif k == :vuln_test bad_reqs << k unless profile[k.to_sym].to_s == 'true' elsif v.is_a? Regexp bad_reqs << k if profile[k.to_sym] !~ v @@ -232,7 +237,6 @@ module Msf bad_reqs end - # # Returns the target profile based on the tag. Each profile has the following structure: # 'cookie_name' => # { @@ -253,7 +257,7 @@ module Msf # # If the source is 'script', the profile might have even more information about plugins: # 'office' : The version of Microsoft Office (IE only) - # 'activex' : Whether a specific method is available from an ActiveX control (IE only) + # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) # 'java' : The Java version # 'mshtml_build' : The MSHTML build version # 'flash' : The Flash version @@ -261,45 +265,41 @@ module Msf # # @param tag [String] Either a cookie or IP + User-Agent # @return [Hash] The profile found. If not found, returns nil - # def get_profile(tag) sync do return @target_profiles[tag] end end - # + # Updates information for a specific profile # # @param target_profile [Hash] The profile to update # @param key [Symbol] The symbol to use for the hash # @param value [String] The value to assign - # def update_profile(target_profile, key, value) sync do target_profile[key] = value end end - # + # Initializes a profile, if it did not previously exist # # @param tag [String] A unique string as a way to ID the profile - # def init_profile(tag) sync do @target_profiles[tag] ||= {} end end - # + # 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. # # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser - # def retrieve_tag(cli, request) cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s) tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first @@ -317,13 +317,12 @@ module Msf tag end - # + # Registers target information to @target_profiles # # @param source [Symbol] Either :script, or :headers # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser - # def process_browser_info(source, cli, request) tag = retrieve_tag(cli, request) init_profile(tag) @@ -361,27 +360,28 @@ module Msf }) end - # + # Checks if the target is running a proxy # # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [Boolean] True if found, otherwise false - # def has_proxy?(request) proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys !proxy_header_set.empty? end - # + # Returns the code for client-side detection # # @param user_agent [String] The user-agent of the browser # @return [String] Returns the HTML for detection - # def get_detection_html(user_agent) + print_debug(user_agent) ua_info = fingerprint_user_agent(user_agent) os = ua_info[:os_name] client = ua_info[:ua_name] + print_debug(os.inspect) + print_debug(client.inspect) code = ERB.new(%Q| <%= js_base64 %> @@ -418,11 +418,20 @@ module Msf d['office'] = ie_addons_detect.getMsOfficeVersion(); d['mshtml_build'] = ScriptEngineBuildVersion().toString(); <% - clsid = @requirements[:clsid] - method = @requirements[:method] - if clsid and method + activex = @requirements[:activex] + if activex + activex.each do \|a\| + clsid = a[:clsid] + method = a[:method] %> - d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=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 %> @@ -438,7 +447,7 @@ module Msf %Q|