Skip to content

Commit 7b6b94a

Browse files
committed
Land rapid7#3247 - Revert rapid7#3224 jsobfu string size fixes
2 parents 43a4f41 + e09f887 commit 7b6b94a

File tree

4 files changed

+92
-201
lines changed

4 files changed

+92
-201
lines changed

data/js/detect/os.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11

22
// Case matters, see lib/msf/core/constants.rb
33
// All of these should match up with constants in ::Msf::HttpClients
4-
var clients_opera = "Opera";
5-
var clients_ie = "MSIE";
6-
var clients_ff = "Firefox";
7-
var clients_chrome= "Chrome";
8-
var clients_safari= "Safari";
4+
clients_opera = "Opera";
5+
clients_ie = "MSIE";
6+
clients_ff = "Firefox";
7+
clients_chrome= "Chrome";
8+
clients_safari= "Safari";
99

1010
// All of these should match up with constants in ::Msf::OperatingSystems
11-
var oses_linux = "Linux";
12-
var oses_windows = "Microsoft Windows";
13-
var oses_mac_osx = "Mac OS X";
14-
var oses_freebsd = "FreeBSD";
15-
var oses_netbsd = "NetBSD";
16-
var oses_openbsd = "OpenBSD";
11+
oses_linux = "Linux";
12+
oses_windows = "Microsoft Windows";
13+
oses_mac_osx = "Mac OS X";
14+
oses_freebsd = "FreeBSD";
15+
oses_netbsd = "NetBSD";
16+
oses_openbsd = "OpenBSD";
1717

1818
// All of these should match up with the ARCH_* constants
19-
var arch_armle = "armle";
20-
var arch_x86 = "x86";
21-
var arch_x86_64 = "x86_64";
22-
var arch_ppc = "ppc";
19+
arch_armle = "armle";
20+
arch_x86 = "x86";
21+
arch_x86_64 = "x86_64";
22+
arch_ppc = "ppc";
2323

2424
window.os_detect = {};
2525

lib/rex/exploitation/jsobfu.rb

Lines changed: 53 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -48,71 +48,14 @@ module Exploitation
4848
#
4949
class JSObfu
5050

51-
# A single Javascript scope, used as a key-value store
52-
# to maintain uniqueness of members in generated closures.
53-
# For speed this class is implemented as a subclass of Hash.
54-
class Scope < Hash
55-
56-
# these keywords should never be used as a random var name
57-
# source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Reserved_Words
58-
RESERVED_KEYWORDS = %w(
59-
break case catch continue debugger default delete do else finally
60-
for function if in instanceof new return switch this throw try
61-
typeof var void while with class enum export extends import super
62-
implements interface let package private protected public static yield
63-
)
64-
65-
# these vars should not be shadowed as they may be used in other obfuscated code
66-
BUILTIN_VARS = %w(
67-
String window unescape
68-
)
69-
70-
# @param [Rex::Exploitation::JSObfu::Scope] parent an optional parent scope,
71-
# sometimes necessary to prevent needless var shadowing
72-
def initialize(parent=nil)
73-
@parent = parent
74-
@rand_gen = Rex::RandomIdentifierGenerator.new(
75-
:max_length => 15,
76-
:first_char_set => Rex::Text::Alpha+"_$",
77-
:char_set => Rex::Text::AlphaNumeric+"_$",
78-
:min_length => 1
79-
)
80-
end
81-
82-
# @return [String] a unique random var name that is not a reserved keyword
83-
def random_var_name
84-
len = 1
85-
loop do
86-
text = random_string(len)
87-
unless has_key?(text) or
88-
RESERVED_KEYWORDS.include?(text) or
89-
BUILTIN_VARS.include?(text)
90-
91-
self[text] = nil
92-
return text
93-
end
94-
len += 1
95-
end
96-
end
97-
98-
# @return [Boolean] var is in scope
99-
def has_key?(key)
100-
super or (@parent and @parent.has_key?(key))
101-
end
102-
103-
# @return [String] a random string that can be used as a var
104-
def random_string(len)
105-
@rand_gen.generate(len)
106-
end
107-
108-
end
109-
110-
#
111-
# The maximum length of a string that can be passed through
112-
# #transform_string without being chopped up into separate
113-
# expressions and concatenated
114-
#
115-
MAX_STRING_CHUNK = 10000
51+
# these keywords should never be used as a random var name
52+
# source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Reserved_Words
53+
RESERVED_KEYWORDS = %w(
54+
break case catch continue debugger default delete do else finally
55+
for function if in instanceof new return switch this throw try
56+
typeof var void while with class enum export extends import super
57+
implements interface let package private protected public static yield
58+
)
11659

11760
#
11861
# Abstract Syntax Tree generated by RKelly::Parser#parse
@@ -126,8 +69,12 @@ def initialize(code)
12669
@code = code
12770
@funcs = {}
12871
@vars = {}
129-
@scope = Scope.new
13072
@debug = false
73+
@rand_gen = Rex::RandomIdentifierGenerator.new(
74+
:max_length => 15,
75+
:first_char_set => Rex::Text::Alpha+"_$",
76+
:char_set => Rex::Text::AlphaNumeric+"_$"
77+
)
13178
end
13279

13380
#
@@ -175,8 +122,23 @@ def obfuscate
175122
obfuscate_r(@ast)
176123
end
177124

125+
# @return [String] a unique random var name that is not a reserved keyword
126+
def random_var_name
127+
loop do
128+
text = random_string
129+
unless @vars.has_value?(text) or RESERVED_KEYWORDS.include?(text)
130+
return text
131+
end
132+
end
133+
end
134+
178135
protected
179136

137+
# @return [String] a random string
138+
def random_string
139+
@rand_gen.generate
140+
end
141+
180142
#
181143
# Recursive method to obfuscate the given +ast+.
182144
#
@@ -220,12 +182,12 @@ def obfuscate_r(ast)
220182
# Variables
221183
when ::RKelly::Nodes::VarDeclNode
222184
if @vars[node.name].nil?
223-
@vars[node.name] = @scope.random_var_name
185+
@vars[node.name] = random_var_name
224186
end
225187
node.name = @vars[node.name]
226188
when ::RKelly::Nodes::ParameterNode
227189
if @vars[node.value].nil?
228-
@vars[node.value] = @scope.random_var_name
190+
@vars[node.value] = random_var_name
229191
end
230192
node.value = @vars[node.value]
231193
when ::RKelly::Nodes::ResolveNode
@@ -247,7 +209,7 @@ def obfuscate_r(ast)
247209
# Functions can also act as objects, so store them in the vars
248210
# and the functions list so we can replace them in both places
249211
if @funcs[node.value].nil? and not @funcs.values.include?(node.value)
250-
@funcs[node.value] = @scope.random_var_name
212+
@funcs[node.value] = random_var_name
251213
if @vars[node.value].nil?
252214
@vars[node.value] = @funcs[node.value]
253215
end
@@ -349,15 +311,14 @@ def transform_string(str)
349311
str = str[1,str.length - 2]
350312
return quote*2 if str.length == 0
351313

352-
if str.length > MAX_STRING_CHUNK
353-
return safe_split(str, quote).map { |args| transform_string(args[1]) }.join('+')
354-
end
355-
356-
transformed = case rand(2)
314+
case rand(2)
357315
when 0
358-
transform_string_split_concat(str, quote)
316+
transformed = transform_string_split_concat(str, quote)
359317
when 1
360-
transform_string_fromCharCode(str)
318+
transformed = transform_string_fromCharCode(str)
319+
#when 2
320+
# # Currently no-op
321+
# transformed = transform_string_unescape(str)
361322
end
362323

363324
#$stderr.puts "Obfuscating str: #{str.ljust 30} #{transformed}"
@@ -392,12 +353,12 @@ def safe_split(str, quote)
392353
break unless str[len]
393354
break if len > max_len
394355
# randomize the length of each part
395-
break if (rand(max_len) == 0)
356+
break if (rand(4) == 0)
396357
end
397358

398359
part = str.slice!(0, len)
399360

400-
var = @scope.random_var_name
361+
var = Rex::Text.rand_text_alpha(4)
401362
parts.push( [ var, "#{quote}#{part}#{quote}" ] )
402363
end
403364

@@ -448,6 +409,12 @@ def transform_string_split_concat(str, quote)
448409
final
449410
end
450411

412+
413+
# TODO
414+
#def transform_string_unescape(str)
415+
# str
416+
#end
417+
451418
#
452419
# Return a call to String.fromCharCode() with each char of the input as arguments
453420
#
@@ -456,17 +423,9 @@ def transform_string_split_concat(str, quote)
456423
# output: String.fromCharCode(0x41, 10)
457424
#
458425
def transform_string_fromCharCode(str)
459-
"String.fromCharCode(#{string_to_bytes(str)})"
460-
end
461-
462-
#
463-
# Return a comma-separated list of byte values with random encodings (decimal/hex/octal)
464-
#
465-
def string_to_bytes(str)
466-
len = 0
426+
buf = "String.fromCharCode("
467427
bytes = str.unpack("C*")
468-
encoded_bytes = []
469-
428+
len = 0
470429
while str.length > 0
471430
if str[0,1] == "\\"
472431
str.slice!(0,1)
@@ -513,12 +472,16 @@ def string_to_bytes(str)
513472
else
514473
char = str.slice!(0,1).unpack("C").first
515474
end
516-
encoded_bytes << rand_base(char)
475+
buf << "#{rand_base(char)},"
517476
end
477+
# Strip off the last comma
478+
buf = buf[0,buf.length-1] + ")"
479+
transformed = buf
518480

519-
encoded_bytes.join(',')
481+
transformed
520482
end
521483

484+
522485
end
523486
end
524487
end

spec/lib/rex/exploitation/jsobfu_scope_spec.rb

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)