Cleanup evasion extension; disable scramble obfuscation
This commit is contained in:
@@ -46,6 +46,32 @@ module Core
|
||||
token
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a random alphanumeric string
|
||||
# Note: this isn't securely random
|
||||
# @todo use SecureRandom once Ruby 2.4 is EOL
|
||||
#
|
||||
# @param length integer length of returned string
|
||||
#
|
||||
def self.random_alphanum_string(length = 10)
|
||||
raise TypeError, 'Invalid length' unless length.integer?
|
||||
raise TypeError, 'Invalid length' unless length.positive?
|
||||
|
||||
[*('a'..'z'),*('A'..'Z'),*('0'..'9')].shuffle[0,length].join
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a random hex string
|
||||
#
|
||||
# @param length integer length of returned string
|
||||
#
|
||||
def self.random_hex_string(length = 10)
|
||||
raise TypeError, 'Invalid length' unless length.integer?
|
||||
raise TypeError, 'Invalid length' unless length.positive?
|
||||
|
||||
OpenSSL::Random.random_bytes(length).unpack('H*').first[0...length]
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a unique identifier for DNS rules.
|
||||
#
|
||||
@@ -53,10 +79,9 @@ module Core
|
||||
#
|
||||
def self.dns_rule_id
|
||||
id = nil
|
||||
length = 4
|
||||
|
||||
begin
|
||||
id = OpenSSL::Random.random_bytes(length).unpack('H*')[0]
|
||||
id = random_hex_string(8)
|
||||
BeEF::Core::Models::Dns::Rule.each { |rule| throw StandardError if id == rule.id }
|
||||
rescue StandardError
|
||||
retry
|
||||
|
||||
@@ -9,13 +9,19 @@ beef:
|
||||
enable: false
|
||||
name: 'Evasion'
|
||||
authors: ["antisnatchor"]
|
||||
|
||||
# Exclude code JavaScript libraries
|
||||
exclude_core_js: ["lib/jquery-1.12.4.min.js", "lib/json2.js", "lib/jools.min.js", "lib/mdetect.js"]
|
||||
scramble_variables: true
|
||||
scramble_cookies: true
|
||||
|
||||
# Obfuscation methods are executed in the order in which they're provided here
|
||||
# Available techniques: ["minify", "base_64", "whitespace"]
|
||||
chain: ["minify", "base_64"]
|
||||
|
||||
# experimental (broken - do not use):
|
||||
scramble_variables: false
|
||||
scramble_cookies: false
|
||||
scramble:
|
||||
beef: "beef"
|
||||
Beef: "Beef"
|
||||
evercookie: "evercookie"
|
||||
BeEF: "BeEF"
|
||||
chain: ["scramble", "minify"]
|
||||
# other available obfuscation techniques: ["minify", "base_64", "whitespace"]
|
||||
|
||||
@@ -6,55 +6,86 @@
|
||||
module BeEF
|
||||
module Extension
|
||||
module Evasion
|
||||
class Evasion
|
||||
include Singleton
|
||||
@@config = BeEF::Core::Configuration.instance
|
||||
@@techniques = @@config.get('beef.extension.evasion.chain')
|
||||
class Evasion
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@@config = BeEF::Core::Configuration.instance
|
||||
@@enabled = @@config.get('beef.extension.evasion.enable')
|
||||
|
||||
def initialize
|
||||
return unless @@enabled
|
||||
@techniques ||= load_techniques
|
||||
|
||||
if @techniques.empty?
|
||||
print_error '[Evasion] Initialization failed. No obfuscation techniques specified.'
|
||||
@@config.set('beef.extension.evasion.enable', false)
|
||||
return
|
||||
end
|
||||
|
||||
# Obfuscate the input JS applying the chain of techniques defined in the main config file.
|
||||
def obfuscate(input)
|
||||
@input = apply_chain(input, @@techniques)
|
||||
end
|
||||
|
||||
def add_bootstrapper
|
||||
@bootstrap = ''
|
||||
# add stuff at the end, only once (when serving the initial init javascript)
|
||||
@@techniques.each do |technique|
|
||||
#1. get the ruby module inside the obfuscation directory: the file name will be the same of the string used in "chain"
|
||||
#2. call the "execute" method of the ruby module, passing the input
|
||||
#3. update the input in order that next technique will work on the pre-processed input.
|
||||
if File.exists?("#{$root_dir}/extensions/evasion/obfuscation/#{technique}.rb")
|
||||
klass = BeEF::Extension::Evasion.const_get(technique.capitalize).instance
|
||||
is_bootstrap_needed = klass.need_bootstrap
|
||||
if is_bootstrap_needed
|
||||
print_debug "[OBFUSCATION] Adding bootstrapper for technique [#{technique}]"
|
||||
@bootstrap += klass.get_bootstrap
|
||||
end
|
||||
end
|
||||
@bootstrap
|
||||
end
|
||||
@bootstrap
|
||||
end
|
||||
|
||||
def apply_chain(input, techniques)
|
||||
@output = input
|
||||
techniques.each do |technique|
|
||||
#1. get the ruby module inside the obfuscation directory: the file name will be the same of the string used in "chain"
|
||||
#2. call the "execute" method of the ruby module, passing the input
|
||||
#3. update the input in order that next technique will work on the pre-processed input.
|
||||
if File.exists?("#{$root_dir}/extensions/evasion/obfuscation/#{technique}.rb")
|
||||
print_debug "[OBFUSCATION] Applying technique [#{technique}]"
|
||||
klass = BeEF::Extension::Evasion.const_get(technique.capitalize).instance
|
||||
@output = klass.execute(@output, @@config)
|
||||
end
|
||||
@output
|
||||
end
|
||||
@output
|
||||
end
|
||||
print_debug "[Evasion] Loaded obfuscation chain: #{@techniques.join(', ')}"
|
||||
end
|
||||
|
||||
# load obfuscation technique chain
|
||||
def load_techniques
|
||||
techniques = @@config.get('beef.extension.evasion.chain') || []
|
||||
return [] if techniques.empty?
|
||||
|
||||
chain = []
|
||||
techniques.each do |technique|
|
||||
unless File.exist?("#{$root_dir}/extensions/evasion/obfuscation/#{technique}.rb")
|
||||
print_error "[Evasion] Failed to load obfuscation technique '#{technique}' - file does not exist"
|
||||
next
|
||||
end
|
||||
chain << technique
|
||||
end
|
||||
|
||||
chain
|
||||
rescue => e
|
||||
print_error "[Evasion] Failed to load obfuscation technique chain: #{e.message}"
|
||||
[]
|
||||
end
|
||||
|
||||
# Obfuscate the input JS applying the chain of techniques defined in the main config file.
|
||||
def obfuscate(input)
|
||||
@input = apply_chain(input)
|
||||
end
|
||||
|
||||
def add_bootstrapper
|
||||
bootstrap = ''
|
||||
# add stuff at the end, only once (when serving the initial init javascript)
|
||||
@techniques.each do |technique|
|
||||
# Call the "execute" method of the technique module, passing the input and update
|
||||
# the input in preperation for the next technique in the chain
|
||||
klass = BeEF::Extension::Evasion.const_get(technique.capitalize).instance
|
||||
if klass.need_bootstrap?
|
||||
print_debug "[Evasion] Adding bootstrapper for technique: #{technique}"
|
||||
bootstrap << klass.get_bootstrap
|
||||
end
|
||||
end
|
||||
|
||||
bootstrap
|
||||
rescue => e
|
||||
print_error "[Evasion] Failed to bootstrap obfuscation technique: #{e.message}"
|
||||
puts e.backtrace
|
||||
end
|
||||
|
||||
def apply_chain(input)
|
||||
output = input
|
||||
@techniques.each do |technique|
|
||||
# Call the "execute" method of the technique module, passing the input and update
|
||||
# the input in preperation for the next technique in the chain
|
||||
print_debug "[Evasion] Applying technique: #{technique}"
|
||||
klass = BeEF::Extension::Evasion.const_get(technique.capitalize).instance
|
||||
output = klass.execute(output, @@config)
|
||||
end
|
||||
|
||||
print_debug "[Evasion] Obfuscation completed (#{output.length} bytes)"
|
||||
output
|
||||
rescue => e
|
||||
print_error "[Evasion] Failed to apply obfuscation technique: #{e.message}"
|
||||
puts e.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,8 +16,7 @@ end
|
||||
end
|
||||
|
||||
require 'extensions/evasion/evasion'
|
||||
require 'extensions/evasion/helper'
|
||||
require 'extensions/evasion/obfuscation/scramble'
|
||||
#require 'extensions/evasion/obfuscation/scramble'
|
||||
require 'extensions/evasion/obfuscation/minify'
|
||||
require 'extensions/evasion/obfuscation/base_64'
|
||||
require 'extensions/evasion/obfuscation/whitespace'
|
||||
|
||||
@@ -9,21 +9,22 @@ module BeEF
|
||||
class Base_64
|
||||
include Singleton
|
||||
|
||||
def need_bootstrap
|
||||
def need_bootstrap?
|
||||
true
|
||||
end
|
||||
|
||||
def get_bootstrap
|
||||
# the decode function is obfuscated, and it's called "dec" (see below in "execute", where it is used)
|
||||
decode_function = 'var _0x33db=["\x61\x74\x6F\x62","\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x6C\x65\x6E\x67\x74\x68","\x6A\x6F\x69\x6E"];function dec(_0x487fx2){if(window[_0x33db[0]]){return atob(_0x487fx2);} ;var _0x487fx3=_0x33db[1];var _0x487fx4,_0x487fx5,_0x487fx6,_0x487fx7,_0x487fx8,_0x487fx9,_0x487fxa,_0x487fxb,_0x487fxc=0,_0x487fxd=0,dec=_0x33db[2],_0x487fxe=[];if(!_0x487fx2){return _0x487fx2;} ;_0x487fx2+=_0x33db[2];do{_0x487fx7=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fx8=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fx9=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fxa=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fxb=_0x487fx7<<18|_0x487fx8<<12|_0x487fx9<<6|_0x487fxa;_0x487fx4=_0x487fxb>>16&0xff;_0x487fx5=_0x487fxb>>8&0xff;_0x487fx6=_0x487fxb&0xff;if(_0x487fx9==64){_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4);} else {if(_0x487fxa==64){_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4,_0x487fx5);} else {_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4,_0x487fx5,_0x487fx6);} ;} ;} while(_0x487fxc<_0x487fx2[_0x33db[6]]);;dec=_0x487fxe[_0x33db[7]](_0x33db[2]);return dec;};'
|
||||
# the decode function is obfuscated, and it's called "dec"
|
||||
# (see below in "execute", where it is used)
|
||||
'var _0x33db=["\x61\x74\x6F\x62","\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x6C\x65\x6E\x67\x74\x68","\x6A\x6F\x69\x6E"];function dec(_0x487fx2){if(window[_0x33db[0]]){return atob(_0x487fx2);} ;var _0x487fx3=_0x33db[1];var _0x487fx4,_0x487fx5,_0x487fx6,_0x487fx7,_0x487fx8,_0x487fx9,_0x487fxa,_0x487fxb,_0x487fxc=0,_0x487fxd=0,dec=_0x33db[2],_0x487fxe=[];if(!_0x487fx2){return _0x487fx2;} ;_0x487fx2+=_0x33db[2];do{_0x487fx7=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fx8=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fx9=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fxa=_0x487fx3[_0x33db[4]](_0x487fx2[_0x33db[3]](_0x487fxc++));_0x487fxb=_0x487fx7<<18|_0x487fx8<<12|_0x487fx9<<6|_0x487fxa;_0x487fx4=_0x487fxb>>16&0xff;_0x487fx5=_0x487fxb>>8&0xff;_0x487fx6=_0x487fxb&0xff;if(_0x487fx9==64){_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4);} else {if(_0x487fxa==64){_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4,_0x487fx5);} else {_0x487fxe[_0x487fxd++]=String[_0x33db[5]](_0x487fx4,_0x487fx5,_0x487fx6);} ;} ;} while(_0x487fxc<_0x487fx2[_0x33db[6]]);;dec=_0x487fxe[_0x33db[7]](_0x33db[2]);return dec;};'
|
||||
end
|
||||
|
||||
def execute(input, config)
|
||||
encoded = Base64.strict_encode64(input)
|
||||
# basically, use atob if supported otherwise a normal base64 JS implementation (ie.: IE :-)
|
||||
var_name = BeEF::Extension::Evasion::Helper::random_string(3)
|
||||
var_name = BeEF::Core::Crypto::random_alphanum_string(3)
|
||||
input = "var #{var_name}=\"#{encoded}\";[].constructor.constructor(dec(#{var_name}))();"
|
||||
print_debug "[OBFUSCATION - BASE64] Javascript has been base64'ed'"
|
||||
print_debug "[OBFUSCATION - Base64] Javascript has been base64 encoded"
|
||||
input
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,19 +10,26 @@ module BeEF
|
||||
class Minify
|
||||
include Singleton
|
||||
|
||||
def need_bootstrap
|
||||
def need_bootstrap?
|
||||
false
|
||||
end
|
||||
|
||||
def execute(input, config)
|
||||
begin
|
||||
input2 = Uglifier.compile(input)
|
||||
print_debug "[OBFUSCATION - MINIFIER] Javascript has been minified"
|
||||
input2
|
||||
rescue
|
||||
print_error "[OBFUSCATION - MINIFIER FAILED] Javascript couldn't be minified. Returning the input form."
|
||||
input
|
||||
end
|
||||
opts = {
|
||||
:output => {
|
||||
:comments => :none
|
||||
},
|
||||
:compress => {
|
||||
:dead_code => true,
|
||||
:drop_console => (config.get('beef.client_debug') ? false : true)
|
||||
}
|
||||
}
|
||||
output = Uglifier.compile(input, opts)
|
||||
print_debug "[OBFUSCATION - Minifier] JavaScript has been minified"
|
||||
output
|
||||
rescue => e
|
||||
print_error "[OBFUSCATION - Minifier] JavaScript couldn't be minified: #{e.messsage}"
|
||||
input
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ module BeEF
|
||||
class Scramble
|
||||
include Singleton
|
||||
|
||||
def need_bootstrap
|
||||
def need_bootstrap?
|
||||
false
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ module BeEF
|
||||
to_scramble.each do |var, value|
|
||||
if var == value
|
||||
# Variables have not been scrambled yet
|
||||
mod_var = BeEF::Extension::Evasion::Helper::random_string(3)
|
||||
mod_var = BeEF::Core::Crypto::random_alphanum_string(3)
|
||||
@output.gsub!(var,mod_var)
|
||||
config.set("beef.extension.evasion.scramble.#{var}",mod_var)
|
||||
print_debug "[OBFUSCATION - SCRAMBLER] string [#{var}] scrambled -> [#{mod_var}]"
|
||||
@@ -34,7 +34,7 @@ module BeEF
|
||||
|
||||
if config.get('beef.extension.evasion.scramble_cookies')
|
||||
# ideally this should not be static, but it's static in JS code, so fine for nowend
|
||||
mod_cookie = BeEF::Extension::Evasion::Helper::random_string(5)
|
||||
mod_cookie = BeEF::Core::Crypto::random_alphanum_string(5)
|
||||
if config.get('beef.http.hook_session_name') == "BEEFHOOK"
|
||||
@output.gsub!("BEEFHOOK",mod_cookie)
|
||||
config.set('beef.http.hook_session_name',mod_cookie)
|
||||
|
||||
@@ -9,14 +9,15 @@ module BeEF
|
||||
class Whitespace
|
||||
include Singleton
|
||||
|
||||
def need_bootstrap
|
||||
def need_bootstrap?
|
||||
true
|
||||
end
|
||||
|
||||
def get_bootstrap
|
||||
# the decode function is in plain text - called IE-spacer - because trolling is always a good idea
|
||||
decode_function =
|
||||
# the decode function is in plain text - called IE-spacer - because trolling is always a good idea
|
||||
decode_function =
|
||||
"//Dirty IE6 whitespace bug hack
|
||||
if (typeof IE_spacer === 'function') {} else {
|
||||
function IE_spacer(css_space) {
|
||||
var spacer = '';
|
||||
for(y = 0; y < css_space.length/8; y++)
|
||||
@@ -35,15 +36,15 @@ function IE_spacer(css_space) {
|
||||
}
|
||||
spacer += String.fromCharCode(v);
|
||||
}return spacer;
|
||||
}"
|
||||
}}"
|
||||
end
|
||||
|
||||
def execute(input, config)
|
||||
size = input.length
|
||||
encoded = encode(input)
|
||||
var_name = BeEF::Extension::Evasion::Helper::random_string(3)
|
||||
var_name = BeEF::Core::Crypto::random_alphanum_string(3)
|
||||
input = "var #{var_name}=\"#{encoded}\";[].constructor.constructor(IE_spacer(#{var_name}))();"
|
||||
print_debug "[OBFUSCATION - WHITESPACE] #{size}byte of Javascript code has been Whitespaced"
|
||||
print_debug "[OBFUSCATION - WHITESPACE] #{size} bytes of Javascript code has been Whitespaced"
|
||||
input
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user