#!/usr/bin/env ruby

#
# Copyright (c) 2006-2020 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#

#
# @note stop Fixnum deprecation warning from being displayed
#
$VERBOSE = nil

#
# @note Version check to ensure BeEF is running Ruby 2.5+
#
if RUBY_VERSION < '2.5'
  puts
  puts "Ruby version #{RUBY_VERSION} is no longer supported. Please upgrade to Ruby version 2.5 or later."
  puts
  exit 1
end

#
# @note Platform check to ensure BeEF is not running on Windows
#
if RUBY_PLATFORM.downcase.include?('mswin') || RUBY_PLATFORM.downcase.include?('mingw')
  puts
  puts "Ruby platform #{RUBY_PLATFORM} is not supported."
  puts
  exit 1
end

#
# @note set load path, application root directory and user preferences directory
#
$root_dir = File.join(File.expand_path(File.dirname(File.realpath(__FILE__))), '.')
$:.unshift($root_dir)
$home_dir = File.expand_path("#{Dir.home}/.beef/", __FILE__).freeze

#
# @note Require core loader
#
require 'core/loader'

#
# @note Create ~/.beef/
#
begin
  FileUtils.mkdir_p($home_dir) unless File.directory?($home_dir)
rescue => e
  print_error "Could not create '#{$home_dir}': #{e.message}"
  exit 1
end

#
# @note Initialize the Configuration object. Loads a different config.yaml if -c flag was passed.
#
if BeEF::Core::Console::CommandLine.parse[:ext_config].empty?
  config = BeEF::Core::Configuration.new("#{$root_dir}/config.yaml")
else
  config = BeEF::Core::Configuration.new("#{BeEF::Core::Console::CommandLine.parse[:ext_config]}")
end

#
# @note set log level
#
BeEF.logger.level = config.get('beef.debug') ? Logger::DEBUG : Logger::WARN

#
# @note Check the system language settings for UTF-8 compatibility
#
env_lang = ENV['LANG']
if env_lang !~ /(utf8|utf-8)/i
  print_warning "Warning: System language $LANG '#{env_lang}' does not appear to be UTF-8 compatible."
  if env_lang =~ /\A([a-z]+_[a-z]+)\./i
    country = $1
    print_more "Try: export LANG=#{country}.utf8"
  end
end

#
# @note Check if port and WebSocket port need to be updated from command line parameters
#
unless BeEF::Core::Console::CommandLine.parse[:port].empty?
  config.set('beef.http.port', BeEF::Core::Console::CommandLine.parse[:port])
end

unless BeEF::Core::Console::CommandLine.parse[:ws_port].empty?
  config.set('beef.http.websocket.port', BeEF::Core::Console::CommandLine.parse[:ws_port])
end

#
# @note Validate configuration file
#
unless BeEF::Core::Configuration.instance.validate
  exit 1
end

#
# @note Exit on default credentials
#
if config.get("beef.credentials.user").eql?('beef') && config.get("beef.credentials.passwd").eql?('beef')
  print_error "ERROR: Default username and password in use!"
  print_more "Change the beef.credentials.passwd in config.yaml"
  exit 1
end

#
# @note Validate beef.http.public and beef.http.public_port
#
unless config.get('beef.http.public').to_s.eql?('') || BeEF::Filters.is_valid_hostname?(config.get('beef.http.public'))
  print_error "ERROR: Invalid public hostname: #{config.get('beef.http.public')}"
  exit 1
end

unless config.get('beef.http.public_port').to_s.eql?('') || BeEF::Filters.is_valid_port?(config.get('beef.http.public_port'))
  print_error "ERROR: Invalid public port: #{config.get('beef.http.public_port')}"
  exit 1
end

#
# @note After the BeEF core is loaded, bootstrap the rest of the framework internals
#
require 'core/bootstrap'

#
# @note Prints the BeEF ascii art if the -a flag was passed
#
if BeEF::Core::Console::CommandLine.parse[:ascii_art] == true
  BeEF::Core::Console::Banners.print_ascii_art
end

#
# @note Prints BeEF welcome message
#
BeEF::Core::Console::Banners.print_welcome_msg

#
# @note Loads enabled extensions
#
BeEF::Extensions.load

#
# @note Loads enabled modules
#
BeEF::Modules.load

#
# @note Disable reverse DNS
#
Socket.do_not_reverse_lookup = true

#
# @note Database setup
#
#
# @note Load the database
#
db_file = config.get('beef.database.file')
# @note Resets the database if the -x flag was passed
if BeEF::Core::Console::CommandLine.parse[:resetdb]
  print_info 'Resetting the database for BeEF.'
  File.delete(db_file) if File.exists?(db_file)
end
# Connect to DB
ActiveRecord::Base.logger = nil
OTR::ActiveRecord.migrations_paths = [File.join('core', 'main', 'ar-migrations')]
OTR::ActiveRecord.configure_from_hash!(adapter:'sqlite3', database:db_file)
# Migrate (if required)
context = ActiveRecord::Migration.new.migration_context
if context.needs_migration?
  ActiveRecord::Migrator.new(:up, context.migrations, context.schema_migration).migrate
end

#
# @note Extensions may take a moment to load, thus we print out a please wait message
#
print_info 'BeEF is loading. Wait a few seconds...'

#
# @note Execute migration procedure, checks for new modules
#
BeEF::Core::Migration.instance.update_db!

#
# @note Create HTTP Server and prepare it to run
#
http_hook_server = BeEF::Core::Server.instance
http_hook_server.prepare

#
# @note Prints information back to the user before running the server
#
BeEF::Core::Console::Banners.print_loaded_extensions
BeEF::Core::Console::Banners.print_loaded_modules
BeEF::Core::Console::Banners.print_network_interfaces_count
BeEF::Core::Console::Banners.print_network_interfaces_routes

#
# @note Prints the API key needed to use the RESTful API
#
print_info "RESTful API key: #{BeEF::Core::Crypto::api_token}"

#
# @note Load the GeoIP database
#
BeEF::Core::GeoIp.instance

#
# @note Call the API method 'pre_http_start'
#
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'pre_http_start', http_hook_server)

#
# @note Load any ARE (Autorun Rule Engine) rules scanning the <beef_root>/arerules/enabled directory
#
BeEF::Core::AutorunEngine::RuleLoader.instance.load_directory

#
# @note Start the WebSocket server
#
if config.get("beef.http.websocket.enable")
  BeEF::Core::Websocket::Websocket.instance
  BeEF::Core::Console::Banners.print_websocket_servers
end

#
# @note Start HTTP server
#
print_info 'BeEF server started (press control+c to stop)'
http_hook_server.start
