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|