Profile sharing works for the first time

bug/bundler_fix
wchen-r7 2015-04-29 18:45:08 -05:00
parent c18c5c7b6e
commit f3e026db6c
2 changed files with 62 additions and 76 deletions

View File

@ -154,9 +154,9 @@ module Msf
# @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)}
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]}]
Hash[tmp.map{|(k,v)| [k.to_s,v]}]
end
@ -214,22 +214,23 @@ module Msf
# @param profile [Hash] The profile to check
# @return [Array] An array of requirements not met
def get_bad_requirements(profile)
profile = profile.first[1]
bad_reqs = []
@requirements.each do |k, v|
expected = k != :vuln_test ? v : 'true'
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_s]}")
if k == :activex
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
bad_reqs << k if has_bad_activex?(profile[k.to_s])
elsif k == :vuln_test
bad_reqs << k unless profile[k.to_sym].to_s == 'true'
bad_reqs << k unless profile[k.to_s].to_s == 'true'
elsif v.is_a? Regexp
bad_reqs << k if profile[k.to_sym] !~ v
bad_reqs << k if profile[k.to_s] !~ v
elsif v.is_a? Proc
bad_reqs << k unless v.call(profile[k.to_sym])
bad_reqs << k unless v.call(profile[k.to_s])
else
bad_reqs << k if profile[k.to_sym] != v
bad_reqs << k if profile[k.to_s] != v
end
end
@ -246,7 +247,9 @@ module Msf
# @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)
#$stderr.puts "Found cookie: #{cookie.inspect}"
tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first
#$stderr.puts "Found tag: #{tag}"
if tag.blank?
# Browser probably doesn't allow cookies, plan B :-/
@ -269,9 +272,10 @@ module Msf
# @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)
target_info = get_profile(tag)
update_profile(target_info, :source, source.to_s)
update_profile(tag, :source, source.to_s)
found_ua_name = ''
found_ua_ver = ''
# Gathering target info from the detection stage
case source
@ -279,7 +283,9 @@ module Msf
# Gathers target data from a POST request
parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '')
vprint_debug("Received sniffed browser data over POST: \n#{parsed_body}.")
parsed_body.each { |k, v| update_profile(target_info, k.to_sym, v.first) }
parsed_body.each { |k, v| update_profile(tag, k.to_s, 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.
@ -288,19 +294,21 @@ module Msf
# Kill this to save space.
fp.delete(:ua_string)
fp.each do |k, v|
update_profile(target_info, k.to_sym, v)
update_profile(tag, k.to_s, v)
end
found_ua_name = fp[:ua_name]
found_ua_ver = fp[:ua_ver]
end
# Other detections
update_profile(target_info, :proxy, has_proxy?(request))
update_profile(target_info, :language, request.headers['Accept-Language'] || '')
update_profile(tag, :proxy, has_proxy?(request))
update_profile(tag, :language, request.headers['Accept-Language'] || '')
report_client({
:host => cli.peerhost,
:ua_string => request.headers['User-Agent'],
:ua_name => target_info[:ua_name],
:ua_ver => target_info[:ua_ver]
:ua_name => found_ua_name,
:ua_ver => found_ua_ver
})
end
@ -402,6 +410,7 @@ module Msf
datastore['CookieName'] || DEFAULT_COOKIE_NAME
end
def cookie_header(tag)
cookie = "#{cookie_name}=#{tag};"
if datastore['CookieExpiration'].present?
@ -423,7 +432,7 @@ module Msf
#
# This is the information gathering stage
#
if get_profile(retrieve_tag(cli, request))
unless get_profile_info(retrieve_tag(cli, request)).empty?
send_redirect(cli, get_module_resource)
return
end
@ -459,15 +468,15 @@ module Msf
#
tag = retrieve_tag(cli, request)
vprint_status("Serving exploit to user with tag #{tag}")
profile = get_profile(tag)
if profile.nil?
profile = get_profile_info(tag)
if profile.empty?
print_status("Browsing directly to the exploit URL is forbidden.")
send_not_found(cli)
elsif profile[:tried] and datastore['Retries'] == false
print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.")
send_not_found(cli)
else
update_profile(profile, :tried, true)
update_profile(tag, :tried, true)
vprint_status("Setting target \"#{tag}\" to :tried.")
try_set_target(profile)
bad_reqs = get_bad_requirements(profile)

View File

@ -1,69 +1,46 @@
require 'Msgpack'
module Msf
module Exploit::Remote::BrowserProfileManager
def initialize(info={})
super
# See get_profile's documentation to understand what @target_profiles stores
@target_profiles = {}
end
public
# Returns the target profile based on the tag. Each profile has the following structure:
# 'cookie_name' =>
# {
# :os_name => 'Windows 7'
# ...... etc ......
# }
# A profile should at least have info about the following:
# :source : The data source. Either from 'script', or 'headers'. The 'script' source
# should be more accurate in some scenarios like browser compatibility mode
# :ua_name : The name of the browser
# :ua_ver : The version of the browser (not yet implemented)
# :os_name : The name of the OS ("Windows XP")
# :language : The system's language
# :arch : The system's arch
# :proxy : Indicates whether proxy is used
#
# For more info about what the actual value might be for each key, see HttpServer.
#
# 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 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
# 'silverlight' : The Silverlight version
#
# @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]
NOTE_TYPE_PREFIX = 'BrowserExploitServer.Client'
def get_profile_info(tag)
normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}"
framework.db.notes.each do |note|
return MessagePack.unpack(note.data) if note.ntype == normalized_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
def update_profile(tag, key, value)
profile = get_profile_info(tag)
if profile.empty?
init_profile(tag)
profile = get_profile_info(tag)
end
normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}"
profile[normalized_tag][key.to_s] = value
framework.db.report_note(
:type => normalized_tag,
:data => profile.to_msgpack,
:update => :unique
)
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
normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}"
empty_profile = { normalized_tag => {} }
framework.db.report_note(
:type => normalized_tag,
:data => empty_profile.to_msgpack,
:update => :unique
)
end
end
end
end