AutorunEngine: Cleanup, rename REST routes, use Core::Models in REST API (#2785)
* arerules: Remove os/browser properties with redundant 'ALL' values * Tools: Add Autorun REST examples * AutorunEngine: Cleanup, rename REST routes, use Core::Models in REST API
This commit is contained in:
@@ -1,9 +1,5 @@
|
|||||||
{"name": "Display an alert",
|
{"name": "Display an alert",
|
||||||
"author": "mgeeky",
|
"author": "mgeeky",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "alert_dialog",
|
{"name": "alert_dialog",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "Start CoinHive JavaScript miner",
|
{"name": "Start CoinHive JavaScript miner",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "coinhive_miner",
|
{"name": "coinhive_miner",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "Confirm Close Tab",
|
{"name": "Confirm Close Tab",
|
||||||
"author": "mgeeky",
|
"author": "mgeeky",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "confirm_close_tab",
|
{"name": "confirm_close_tab",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"name": "Firefox Extension Dropper",
|
"name": "Firefox Extension Dropper",
|
||||||
"author": "antisnatchor",
|
"author": "antisnatchor",
|
||||||
"browser": "FF",
|
"browser": "FF",
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "OSX",
|
"os": "OSX",
|
||||||
"os_version": ">= 10.8",
|
"os_version": ">= 10.8",
|
||||||
"modules": [{
|
"modules": [{
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Get Cookie",
|
"name": "Get Cookie",
|
||||||
"author": "@benichmt1",
|
"author": "@benichmt1",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_cookie",
|
{"name": "get_cookie",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"name": "HTA PowerShell",
|
"name": "HTA PowerShell",
|
||||||
"author": "antisnatchor",
|
"author": "antisnatchor",
|
||||||
"browser": "IE",
|
"browser": "IE",
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "Windows",
|
"os": "Windows",
|
||||||
"os_version": ">= 7",
|
"os_version": ">= 7",
|
||||||
"modules": [
|
"modules": [
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN CORS Scan",
|
{"name": "LAN CORS Scan",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": ["FF", "C"],
|
"browser": ["FF", "C"],
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "LAN CORS Scan (Common IPs)",
|
{"name": "LAN CORS Scan (Common IPs)",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "cross_origin_scanner_cors",
|
{"name": "cross_origin_scanner_cors",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN Fingerprint",
|
{"name": "LAN Fingerprint",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": ["FF", "C"],
|
"browser": ["FF", "C"],
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "LAN Fingerprint (Common IPs)",
|
{"name": "LAN Fingerprint (Common IPs)",
|
||||||
"author": "antisnatchor",
|
"author": "antisnatchor",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "internal_network_fingerprinting",
|
{"name": "internal_network_fingerprinting",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN Flash Scan",
|
{"name": "LAN Flash Scan",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": ["FF", "C"],
|
"browser": ["FF", "C"],
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN Flash Scan (Common IPs)",
|
{"name": "LAN Flash Scan (Common IPs)",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": ["FF", "C"],
|
"browser": ["FF", "C"],
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "cross_origin_scanner_flash",
|
{"name": "cross_origin_scanner_flash",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN HTTP Scan",
|
{"name": "LAN HTTP Scan",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": ["FF", "C"],
|
"browser": ["FF", "C"],
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "LAN HTTP Scan (Common IPs)",
|
{"name": "LAN HTTP Scan (Common IPs)",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_http_servers",
|
{"name": "get_http_servers",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN Ping Sweep",
|
{"name": "LAN Ping Sweep",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "FF",
|
"browser": "FF",
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{"name": "LAN Ping Sweep (Common IPs)",
|
{"name": "LAN Ping Sweep (Common IPs)",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "FF",
|
"browser": "FF",
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "ping_sweep",
|
{"name": "ping_sweep",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "LAN Port Scan",
|
{"name": "LAN Port Scan",
|
||||||
"author": "aburro & aussieklutz",
|
"author": "aburro & aussieklutz",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "LAN SW Port Scan",
|
{"name": "LAN SW Port Scan",
|
||||||
"author": "aburro & aussieklutz",
|
"author": "aburro & aussieklutz",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "get_internal_ip_webrtc",
|
{"name": "get_internal_ip_webrtc",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "Perform Man-In-The-Browser",
|
{"name": "Perform Man-In-The-Browser",
|
||||||
"author": "mgeeky",
|
"author": "mgeeky",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "man_in_the_browser",
|
{"name": "man_in_the_browser",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Raw JavaScript",
|
"name": "Raw JavaScript",
|
||||||
"author": "wade@bindshell.net",
|
"author": "wade@bindshell.net",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "raw_javascript",
|
{"name": "raw_javascript",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{"name": "Collects multiple snapshots of the webpage within Same-Origin",
|
{"name": "Collects multiple snapshots of the webpage within Same-Origin",
|
||||||
"author": "mgeeky",
|
"author": "mgeeky",
|
||||||
"browser": ["FF", "C", "O", "IE", "S"],
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "ALL",
|
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{"name": "spyder_eye",
|
{"name": "spyder_eye",
|
||||||
"condition": null,
|
"condition": null,
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Windows Fake Malware",
|
"name": "Windows Fake Malware",
|
||||||
"author": "bcoles",
|
"author": "bcoles",
|
||||||
"browser": "ALL",
|
|
||||||
"browser_version": "ALL",
|
|
||||||
"os": "Windows",
|
"os": "Windows",
|
||||||
"os_version": "ALL",
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"name": "blockui",
|
"name": "blockui",
|
||||||
|
|||||||
@@ -19,24 +19,231 @@ module BeEF
|
|||||||
@debug_on = @config.get('beef.debug')
|
@debug_on = @config.get('beef.debug')
|
||||||
|
|
||||||
@VERSION = ['<', '<=', '==', '>=', '>', 'ALL']
|
@VERSION = ['<', '<=', '==', '>=', '>', 'ALL']
|
||||||
@VERSION_STR = %w[XP Vista]
|
@VERSION_STR = %w[XP Vista 7]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if there are any ARE rules to be triggered for the specified hooked browser.
|
||||||
|
#
|
||||||
|
# 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 find_matching_rules_for_zombie(browser, browser_version, os, os_version)
|
||||||
|
rules = BeEF::Core::Models::Rule.all
|
||||||
|
|
||||||
|
return if rules.nil?
|
||||||
|
return if rules.empty?
|
||||||
|
|
||||||
|
# TODO: handle cases where there are multiple ARE rules for the same hooked browser.
|
||||||
|
# maybe rules need to have priority or something?
|
||||||
|
|
||||||
|
print_info '[ARE] Checking if any defined rules should be triggered on target.'
|
||||||
|
|
||||||
|
match_rules = []
|
||||||
|
rules.each do |rule|
|
||||||
|
next unless zombie_matches_rule?(browser, browser_version, os, os_version, rule)
|
||||||
|
|
||||||
|
match_rules.push(rule.id)
|
||||||
|
print_more("Hooked browser and OS match rule: #{rule.name}.")
|
||||||
|
end
|
||||||
|
|
||||||
|
print_more("Found [#{match_rules.length}/#{rules.length}] ARE rules matching the hooked browser.")
|
||||||
|
|
||||||
|
match_rules
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Boolean]
|
||||||
|
# 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
|
||||||
|
def zombie_matches_rule?(browser, browser_version, os, os_version, rule)
|
||||||
|
return false if rule.nil?
|
||||||
|
|
||||||
|
unless zombie_browser_matches_rule?(browser, browser_version, rule)
|
||||||
|
print_debug("Browser version check -> (hook) #{browser_version} #{rule.browser_version} (rule) : does not match")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
print_debug("Browser version check -> (hook) #{browser_version} #{rule.browser_version} (rule) : matched")
|
||||||
|
|
||||||
|
unless zombie_os_matches_rule?(os, os_version, rule)
|
||||||
|
print_debug("OS version check -> (hook) #{os_version} #{rule.os_version} (rule): does not match")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
print_debug("OS version check -> (hook) #{os_version} #{rule.os_version} (rule): matched")
|
||||||
|
|
||||||
|
true
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error e.message
|
||||||
|
print_debug e.backtrace.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Boolean]
|
||||||
|
# TODO: This should be updated to support matching multiple OS (like the browser check below)
|
||||||
|
def zombie_os_matches_rule?(os, os_version, rule)
|
||||||
|
return false if rule.nil?
|
||||||
|
|
||||||
|
return false unless rule.os == 'ALL' || os == rule.os
|
||||||
|
|
||||||
|
# check if the OS versions match
|
||||||
|
os_ver_rule_cond = rule.os_version.split(' ').first
|
||||||
|
|
||||||
|
return true if os_ver_rule_cond == 'ALL'
|
||||||
|
|
||||||
|
return false unless @VERSION.include?(os_ver_rule_cond) || @VERSION_STR.include?(os_ver_rule_cond)
|
||||||
|
|
||||||
|
os_ver_rule_maj = rule.os_version.split(' ').last.split('.').first
|
||||||
|
os_ver_rule_min = rule.os_version.split(' ').last.split('.').last
|
||||||
|
|
||||||
|
if os_ver_rule_maj == 'XP'
|
||||||
|
os_ver_rule_maj = 5
|
||||||
|
os_ver_rule_min = 0
|
||||||
|
elsif os_ver_rule_maj == 'Vista'
|
||||||
|
os_ver_rule_maj = 6
|
||||||
|
os_ver_rule_min = 0
|
||||||
|
elsif os_ver_rule_maj == '7'
|
||||||
|
os_ver_rule_maj = 6
|
||||||
|
os_ver_rule_min = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# XP is Windows 5.0 and Vista is Windows 6.0. Easier for comparison later on.
|
||||||
|
# TODO: BUG: This will fail horribly if the target OS is Windows 7 or newer,
|
||||||
|
# as no version normalization is performed.
|
||||||
|
# TODO: Update this for every OS since Vista/7 ...
|
||||||
|
if os_version == 'XP'
|
||||||
|
os_ver_hook_maj = 5
|
||||||
|
os_ver_hook_min = 0
|
||||||
|
elsif os_version == 'Vista'
|
||||||
|
os_ver_hook_maj = 6
|
||||||
|
os_ver_hook_min = 0
|
||||||
|
elsif os_version == '7'
|
||||||
|
os_ver_hook_maj = 6
|
||||||
|
os_ver_hook_min = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !os_version.nil? || rule.os_version != 'ALL'
|
||||||
|
os_major_version_match = compare_versions(os_ver_hook_maj.to_s, os_ver_rule_cond, os_ver_rule_maj.to_s)
|
||||||
|
os_minor_version_match = compare_versions(os_ver_hook_min.to_s, os_ver_rule_cond, os_ver_rule_min.to_s)
|
||||||
|
return false unless (os_major_version_match && os_minor_version_match)
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error e.message
|
||||||
|
print_debug e.backtrace.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Boolean]
|
||||||
|
def zombie_browser_matches_rule?(browser, browser_version, rule)
|
||||||
|
return false if rule.nil?
|
||||||
|
|
||||||
|
b_ver_cond = rule.browser_version.split(' ').first
|
||||||
|
|
||||||
|
return false unless @VERSION.include?(b_ver_cond)
|
||||||
|
|
||||||
|
b_ver = rule.browser_version.split(' ').last
|
||||||
|
|
||||||
|
return false unless BeEF::Filters.is_valid_browserversion?(b_ver)
|
||||||
|
|
||||||
|
# check if rule specifies multiple browsers
|
||||||
|
if rule.browser =~ /\A[A-Z]+\Z/
|
||||||
|
return false unless rule.browser == 'ALL' || browser == rule.browser
|
||||||
|
|
||||||
|
# check if the browser version matches
|
||||||
|
browser_version_match = compare_versions(browser_version.to_s, b_ver_cond, b_ver.to_s)
|
||||||
|
return false unless browser_version_match
|
||||||
|
else
|
||||||
|
browser_match = false
|
||||||
|
rule.browser.gsub(/[^A-Z,]/i, '').split(',').each do |b|
|
||||||
|
if b == browser || b == 'ALL'
|
||||||
|
browser_match = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false unless browser_match
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error e.message
|
||||||
|
print_debug e.backtrace.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the hooked browser type/version and OS type/version match any Rule-sets
|
# 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
|
# stored in the BeEF::Core::Models::Rule database table
|
||||||
# If one or more Rule-sets do match, trigger the module chain specified
|
# If one or more Rule-sets do match, trigger the module chain specified
|
||||||
def run(hb_id, browser_name, browser_version, os_name, os_version)
|
def find_and_run_all_matching_rules_for_zombie(hb_id)
|
||||||
|
return if hb_id.nil?
|
||||||
|
|
||||||
|
hb_details = BeEF::Core::Models::BrowserDetails
|
||||||
|
browser_name = hb_details.get(hb_id, 'browser.name')
|
||||||
|
browser_version = hb_details.get(hb_id, 'browser.version')
|
||||||
|
os_name = hb_details.get(hb_id, 'host.os.name')
|
||||||
|
os_version = hb_details.get(hb_id, 'host.os.version')
|
||||||
|
|
||||||
are = BeEF::Core::AutorunEngine::Engine.instance
|
are = BeEF::Core::AutorunEngine::Engine.instance
|
||||||
match_rules = are.match(browser_name, browser_version, os_name, os_version)
|
rules = are.find_matching_rules_for_zombie(browser_name, browser_version, os_name, os_version)
|
||||||
are.trigger(match_rules, hb_id) if !match_rules.nil? && match_rules.length > 0
|
|
||||||
|
return if rules.nil?
|
||||||
|
return if rules.empty?
|
||||||
|
|
||||||
|
are.run_rules_on_zombie(rules, hb_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Run the specified rule IDs on the specified zombie ID
|
||||||
|
# only if the rules match.
|
||||||
|
def run_matching_rules_on_zombie(rule_ids, hb_id)
|
||||||
|
return if rule_ids.nil?
|
||||||
|
return if hb_id.nil?
|
||||||
|
|
||||||
|
rule_ids = [rule_ids.to_i] if rule_ids.is_a?(String)
|
||||||
|
|
||||||
|
hb_details = BeEF::Core::Models::BrowserDetails
|
||||||
|
browser_name = hb_details.get(hb_id, 'browser.name')
|
||||||
|
browser_version = hb_details.get(hb_id, 'browser.version')
|
||||||
|
os_name = hb_details.get(hb_id, 'host.os.name')
|
||||||
|
os_version = hb_details.get(hb_id, 'host.os.version')
|
||||||
|
|
||||||
|
are = BeEF::Core::AutorunEngine::Engine.instance
|
||||||
|
rules = are.find_matching_rules_for_zombie(browser_name, browser_version, os_name, os_version)
|
||||||
|
|
||||||
|
return if rules.nil?
|
||||||
|
return if rules.empty?
|
||||||
|
|
||||||
|
new_rules = []
|
||||||
|
rules.each do |rule|
|
||||||
|
new_rules << rule if rule_ids.include?(rule)
|
||||||
|
end
|
||||||
|
|
||||||
|
return if new_rules.empty?
|
||||||
|
|
||||||
|
are.run_rules_on_zombie(new_rules, hb_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the specified rule IDs on the specified zombie ID
|
||||||
|
# regardless of whether the rules match.
|
||||||
# Prepare and return the JavaScript of the modules to be sent.
|
# Prepare and return the JavaScript of the modules to be sent.
|
||||||
# It also updates the rules ARE execution table with timings
|
# It also updates the rules ARE execution table with timings
|
||||||
def trigger(rule_ids, hb_id)
|
def run_rules_on_zombie(rule_ids, hb_id)
|
||||||
|
return if rule_ids.nil?
|
||||||
|
return if hb_id.nil?
|
||||||
|
|
||||||
hb = BeEF::HBManager.get_by_id(hb_id)
|
hb = BeEF::HBManager.get_by_id(hb_id)
|
||||||
hb_session = hb.session
|
hb_session = hb.session
|
||||||
|
|
||||||
|
rule_ids = [rule_ids] if rule_ids.is_a?(Integer)
|
||||||
|
|
||||||
rule_ids.each do |rule_id|
|
rule_ids.each do |rule_id|
|
||||||
rule = BeEF::Core::Models::Rule.find(rule_id)
|
rule = BeEF::Core::Models::Rule.find(rule_id)
|
||||||
modules = JSON.parse(rule.modules)
|
modules = JSON.parse(rule.modules)
|
||||||
@@ -86,6 +293,8 @@ module BeEF
|
|||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print_more "Triggering rules #{rule_ids} on HB #{hb_id}"
|
||||||
|
|
||||||
are_exec = BeEF::Core::Models::Execution.new(
|
are_exec = BeEF::Core::Models::Execution.new(
|
||||||
session_id: hb_session,
|
session_id: hb_session,
|
||||||
mod_count: modules.length,
|
mod_count: modules.length,
|
||||||
@@ -96,12 +305,11 @@ module BeEF
|
|||||||
rule_id: rule_id
|
rule_id: rule_id
|
||||||
)
|
)
|
||||||
are_exec.save!
|
are_exec.save!
|
||||||
|
|
||||||
# Once Engine.check() verified that the hooked browser match a Rule, trigger the Rule ;-)
|
|
||||||
print_more "Triggering ruleset #{rule_ids} on HB #{hb_id}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
# Wraps module bodies in their own function, using setTimeout to trigger them with an eventual delay.
|
# Wraps module bodies in their own function, using setTimeout to trigger them with an eventual delay.
|
||||||
# Launch order is also taken care of.
|
# Launch order is also taken care of.
|
||||||
# - sequential chain with delays (setTimeout stuff)
|
# - sequential chain with delays (setTimeout stuff)
|
||||||
@@ -345,20 +553,18 @@ module BeEF
|
|||||||
print_error '[ARE] Could not find module end index' if wrapper_end_index.nil?
|
print_error '[ARE] Could not find module end index' if wrapper_end_index.nil?
|
||||||
|
|
||||||
cleaned_cmd_body = cmd_body.slice(wrapper_start_index..-(wrapper_end_index + 1)).join("\n")
|
cleaned_cmd_body = cmd_body.slice(wrapper_start_index..-(wrapper_end_index + 1)).join("\n")
|
||||||
|
|
||||||
print_error '[ARE] No command to send' if cleaned_cmd_body.eql?('')
|
print_error '[ARE] No command to send' if cleaned_cmd_body.eql?('')
|
||||||
|
|
||||||
# check if <<mod_input>> should be replaced with a variable name (depending if the variable is a string or number)
|
# check if <<mod_input>> should be replaced with a variable name (depending if the variable is a string or number)
|
||||||
if replace_input
|
return cleaned_cmd_body unless replace_input
|
||||||
if cleaned_cmd_body.include?('"<<mod_input>>"')
|
|
||||||
final_cmd_body = cleaned_cmd_body.gsub('"<<mod_input>>"', 'mod_input')
|
if cleaned_cmd_body.include?('"<<mod_input>>"')
|
||||||
elsif cleaned_cmd_body.include?('\'<<mod_input>>\'')
|
cleaned_cmd_body.gsub('"<<mod_input>>"', 'mod_input')
|
||||||
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
|
elsif cleaned_cmd_body.include?('\'<<mod_input>>\'')
|
||||||
elsif cleaned_cmd_body.include?('<<mod_input>>')
|
cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
|
||||||
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
|
elsif cleaned_cmd_body.include?('<<mod_input>>')
|
||||||
else
|
cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
|
||||||
return cleaned_cmd_body
|
|
||||||
end
|
|
||||||
final_cmd_body
|
|
||||||
else
|
else
|
||||||
cleaned_cmd_body
|
cleaned_cmd_body
|
||||||
end
|
end
|
||||||
@@ -366,129 +572,6 @@ module BeEF
|
|||||||
print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body. #{e.message}"
|
print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body. #{e.message}"
|
||||||
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 = []
|
|
||||||
rules = if rule_id.nil?
|
|
||||||
BeEF::Core::Models::Rule.all
|
|
||||||
else
|
|
||||||
[BeEF::Core::Models::Rule.find(rule_id)]
|
|
||||||
end
|
|
||||||
return nil if rules.nil?
|
|
||||||
return nil unless rules.length > 0
|
|
||||||
|
|
||||||
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|
|
|
||||||
browser_match = false
|
|
||||||
os_match = false
|
|
||||||
|
|
||||||
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.
|
|
||||||
if os_version == 'XP'
|
|
||||||
os_ver_hook_maj = 5
|
|
||||||
os_ver_hook_min = 0
|
|
||||||
end
|
|
||||||
if os_version == 'Vista'
|
|
||||||
os_ver_hook_maj = 6
|
|
||||||
os_ver_hook_min = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if os_ver_rule_maj == 'XP'
|
|
||||||
os_ver_rule_maj = 5
|
|
||||||
os_ver_rule_min = 0
|
|
||||||
end
|
|
||||||
if os_ver_rule_maj == 'Vista'
|
|
||||||
os_ver_rule_maj = 6
|
|
||||||
os_ver_rule_min = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# skip rule unless the browser matches
|
|
||||||
browser_match = false
|
|
||||||
# check if rule specifies multiple browsers
|
|
||||||
if rule.browser =~ /\A[A-Z]+\Z/
|
|
||||||
next unless rule.browser == 'ALL' || browser == rule.browser
|
|
||||||
|
|
||||||
# check if the browser version matches
|
|
||||||
browser_version_match = compare_versions(browser_version.to_s, b_ver_cond, b_ver.to_s)
|
|
||||||
browser_match = if browser_version_match
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
print_more "Browser version check -> (hook) #{browser_version} #{rule.browser_version} (rule) : #{browser_version_match}"
|
|
||||||
else
|
|
||||||
rule.browser.gsub(/[^A-Z,]/i, '').split(',').each do |b|
|
|
||||||
browser_match = true if b == browser || b == 'ALL'
|
|
||||||
end
|
|
||||||
# else, only one browser
|
|
||||||
end
|
|
||||||
next unless browser_match
|
|
||||||
|
|
||||||
# skip rule unless the OS matches
|
|
||||||
next unless rule.os == 'ALL' || os == rule.os
|
|
||||||
|
|
||||||
# check if the OS versions match
|
|
||||||
if !os_version.nil? || rule.os_version != 'ALL'
|
|
||||||
os_major_version_match = compare_versions(os_ver_hook_maj.to_s, os_ver_rule_cond, os_ver_rule_maj.to_s)
|
|
||||||
os_minor_version_match = compare_versions(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 = true
|
|
||||||
os_minor_version_match = 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 StandardError => e
|
|
||||||
print_error e.message
|
|
||||||
print_debug e.backtrace.join("\n")
|
|
||||||
end
|
|
||||||
print_more "Found [#{match_rules.length}/#{rules.length}] ARE rules matching the hooked browser type/version."
|
|
||||||
|
|
||||||
match_rules
|
|
||||||
end
|
|
||||||
|
|
||||||
# compare versions
|
# compare versions
|
||||||
def compare_versions(ver_a, cond, ver_b)
|
def compare_versions(ver_a, cond, ver_b)
|
||||||
return true if cond == 'ALL'
|
return true if cond == 'ALL'
|
||||||
|
|||||||
@@ -28,11 +28,13 @@ module BeEF
|
|||||||
|
|
||||||
# validate hook session value
|
# validate hook session value
|
||||||
session_id = get_param(@data, 'beefhook')
|
session_id = get_param(@data, 'beefhook')
|
||||||
|
|
||||||
print_debug "[INIT] Processing Browser Details for session #{session_id}"
|
print_debug "[INIT] Processing Browser Details for session #{session_id}"
|
||||||
unless BeEF::Filters.is_valid_hook_session_id?(session_id)
|
unless BeEF::Filters.is_valid_hook_session_id?(session_id)
|
||||||
(err_msg 'session id is invalid'
|
err_msg 'session id is invalid'
|
||||||
return)
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
hooked_browser = HB.where(session: session_id).first
|
hooked_browser = HB.where(session: session_id).first
|
||||||
return unless hooked_browser.nil? # browser is already registered with framework
|
return unless hooked_browser.nil? # browser is already registered with framework
|
||||||
|
|
||||||
@@ -567,7 +569,7 @@ module BeEF
|
|||||||
|
|
||||||
# check if any ARE rules shall be triggered only if the channel is != WebSockets (XHR). If the channel
|
# check if any ARE rules shall be triggered only if the channel is != WebSockets (XHR). If the channel
|
||||||
# is WebSockets, then ARe rules are triggered after channel is established.
|
# is WebSockets, then ARe rules are triggered after channel is established.
|
||||||
BeEF::Core::AutorunEngine::Engine.instance.run(zombie.id, browser_name, browser_version, os_name, os_version) unless config.get('beef.http.websocket.enable')
|
BeEF::Core::AutorunEngine::Engine.instance.find_and_run_all_matching_rules_for_zombie(zombie.id) unless config.get('beef.http.websocket.enable')
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_param(query, key)
|
def get_param(query, key)
|
||||||
|
|||||||
@@ -10,14 +10,13 @@ module BeEF
|
|||||||
# Objects stores known 'legacy' browser User Agents.
|
# Objects stores known 'legacy' browser User Agents.
|
||||||
#
|
#
|
||||||
# This table is used to determine if a hooked browser is a 'legacy'
|
# This table is used to determine if a hooked browser is a 'legacy'
|
||||||
# browser and therefore which version of the hook file to generate and use
|
# browser.
|
||||||
#
|
#
|
||||||
# TODO: make it an actual table
|
# TODO: make it an actual table
|
||||||
#
|
#
|
||||||
module LegacyBrowserUserAgents
|
module LegacyBrowserUserAgents
|
||||||
def self.user_agents
|
def self.user_agents
|
||||||
[
|
[
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0'
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ module BeEF
|
|||||||
MOUNTS = BeEF::Core::Server.instance.mounts
|
MOUNTS = BeEF::Core::Server.instance.mounts
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
return unless @@config.get('beef.websocket.enable')
|
||||||
|
|
||||||
secure = @@config.get('beef.http.websocket.secure')
|
secure = @@config.get('beef.http.websocket.secure')
|
||||||
|
|
||||||
# @note Start a WSS server socket
|
# @note Start a WSS server socket
|
||||||
@@ -110,11 +112,7 @@ module BeEF
|
|||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
browser_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.name')
|
BeEF::Core::AutorunEngine::Engine.instance.find_and_run_all_matching_rules_for_zombie(hooked_browser.id)
|
||||||
browser_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.version')
|
|
||||||
os_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.name')
|
|
||||||
os_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.version')
|
|
||||||
BeEF::Core::AutorunEngine::Engine.instance.run(hooked_browser.id, browser_name, browser_version, os_name, os_version)
|
|
||||||
|
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,106 +19,117 @@ module BeEF
|
|||||||
'Expires' => '0'
|
'Expires' => '0'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a new ruleset. Returne the rule_id if request was successful
|
#
|
||||||
|
# Get all rules
|
||||||
|
#
|
||||||
|
get '/rules' do
|
||||||
|
rules = BeEF::Core::Models::Rule.all
|
||||||
|
{
|
||||||
|
'success' => true,
|
||||||
|
'count' => rules.length,
|
||||||
|
'rules' => rules.to_json
|
||||||
|
}.to_json
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error("Internal error while retrieving Autorun rules: #{e.message}")
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a specific rule by ID
|
||||||
|
get '/rule/:rule_id' do
|
||||||
|
rule_id = params[:rule_id]
|
||||||
|
|
||||||
|
rule = BeEF::Core::Models::Rule.find(rule_id)
|
||||||
|
raise InvalidParameterError, 'id' if rule.nil?
|
||||||
|
|
||||||
|
halt 404 if rule.empty?
|
||||||
|
|
||||||
|
rule.to_json
|
||||||
|
rescue InvalidParameterError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving Autorun rule with id #{rule_id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add a new ruleset. Return the rule_id if request was successful.
|
||||||
|
# @return [Integer] rule ID
|
||||||
|
#
|
||||||
post '/rule/add' do
|
post '/rule/add' do
|
||||||
request.body.rewind
|
request.body.rewind
|
||||||
begin
|
data = JSON.parse request.body.read
|
||||||
data = JSON.parse request.body.read
|
rloader = BeEF::Core::AutorunEngine::RuleLoader.instance
|
||||||
rloader = BeEF::Core::AutorunEngine::RuleLoader.instance
|
rloader.load_rule_json(data).to_json
|
||||||
rloader.load(data).to_json
|
|
||||||
rescue StandardError => e
|
|
||||||
err = 'Malformed JSON ruleset.'
|
|
||||||
print_error "[ARE] ERROR: #{e.message}"
|
|
||||||
{ 'success' => false, 'error' => err }.to_json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Delete a ruleset
|
|
||||||
get '/rule/delete/:rule_id' do
|
|
||||||
rule_id = params[:rule_id]
|
|
||||||
rule = BeEF::Core::AutorunEngine::Models::Rule.find(rule_id)
|
|
||||||
rule.destroy
|
|
||||||
{ 'success' => true }.to_json
|
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
err = 'Error getting rule.'
|
print_error "Internal error while adding Autorun rule: #{e.message}"
|
||||||
print_error "[ARE] ERROR: #{e.message}"
|
{ 'success' => false, 'error' => e.message }.to_json
|
||||||
{ 'success' => false, 'error' => err }.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Trigger a specified rule_id on online hooked browsers. Offline hooked browsers are ignored
|
#
|
||||||
get '/rule/trigger/:rule_id' do
|
# Delete a ruleset
|
||||||
|
#
|
||||||
|
delete '/rule/:rule_id' do
|
||||||
|
rule_id = params[:rule_id]
|
||||||
|
rule = BeEF::Core::Models::Rule.find(rule_id)
|
||||||
|
raise InvalidParameterError, 'id' if rule.nil?
|
||||||
|
rule.destroy
|
||||||
|
|
||||||
|
{ 'success' => true }.to_json
|
||||||
|
rescue InvalidParameterError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while deleting Autorun rule: #{e.message}"
|
||||||
|
{ 'success' => false, 'error' => e.message }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Run a specified rule on all online hooked browsers (if the zombie matches the rule).
|
||||||
|
# Offline hooked browsers are ignored
|
||||||
|
#
|
||||||
|
get '/run/:rule_id' do
|
||||||
rule_id = params[:rule_id]
|
rule_id = params[:rule_id]
|
||||||
|
|
||||||
online_hooks = BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))
|
online_hooks = BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))
|
||||||
are = BeEF::Core::AutorunEngine::Engine.instance
|
|
||||||
|
|
||||||
if online_hooks.nil?
|
if online_hooks.nil?
|
||||||
{ 'success' => false, 'error' => 'There are currently no hooked browsers online.' }.to_json
|
return { 'success' => false, 'error' => 'There are currently no hooked browsers online.' }.to_json
|
||||||
else
|
|
||||||
online_hooks.each do |hb|
|
|
||||||
hb_details = BeEF::Core::Models::BrowserDetails
|
|
||||||
browser_name = hb_details.get(hb.session, 'browser.name')
|
|
||||||
browser_version = hb_details.get(hb.session, 'browser.version')
|
|
||||||
os_name = hb_details.get(hb.session, 'host.os.name')
|
|
||||||
os_version = hb_details.get(hb.session, 'host.os.version')
|
|
||||||
|
|
||||||
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
|
|
||||||
{ 'success' => true }.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
are = BeEF::Core::AutorunEngine::Engine.instance
|
||||||
|
online_hooks.each do |hb|
|
||||||
|
are.run_matching_rules_on_zombie(rule_id, hb.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
{ 'success' => true }.to_json
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
err = 'Malformed JSON ruleset.'
|
msg = "Could not trigger rules: #{e.message}"
|
||||||
print_error "[ARE] ERROR: #{e.message}"
|
print_error "[ARE] #{msg}"
|
||||||
{ 'success' => false, 'error' => err }.to_json
|
{ 'success' => false, 'error' => msg }.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delete a ruleset
|
#
|
||||||
get '/rule/list/:rule_id' do
|
# Run a specified rule on the specified hooked browser.
|
||||||
|
#
|
||||||
|
get '/run/:rule_id/:hb_id' do
|
||||||
rule_id = params[:rule_id]
|
rule_id = params[:rule_id]
|
||||||
if rule_id == 'all'
|
hb_id = params[:hb_id]
|
||||||
result = []
|
|
||||||
rules = BeEF::Core::AutorunEngine::Models::Rule.all
|
raise InvalidParameterError, 'rule_id' if rule_id.nil?
|
||||||
rules.each do |rule|
|
raise InvalidParameterError, 'hb_id' if hb_id.nil?
|
||||||
{
|
|
||||||
'id' => rule.id,
|
are = BeEF::Core::AutorunEngine::Engine.instance
|
||||||
'name' => rule.name,
|
are.run_matching_rules_on_zombie(rule_id, hb_id)
|
||||||
'author' => rule.author,
|
|
||||||
'browser' => rule.browser,
|
{ 'success' => true }.to_json
|
||||||
'browser_version' => rule.browser_version,
|
rescue InvalidParameterError => e
|
||||||
'os' => rule.os,
|
print_error e.message
|
||||||
'os_version' => rule.os_version,
|
halt 400
|
||||||
'modules' => rule.modules,
|
|
||||||
'execution_order' => rule.execution_order,
|
|
||||||
'execution_delay' => rule.execution_delay,
|
|
||||||
'chain_mode' => rule.chain_mode
|
|
||||||
}
|
|
||||||
result.push rule
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result = nil
|
|
||||||
rule = BeEF::Core::AutorunEngine::Models::Rule.get(rule_id)
|
|
||||||
unless rule.nil?
|
|
||||||
result = {
|
|
||||||
'id' => rule.id,
|
|
||||||
'name' => rule.name,
|
|
||||||
'author' => rule.author,
|
|
||||||
'browser' => rule.browser,
|
|
||||||
'browser_version' => rule.browser_version,
|
|
||||||
'os' => rule.os,
|
|
||||||
'os_version' => rule.os_version,
|
|
||||||
'modules' => rule.modules,
|
|
||||||
'execution_order' => rule.execution_order,
|
|
||||||
'execution_delay' => rule.execution_delay,
|
|
||||||
'chain_mode' => rule.chain_mode
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
{ 'success' => true, 'rules' => result }.to_json
|
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
err = 'Error getting rule(s)'
|
msg = "Could not trigger rule: #{e.message}"
|
||||||
print_error "[ARE] ERROR: #{e.message}"
|
print_error "[ARE] #{msg}"
|
||||||
{ 'success' => false, 'error' => err }.to_json
|
{ 'success' => false, 'error' => msg }.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
102
tools/rest_api_examples/autorun
Normal file
102
tools/rest_api_examples/autorun
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# browser-details - Example BeEF RESTful API script
|
||||||
|
# Retrieves all Autorun rules, adds a rule, runs it on all online browsers, then deletes it
|
||||||
|
# Refer to the wiki for info: https://github.com/beefproject/beef/wiki/BeEF-RESTful-API
|
||||||
|
##
|
||||||
|
require 'rest-client'
|
||||||
|
require 'json'
|
||||||
|
require 'optparse'
|
||||||
|
require 'pp'
|
||||||
|
require './lib/string' # colored strings
|
||||||
|
require './lib/print' # print wrappers
|
||||||
|
require './lib/beef_rest_api'
|
||||||
|
|
||||||
|
if ARGV.length == 0
|
||||||
|
puts "#{$0}:"
|
||||||
|
puts "| Example BeEF RESTful API script"
|
||||||
|
puts "| Use --help for help"
|
||||||
|
puts "|_ Use verbose mode (-v) and debug mode (-d) for more output"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# API config
|
||||||
|
proto = 'http'
|
||||||
|
host = '127.0.0.1'
|
||||||
|
port = '3000'
|
||||||
|
user = 'beef'
|
||||||
|
pass = 'beef'
|
||||||
|
|
||||||
|
# Command line options
|
||||||
|
@debug = false
|
||||||
|
@verbose = false
|
||||||
|
OptionParser.new do |opts|
|
||||||
|
opts.on('-h', '--help', 'Shows this help screen') do
|
||||||
|
puts opts
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
opts.on('--host HOST', "Set BeEF host (default: #{host})") do |h|
|
||||||
|
host = h
|
||||||
|
end
|
||||||
|
opts.on('--port PORT', "Set BeEF port (default: #{port})") do |p|
|
||||||
|
port = p
|
||||||
|
end
|
||||||
|
opts.on('--user USERNAME', "Set BeEF username (default: #{user})") do |u|
|
||||||
|
user = u
|
||||||
|
end
|
||||||
|
opts.on('--pass PASSWORD', "Set BeEF password (default: #{pass})") do |p|
|
||||||
|
pass = p
|
||||||
|
end
|
||||||
|
opts.on('--ssl', 'Use HTTPS') do
|
||||||
|
proto = 'https'
|
||||||
|
end
|
||||||
|
opts.on('-v', '--verbose', 'Enable verbose output') do
|
||||||
|
@verbose = true
|
||||||
|
end
|
||||||
|
opts.on('-d', '--debug', 'Enable debug output') do
|
||||||
|
@debug = true
|
||||||
|
end
|
||||||
|
end.parse!
|
||||||
|
|
||||||
|
@api = BeefRestAPI.new proto, host, port, user, pass
|
||||||
|
|
||||||
|
# Retrieve the RESTful API token
|
||||||
|
print_status "Authenticating to: #{proto}://#{host}:#{port}"
|
||||||
|
@api.auth
|
||||||
|
|
||||||
|
# Retrieve BeEF version
|
||||||
|
@api.version
|
||||||
|
|
||||||
|
print_status("Retrieving Autorun rules")
|
||||||
|
rules = @api.autorun_rules
|
||||||
|
print_debug(rules)
|
||||||
|
|
||||||
|
print_status("Adding a rule")
|
||||||
|
|
||||||
|
res = @api.autorun_add_rule({
|
||||||
|
"name": "Say Hello",
|
||||||
|
"author": "REST API",
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"name": "alert_dialog",
|
||||||
|
"options": {
|
||||||
|
"text":"Hello from REST API"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"execution_order": [0],
|
||||||
|
"execution_delay": [0]
|
||||||
|
})
|
||||||
|
|
||||||
|
print_debug(res)
|
||||||
|
|
||||||
|
rule_id = res['rule_id']
|
||||||
|
|
||||||
|
unless rule_id.nil?
|
||||||
|
print_status "Running rule #{rule_id} on all browsers"
|
||||||
|
res = @api.autorun_run_rule_on_all_browsers(rule_id)
|
||||||
|
print_debug(res)
|
||||||
|
|
||||||
|
print_status("Deleting rule #{rule_id}")
|
||||||
|
res = @api.autorun_delete_rule(rule_id)
|
||||||
|
print_debug(res)
|
||||||
|
end
|
||||||
@@ -420,20 +420,25 @@ end
|
|||||||
# add a rule
|
# add a rule
|
||||||
def dns_add_rule(dns_pattern, dns_resource, dns_response)
|
def dns_add_rule(dns_pattern, dns_resource, dns_response)
|
||||||
dns_response = [dns_response] if dns_response.is_a?(String)
|
dns_response = [dns_response] if dns_response.is_a?(String)
|
||||||
begin
|
print_verbose "Adding DNS rule [pattern: #{dns_pattern}, resource: #{dns_resource}, response: #{dns_response}]"
|
||||||
print_verbose "Adding DNS rule [pattern: #{dns_pattern}, resource: #{dns_resource}, response: #{dns_response}]"
|
response = RestClient.post "#{@url}dns/rule?token=#{@token}", {
|
||||||
response = RestClient.post "#{@url}dns/rule?token=#{@token}", {
|
'pattern' => dns_pattern,
|
||||||
'pattern' => dns_pattern,
|
'resource' => dns_resource,
|
||||||
'resource' => dns_resource,
|
'response' => dns_response }.to_json,
|
||||||
'response' => dns_response }.to_json,
|
:content_type => :json,
|
||||||
:content_type => :json,
|
:accept => :json
|
||||||
:accept => :json
|
details = JSON.parse(response.body)
|
||||||
details = JSON.parse(response.body)
|
rule_id = details['id']
|
||||||
print_good "Added rule [id: #{details['id']}]"
|
|
||||||
details
|
if rule_id.nil?
|
||||||
rescue => e
|
print_error("Could not add DNS rule: #{details['error']}")
|
||||||
print_error "Could not add DNS rule: #{e.message}"
|
return details
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print_good "Added rule [id: #{details['id']}]"
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error "Could not add DNS rule: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# get rule details
|
# get rule details
|
||||||
@@ -451,14 +456,78 @@ end
|
|||||||
|
|
||||||
# delete a rule
|
# delete a rule
|
||||||
def dns_delete_rule(id)
|
def dns_delete_rule(id)
|
||||||
begin
|
response = RestClient.delete "#{@url}dns/rule/#{id}?token=#{@token}"
|
||||||
response = RestClient.delete "#{@url}dns/rule/#{id}?token=#{@token}"
|
details = JSON.parse(response.body)
|
||||||
details = JSON.parse(response.body)
|
print_good "Deleted rule [id: #{id}]"
|
||||||
print_good "Deleted rule [id: #{id}]"
|
details
|
||||||
details
|
rescue => e
|
||||||
rescue => e
|
print_error "Could not delete DNS rule: #{e.message}"
|
||||||
print_error "Could not delete DNS rule: #{e.message}"
|
end
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
### Autorun
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
def autorun_rules
|
||||||
|
print_verbose "Retrieving Autorun rules"
|
||||||
|
response = RestClient.get "#{@url}autorun/rules", {:params => {:token => @token}}
|
||||||
|
details = JSON.parse(response.body)
|
||||||
|
print_good("Retrieved #{details['count']} rules")
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error("Could not retrieve Autorun rules: #{e.message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def autorun_delete_rule(id)
|
||||||
|
print_verbose "Deleting Autorun rule with ID: #{id}"
|
||||||
|
response = RestClient.delete "#{@url}autorun/rule/#{id}?token=#{@token}"
|
||||||
|
details = JSON.parse(response.body)
|
||||||
|
print_good("Deleted rule [id: #{id}]")
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error("Could not delete Autorun rule: #{e.message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def autorun_add_rule(data)
|
||||||
|
print_verbose "Adding Autorun rule: #{data}"
|
||||||
|
response = RestClient.post "#{@url}autorun/rule/add?token=#{@token}",
|
||||||
|
data.to_json,
|
||||||
|
:content_type => :json,
|
||||||
|
:accept => :json
|
||||||
|
details = JSON.parse(response.body)
|
||||||
|
rule_id = details['rule_id']
|
||||||
|
|
||||||
|
if rule_id.nil?
|
||||||
|
print_error("Could not add Autorun rule: #{details['error']}")
|
||||||
|
return details
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print_good("Added rule [id: #{details['id']}]")
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error("Could not add Autorun rule: #{e.message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def autorun_run_rule_on_all_browsers(rule_id)
|
||||||
|
print_verbose "Running Autorun rule #{rule_id} on all browsers"
|
||||||
|
response = RestClient.get "#{@url}autorun/run/#{rule_id}", {:params => {:token => @token}}
|
||||||
|
details = JSON.parse(response.body)
|
||||||
|
print_debug details
|
||||||
|
print_good('Done')
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error "Could not run Autorun rule #{rule_id}: #{e.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def autorun_run_rule_on_browser(rule_id, hb_id)
|
||||||
|
print_verbose "Running Autorun rule #{rule_id} on browser #{hb_id}"
|
||||||
|
response = RestClient.get "#{@url}autorun/run/#{rule_id}/#{hb_id}", {:params => {:token => @token}}
|
||||||
|
details = JSON.parse(response.body)
|
||||||
|
print_good('Done')
|
||||||
|
details
|
||||||
|
rescue => e
|
||||||
|
print_error "Could not run Autorun rule #{rule_id}: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user