2007-04-03 07:35:54 +00:00
|
|
|
module Rex
|
|
|
|
module Exploitation
|
|
|
|
|
|
|
|
#
|
|
|
|
# Obfuscates javascript in various ways
|
|
|
|
#
|
|
|
|
class ObfuscateJS
|
2008-07-22 07:28:05 +00:00
|
|
|
attr_reader :opts
|
2007-04-03 07:35:54 +00:00
|
|
|
|
|
|
|
#
|
2008-07-14 18:57:13 +00:00
|
|
|
# Obfuscates a javascript string.
|
|
|
|
#
|
|
|
|
# Options are 'Symbols', described below, and 'Strings', a boolean
|
|
|
|
# which specifies whether strings within the javascript should be
|
|
|
|
# mucked with (defaults to false).
|
|
|
|
#
|
|
|
|
# The 'Symbols' argument should have the following format:
|
2007-04-03 07:35:54 +00:00
|
|
|
#
|
|
|
|
# {
|
2007-04-03 07:56:57 +00:00
|
|
|
# 'Variables' => [ 'var1', ... ],
|
2007-04-03 07:35:54 +00:00
|
|
|
# 'Methods' => [ 'method1', ... ],
|
2008-07-22 07:28:05 +00:00
|
|
|
# 'Namespaces' => [ 'n', ... ],
|
|
|
|
# 'Classes' => [ { 'Namespace' => 'n', 'Class' => 'y'}, ... ]
|
2007-04-03 07:35:54 +00:00
|
|
|
# }
|
|
|
|
#
|
|
|
|
# Make sure you order your methods, classes, and namespaces by most
|
|
|
|
# specific to least specific to prevent partial substitution. For
|
|
|
|
# instance, if you have two methods (joe and joeBob), you should place
|
|
|
|
# joeBob before joe because it is more specific and will be globally
|
|
|
|
# replaced before joe is replaced.
|
|
|
|
#
|
2008-07-14 18:57:13 +00:00
|
|
|
# A simple example follows:
|
|
|
|
#
|
|
|
|
# <code>
|
|
|
|
# js = ObfuscateJS.new <<ENDJS
|
|
|
|
# function say_hi() {
|
|
|
|
# var foo = "Hello, world";
|
|
|
|
# document.writeln(foo);
|
|
|
|
# }
|
|
|
|
# ENDJS
|
|
|
|
# js.obfuscate(
|
|
|
|
# 'Symbols' => {
|
|
|
|
# 'Variables' => [ 'foo' ],
|
|
|
|
# 'Methods' => [ 'say_hi' ]
|
|
|
|
# }
|
|
|
|
# 'Strings' => true
|
|
|
|
# )
|
|
|
|
# </code>
|
|
|
|
#
|
|
|
|
# which should generate something like the following:
|
|
|
|
#
|
|
|
|
# <code>
|
|
|
|
# function oJaDYRzFOyJVQCOHk() { var cLprVG = "\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64"; document.writeln(cLprVG); }
|
|
|
|
# </code>
|
|
|
|
#
|
2008-07-22 07:28:05 +00:00
|
|
|
# String obfuscation tries to deal with escaped quotes within strings but
|
|
|
|
# won't catch things like
|
|
|
|
# "\\"
|
|
|
|
# so be careful.
|
|
|
|
#
|
2007-04-03 07:35:54 +00:00
|
|
|
def self.obfuscate(js, opts = {})
|
|
|
|
ObfuscateJS.new(js).obfuscate(opts)
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Initialize an instance of the obfuscator
|
|
|
|
#
|
2009-07-22 20:14:35 +00:00
|
|
|
def initialize(js = "", opts = {})
|
2007-04-03 07:35:54 +00:00
|
|
|
@js = js
|
|
|
|
@dynsym = {}
|
2008-07-22 07:28:05 +00:00
|
|
|
@opts = {
|
|
|
|
'Symbols' => {
|
|
|
|
'Variables'=>[],
|
|
|
|
'Methods'=>[],
|
|
|
|
'Namespaces'=>[],
|
|
|
|
'Classes'=>[]
|
|
|
|
},
|
|
|
|
'Strings'=>false
|
|
|
|
}
|
|
|
|
@done = false
|
|
|
|
update_opts(opts) if (opts.length > 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_opts(opts)
|
|
|
|
if (opts.nil? or opts.length < 1)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if (@opts['Symbols'] && opts['Symbols'])
|
|
|
|
['Variables', 'Methods', 'Namespaces', 'Classes'].each { |k|
|
|
|
|
if (@opts['Symbols'][k] && opts['Symbols'][k])
|
|
|
|
opts['Symbols'][k].each { |s|
|
|
|
|
if (not @opts['Symbols'][k].include? s)
|
|
|
|
@opts['Symbols'][k].push(s)
|
|
|
|
end
|
|
|
|
}
|
|
|
|
elsif (opts['Symbols'][k])
|
|
|
|
@opts['Symbols'][k] = opts['Symbols'][k]
|
|
|
|
end
|
|
|
|
}
|
|
|
|
elsif opts['Symbols']
|
|
|
|
@opts['Symbols'] = opts['Symbols']
|
|
|
|
end
|
2008-08-15 20:11:42 +00:00
|
|
|
@opts['Strings'] ||= opts['Strings']
|
2007-04-03 07:35:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Returns the dynamic symbol associated with the supplied symbol name
|
|
|
|
#
|
2008-07-22 07:28:05 +00:00
|
|
|
# If obfuscation has not yet been performed (i.e. obfuscate() has not been
|
|
|
|
# called), then this method simply returns its argument
|
|
|
|
#
|
2007-04-03 07:35:54 +00:00
|
|
|
def sym(name)
|
2008-07-01 01:44:56 +00:00
|
|
|
@dynsym[name] || name
|
2007-04-03 07:35:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Obfuscates the javascript string passed to the constructor
|
|
|
|
#
|
|
|
|
def obfuscate(opts = {})
|
2008-07-22 07:28:05 +00:00
|
|
|
return @js if (@done)
|
|
|
|
@done = true
|
|
|
|
|
2007-04-03 07:35:54 +00:00
|
|
|
# Remove our comments
|
|
|
|
remove_comments
|
2008-07-22 07:28:05 +00:00
|
|
|
|
|
|
|
update_opts(opts)
|
2007-04-03 07:35:54 +00:00
|
|
|
|
2008-07-22 07:28:05 +00:00
|
|
|
#$stderr.puts @opts.inspect
|
|
|
|
if (@opts['Strings'])
|
2008-07-14 18:57:13 +00:00
|
|
|
obfuscate_strings()
|
|
|
|
|
2008-07-22 07:28:05 +00:00
|
|
|
# Full space randomization does not work for javascript -- despite
|
|
|
|
# claims that space is irrelavent, newlines break things. Instead,
|
|
|
|
# use only space (0x20) and tab (0x09).
|
2008-07-14 18:57:13 +00:00
|
|
|
|
2009-07-22 20:14:35 +00:00
|
|
|
@js.gsub!(/[\x09\x20]+/) { |s|
|
2008-07-14 18:57:13 +00:00
|
|
|
len = rand(50)+2
|
|
|
|
set = "\x09\x20"
|
|
|
|
buf = ''
|
|
|
|
while (buf.length < len)
|
|
|
|
buf << set[rand(set.length)].chr
|
|
|
|
end
|
|
|
|
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
end
|
2007-04-03 07:35:54 +00:00
|
|
|
# Globally replace symbols
|
2008-07-22 07:28:05 +00:00
|
|
|
replace_symbols(@opts['Symbols']) if @opts['Symbols']
|
2007-04-03 07:35:54 +00:00
|
|
|
|
2008-07-22 07:28:05 +00:00
|
|
|
return @js
|
2007-04-03 07:35:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Returns the replaced javascript string
|
|
|
|
#
|
|
|
|
def to_s
|
|
|
|
@js
|
|
|
|
end
|
2008-07-14 18:57:13 +00:00
|
|
|
alias :to_str :to_s
|
2007-04-03 07:35:54 +00:00
|
|
|
|
2009-07-22 20:14:35 +00:00
|
|
|
def <<(str)
|
|
|
|
@js << str
|
|
|
|
end
|
|
|
|
def +(str)
|
|
|
|
@js + str
|
|
|
|
end
|
|
|
|
|
2007-04-03 07:35:54 +00:00
|
|
|
protected
|
2008-07-22 07:28:05 +00:00
|
|
|
attr_accessor :done
|
2007-04-03 07:35:54 +00:00
|
|
|
|
2008-07-14 18:57:13 +00:00
|
|
|
#
|
|
|
|
# Get rid of both single-line C++ style comments and multiline C style comments.
|
|
|
|
#
|
|
|
|
# Note: embedded comments (e.g.: "/*/**/*/") will break this,
|
|
|
|
# but they also break real javascript engines so I don't care.
|
|
|
|
#
|
2007-04-03 07:35:54 +00:00
|
|
|
def remove_comments
|
2008-07-14 18:57:13 +00:00
|
|
|
@js.gsub!(%r{//.*$}, '')
|
|
|
|
@js.gsub!(%r{/\*.*?\*/}m, '')
|
2007-04-03 07:35:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Replace method, class, and namespace symbols found in the javascript
|
|
|
|
# string
|
|
|
|
def replace_symbols(symbols)
|
|
|
|
taken = { }
|
|
|
|
|
|
|
|
# Generate random symbol names
|
|
|
|
[ 'Variables', 'Methods', 'Classes', 'Namespaces' ].each { |symtype|
|
|
|
|
next if symbols[symtype].nil?
|
|
|
|
symbols[symtype].each { |sym|
|
|
|
|
dyn = Rex::Text.rand_text_alpha(rand(32)+1) until dyn and not taken.key?(dyn)
|
|
|
|
|
|
|
|
taken[dyn] = true
|
|
|
|
|
|
|
|
if symtype == 'Classes'
|
|
|
|
full_sym = sym['Namespace'] + "." + sym['Class']
|
|
|
|
@dynsym[full_sym] = dyn
|
|
|
|
|
|
|
|
@js.gsub!(/#{full_sym}/) { |m|
|
|
|
|
sym['Namespace'] + "." + dyn
|
|
|
|
}
|
|
|
|
else
|
|
|
|
@dynsym[sym] = dyn
|
|
|
|
|
|
|
|
@js.gsub!(/#{sym}/, dyn)
|
|
|
|
end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2008-07-14 18:57:13 +00:00
|
|
|
#
|
|
|
|
# Change each string into some javascript that will generate that string
|
|
|
|
#
|
2008-07-22 07:28:05 +00:00
|
|
|
# There are a couple of caveats to using string obfuscation:
|
|
|
|
# * it tries to deal with escaped quotes within strings but won't catch
|
|
|
|
# things like: "\\"
|
|
|
|
# * multiple calls to this method are very likely to result in incorrect
|
|
|
|
# code. DON'T CALL THIS METHOD MORE THAN ONCE
|
2008-07-14 18:57:13 +00:00
|
|
|
# so be careful.
|
|
|
|
#
|
|
|
|
def obfuscate_strings()
|
|
|
|
@js.gsub!(/".*?[^\\]"|'.*?[^\\]'/) { |str|
|
|
|
|
str = str[1, str.length-2]
|
|
|
|
case (rand(3))
|
|
|
|
when 0
|
|
|
|
buf = '"' + Rex::Text.to_hex(str) + '"'
|
|
|
|
when 1
|
2009-10-20 17:24:33 +00:00
|
|
|
buf = "unescape(\"" + Rex::Text.to_hex(str, "%") + "\")"
|
2008-07-14 18:57:13 +00:00
|
|
|
when 2
|
|
|
|
buf = "String.fromCharCode("
|
|
|
|
str.each_byte { |c|
|
|
|
|
if (0 == rand(2))
|
|
|
|
buf << " %i,"%(c)
|
|
|
|
else
|
|
|
|
buf << " 0x%0.2x,"%(c)
|
|
|
|
end
|
|
|
|
}
|
|
|
|
buf = buf[0,buf.length-1] + " )"
|
|
|
|
end
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
@js
|
|
|
|
end
|
2008-05-17 06:17:41 +00:00
|
|
|
|
2007-04-03 07:35:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2009-07-22 20:14:35 +00:00
|
|
|
end
|