diff --git a/Gemfile b/Gemfile index a873eeb9e..11297d839 100644 --- a/Gemfile +++ b/Gemfile @@ -41,6 +41,7 @@ gem "msfrpc-client" # Metasploit Integration extension gem "rubyzip", ">= 1.0.0" gem "rubydns", "0.7.0" # DNS extension gem "geoip" # geolocation support +gem "dm-serializer" # network extension # For running unit tests if ENV['BEEF_TEST'] diff --git a/extensions/network/api.rb b/extensions/network/api.rb new file mode 100644 index 000000000..078f46010 --- /dev/null +++ b/extensions/network/api.rb @@ -0,0 +1,24 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF + module Extension + module Network + + module RegisterHttpHandler + + BeEF::API::Registrar.instance.register(BeEF::Extension::Network::RegisterHttpHandler, BeEF::API::Server, 'mount_handler') + + # Mounts the handler for processing network host info. + # + # @param beef_server [BeEF::Core::Server] HTTP server instance + def self.mount_handler(beef_server) + beef_server.mount('/api/network', BeEF::Extension::Network::NetworkRest.new) + end + + end + end + end +end diff --git a/extensions/network/config.yaml b/extensions/network/config.yaml new file mode 100644 index 000000000..36323e423 --- /dev/null +++ b/extensions/network/config.yaml @@ -0,0 +1,11 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +beef: + extension: + network: + name: 'Network' + enable: true + authors: ["bcoles"] diff --git a/extensions/network/extension.rb b/extensions/network/extension.rb new file mode 100644 index 000000000..d3c4d17f7 --- /dev/null +++ b/extensions/network/extension.rb @@ -0,0 +1,27 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF +module Extension +module Network + + extend BeEF::API::Extension + + @short_name = 'network' + @full_name = 'Network' + @description = '' + +end +end +end + +require 'extensions/network/network' +require 'extensions/network/models/network_host' +require 'extensions/network/models/network_service' +require 'extensions/network/api' +require 'extensions/network/rest/network' + +require 'dm-serializer' + diff --git a/extensions/network/models/network_host.rb b/extensions/network/models/network_host.rb new file mode 100644 index 000000000..0de5ce0d4 --- /dev/null +++ b/extensions/network/models/network_host.rb @@ -0,0 +1,31 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF + module Core + module Models + # + # Table stores each host identified on the zombie browser's network(s) + # + class NetworkHost + + include DataMapper::Resource + storage_names[:default] = 'network_host' + + property :id, Serial + + property :hooked_browser_id, Text, :lazy => false + property :ip, Text, :lazy => false + property :hostname, String, :lazy => false + property :type, String, :lazy => false # proxy, router, gateway, dns, etc + property :os, String, :lazy => false + property :mac, String, :lazy => false + property :cid, String, :lazy => false # command id or 'init' + + end + + end + end +end diff --git a/extensions/network/models/network_service.rb b/extensions/network/models/network_service.rb new file mode 100644 index 000000000..c806b2aa1 --- /dev/null +++ b/extensions/network/models/network_service.rb @@ -0,0 +1,30 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF + module Core + module Models + # + # Table stores each open port identified on the zombie browser's network(s) + # + class NetworkService + + include DataMapper::Resource + storage_names[:default] = 'network_service' + + property :id, Serial + + property :hooked_browser_id, Text, :lazy => false + property :proto, String, :lazy => false + property :ip, Text, :lazy => false + property :port, String, :lazy => false + property :type, String, :lazy => false + property :cid, String, :lazy => false # command id or 'init' + + end + + end + end +end diff --git a/extensions/network/network.rb b/extensions/network/network.rb new file mode 100644 index 000000000..9a8eed9e2 --- /dev/null +++ b/extensions/network/network.rb @@ -0,0 +1,13 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF +module Extension +module Network + + +end +end +end diff --git a/extensions/network/rest/network.rb b/extensions/network/rest/network.rb new file mode 100644 index 000000000..07096aaca --- /dev/null +++ b/extensions/network/rest/network.rb @@ -0,0 +1,169 @@ +# +# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +module BeEF + module Extension + module Network + + # This class handles the routing of RESTful API requests that interact with network services on the zombie's LAN + class NetworkRest < BeEF::Core::Router::Router + + # Filters out bad requests before performing any routing + before do + config = BeEF::Core::Configuration.instance + @nh = BeEF::Core::Models::NetworkHost + @ns = BeEF::Core::Models::NetworkService + + # Require a valid API token from a valid IP address + halt 401 unless params[:token] == config.get('beef.api_token') + halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip) + + headers 'Content-Type' => 'application/json; charset=UTF-8', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end + + # Returns the entire list of network hosts for all zombies + get '/hosts' do + begin + hosts = @nh.all + count = hosts.length + + result = {} + result[:count] = count + result[:hosts] = hosts.to_json + result.to_json + rescue StandardError => e + print_error "Internal error while retrieving host list (#{e.message})" + halt 500 + end + end + + # Returns the entire list of network services for all zombies + get '/services' do + begin + services = @ns.all + count = services.length + + result = {} + result[:count] = count + result[:services] = services.to_json + result.to_json + rescue StandardError => e + print_error "Internal error while retrieving service list (#{e.message})" + halt 500 + end + end + + # Returns all hosts given a specific hooked browser id + get '/hosts/:id' do + begin + id = params[:id] + + hosts = @nh.all(:hooked_browser_id => id) + count = hosts.length + + result = {} + result[:count] = count + result[:hosts] = hosts + result.to_json + rescue InvalidParamError => e + print_error e.message + halt 400 + rescue StandardError => e + print_error "Internal error while retrieving hosts list for hooked browser with id #{id} (#{e.message})" + halt 500 + end + end + + # Returns all services given a specific hooked browser id + get '/services/:id' do + begin + id = params[:id] + + services = @ns.all(:hooked_browser_id => id) + count = services.length + + result = {} + result[:count] = count + result[:services] = services + result.to_json + rescue InvalidParamError => e + print_error e.message + halt 400 + rescue StandardError => e + print_error "Internal error while retrieving service list for hooked browser with id #{id} (#{e.message})" + halt 500 + end + end + + # Returns a specific host given its id + get '/host/:id' do + begin + id = params[:id] + + host = @nh.all(:id => id) + raise InvalidParamError, 'id' if host.nil? + halt 404 if host.empty? + + host.to_json + rescue InvalidParamError => e + print_error e.message + halt 400 + rescue StandardError => e + print_error "Internal error while retrieving host with id #{id} (#{e.message})" + halt 500 + end + end + + # Returns a specific service given its id + get '/service/:id' do + begin + id = params[:id] + + service = @ns.all(:id => id) + raise InvalidParamError, 'id' if service.nil? + halt 404 if service.empty? + + service.to_json + rescue InvalidParamError => e + print_error e.message + halt 400 + rescue StandardError => e + print_error "Internal error while retrieving service with id #{id} (#{e.message})" + halt 500 + end + end + + # Raised when invalid JSON input is passed to an /api/network handler. + class InvalidJsonError < StandardError + + DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/network handler' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + + end + + # Raised when an invalid named parameter is passed to an /api/network handler. + class InvalidParamError < StandardError + + DEFAULT_MESSAGE = 'Invalid parameter passed to /api/network handler' + + def initialize(message = nil) + str = "Invalid \"%s\" parameter passed to /api/network handler" + message = sprintf str, message unless message.nil? + super(message) + end + + end + + end + + end + end +end