@@ -48,68 +48,14 @@ module Exploitation
48
48
#
49
49
class JSObfu
50
50
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
- )
79
- end
80
-
81
- # @return [String] a unique random var name that is not a reserved keyword
82
- def random_var_name
83
- loop do
84
- text = random_string
85
- unless has_key? ( text ) or
86
- RESERVED_KEYWORDS . include? ( text ) or
87
- BUILTIN_VARS . include? ( text )
88
-
89
- self [ text ] = nil
90
- return text
91
- end
92
- end
93
- end
94
-
95
- # @return [Boolean] var is in scope
96
- def has_key? ( key )
97
- super or ( @parent and @parent . has_key? ( key ) )
98
- end
99
-
100
- # @return [String] a random string
101
- def random_string
102
- @rand_gen . generate
103
- end
104
-
105
- end
106
-
107
- #
108
- # The maximum length of a string that will be passed through
109
- # #transform_string without being chopped up into separate
110
- # expressions and concatenated
111
- #
112
- 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
+ )
113
59
114
60
#
115
61
# Abstract Syntax Tree generated by RKelly::Parser#parse
@@ -123,8 +69,12 @@ def initialize(code)
123
69
@code = code
124
70
@funcs = { }
125
71
@vars = { }
126
- @scope = Scope . new
127
72
@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
+ )
128
78
end
129
79
130
80
#
@@ -172,8 +122,23 @@ def obfuscate
172
122
obfuscate_r ( @ast )
173
123
end
174
124
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
+
175
135
protected
176
136
137
+ # @return [String] a random string
138
+ def random_string
139
+ @rand_gen . generate
140
+ end
141
+
177
142
#
178
143
# Recursive method to obfuscate the given +ast+.
179
144
#
@@ -217,12 +182,12 @@ def obfuscate_r(ast)
217
182
# Variables
218
183
when ::RKelly ::Nodes ::VarDeclNode
219
184
if @vars [ node . name ] . nil?
220
- @vars [ node . name ] = @scope . random_var_name
185
+ @vars [ node . name ] = random_var_name
221
186
end
222
187
node . name = @vars [ node . name ]
223
188
when ::RKelly ::Nodes ::ParameterNode
224
189
if @vars [ node . value ] . nil?
225
- @vars [ node . value ] = @scope . random_var_name
190
+ @vars [ node . value ] = random_var_name
226
191
end
227
192
node . value = @vars [ node . value ]
228
193
when ::RKelly ::Nodes ::ResolveNode
@@ -244,7 +209,7 @@ def obfuscate_r(ast)
244
209
# Functions can also act as objects, so store them in the vars
245
210
# and the functions list so we can replace them in both places
246
211
if @funcs [ node . value ] . nil? and not @funcs . values . include? ( node . value )
247
- @funcs [ node . value ] = @scope . random_var_name
212
+ @funcs [ node . value ] = random_var_name
248
213
if @vars [ node . value ] . nil?
249
214
@vars [ node . value ] = @funcs [ node . value ]
250
215
end
@@ -346,16 +311,14 @@ def transform_string(str)
346
311
str = str [ 1 , str . length - 2 ]
347
312
return quote *2 if str . length == 0
348
313
349
- if str . length > MAX_STRING_CHUNK
350
- return safe_split ( str , quote ) . map { |args | transform_string ( args [ 1 ] ) } . join ( '+' )
351
- end
352
-
353
- transformed = nil
354
314
case rand ( 2 )
355
315
when 0
356
316
transformed = transform_string_split_concat ( str , quote )
357
317
when 1
358
318
transformed = transform_string_fromCharCode ( str )
319
+ #when 2
320
+ # # Currently no-op
321
+ # transformed = transform_string_unescape(str)
359
322
end
360
323
361
324
#$stderr.puts "Obfuscating str: #{str.ljust 30} #{transformed}"
@@ -390,12 +353,12 @@ def safe_split(str, quote)
390
353
break unless str [ len ]
391
354
break if len > max_len
392
355
# randomize the length of each part
393
- break if ( rand ( max_len ) == 0 )
356
+ break if ( rand ( 4 ) == 0 )
394
357
end
395
358
396
359
part = str . slice! ( 0 , len )
397
360
398
- var = @scope . random_var_name
361
+ var = Rex :: Text . rand_text_alpha ( 4 )
399
362
parts . push ( [ var , "#{ quote } #{ part } #{ quote } " ] )
400
363
end
401
364
@@ -446,6 +409,12 @@ def transform_string_split_concat(str, quote)
446
409
final
447
410
end
448
411
412
+
413
+ # TODO
414
+ #def transform_string_unescape(str)
415
+ # str
416
+ #end
417
+
449
418
#
450
419
# Return a call to String.fromCharCode() with each char of the input as arguments
451
420
#
@@ -454,17 +423,9 @@ def transform_string_split_concat(str, quote)
454
423
# output: String.fromCharCode(0x41, 10)
455
424
#
456
425
def transform_string_fromCharCode ( str )
457
- "String.fromCharCode(#{ string_to_bytes ( str ) } )"
458
- end
459
-
460
- #
461
- # Return a comma-separated list of byte values with random encodings (decimal/hex/octal)
462
- #
463
- def string_to_bytes ( str )
464
- len = 0
426
+ buf = "String.fromCharCode("
465
427
bytes = str . unpack ( "C*" )
466
- encoded_bytes = [ ]
467
-
428
+ len = 0
468
429
while str . length > 0
469
430
if str [ 0 , 1 ] == "\\ "
470
431
str . slice! ( 0 , 1 )
@@ -511,12 +472,16 @@ def string_to_bytes(str)
511
472
else
512
473
char = str . slice! ( 0 , 1 ) . unpack ( "C" ) . first
513
474
end
514
- encoded_bytes << rand_base ( char )
475
+ buf << " #{ rand_base ( char ) } ,"
515
476
end
477
+ # Strip off the last comma
478
+ buf = buf [ 0 , buf . length -1 ] + ")"
479
+ transformed = buf
516
480
517
- encoded_bytes . join ( ',' )
481
+ transformed
518
482
end
519
483
484
+
520
485
end
521
486
end
522
487
end
0 commit comments