Thin.send :remove_const, :SERVER
Thin::SERVER = nil
module BeEF
module Core
class Server
include Singleton
attr_reader :root_dir, :url, :configuration, :command_urls, :mounts, :semaphore
def initialize
@configuration = BeEF::Core::Configuration.instance
@url = @configuration.beef_url_str
@root_dir = File.expand_path('../../../', __dir__)
@command_urls = {}
@mounts = {}
@rack_app
@semaphore = Mutex.new
end
def to_h
{
'beef_version' => @configuration.get('beef_version'),
'beef_url' => @url,
'beef_root_dir' => @root_dir,
'beef_host' => @configuration.beef_host,
'beef_port' => @configuration.beef_port,
'beef_public' => @configuration.public_host,
'beef_public_port' => @configuration.public_port,
'beef_hook' => @configuration.get('beef.http.hook_file'),
'beef_proto' => @configuration.beef_proto,
'client_debug' => @configuration.get('beef.client_debug')
}
end
def mount(url, http_handler_class, args = nil)
raise TypeError, '"url" needs to be a string' unless url.is_a?(String)
@mounts[url] = if args.nil?
http_handler_class
else
[http_handler_class, *args]
end
print_debug "Server: mounted handler '#{url}'"
end
def unmount(url)
raise TypeError, '"url" needs to be a string' unless url.is_a?(String)
@mounts.delete url
end
def remap
@rack_app.remap @mounts
end
def prepare
mount(@configuration.get('beef.http.hook_file').to_s, BeEF::Core::Handlers::HookedBrowsers.new)
mount('/init', BeEF::Core::Handlers::BrowserDetails)
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'mount_handler', self)
@rack_app = Rack::URLMap.new(@mounts)
return if @http_server
Thin::Logging.silent = true
if @configuration.get('beef.http.debug') == true
Thin::Logging.silent = false
Thin::Logging.debug = true
end
@http_server = Thin::Server.new(
@configuration.get('beef.http.host'),
@configuration.get('beef.http.port'),
@rack_app
)
return unless @configuration.get('beef.http.https.enable') == true
openssl_version = OpenSSL::OPENSSL_VERSION
if openssl_version =~ / 1\.0\.1([a-f])? /
print_warning "Warning: #{openssl_version} is vulnerable to Heartbleed (CVE-2014-0160)."
print_more 'Upgrade OpenSSL to version 1.0.1g or newer.'
end
cert_key = @configuration.get 'beef.http.https.key'
cert_key = File.expand_path cert_key, $root_dir unless cert_key.start_with? '/'
unless File.exist? cert_key
print_error "Error: #{cert_key} does not exist"
exit 1
end
cert = @configuration.get 'beef.http.https.cert'
cert = File.expand_path cert, $root_dir unless cert.start_with? '/'
unless File.exist? cert
print_error "Error: #{cert} does not exist"
exit 1
end
@http_server.ssl = true
@http_server.ssl_options = {
private_key_file: cert_key,
cert_chain_file: cert,
verify_peer: false
}
if Digest::SHA256.hexdigest(File.read(cert)).eql?('978f761fc30cbd174eab0c6ffd2d235849260c0589a99262f136669224c8d82a') ||
Digest::SHA256.hexdigest(File.read(cert_key)).eql?('446214bb608caf9e21dd105ce3d4ea65a3f32949906f3eb25a2c622a68623122')
print_warning 'Warning: Default SSL cert/key in use.'
print_more 'Use the generate-certificate utility to generate a new certificate.'
end
rescue StandardError => e
print_error "Failed to prepare HTTP server: #{e.message}"
print_error e.backtrace
exit 1
end
def start
@http_server.start do
use OTR::ActiveRecord::ConnectionManagement
end
rescue RuntimeError => e
raise unless e.message.include? 'no acceptor'
print_error "Another process is already listening on port #{@configuration.get('beef.http.port')}, or you're trying to bind BeEF to an invalid IP."
print_error 'Is BeEF already running? Exiting...'
exit 127
end
end
end
end