librex 0.0.35 → 0.0.36

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -3,7 +3,7 @@
3
3
  A non-official re-packaging of the Rex library as a gem for easy of usage of the Metasploit REX framework in a non Metasploit application. I received permission from HDM to create this package.
4
4
 
5
5
  Currently based on:
6
- SVN Revision: 12804
6
+ SVN Revision: 12951
7
7
 
8
8
  # Credits
9
9
  The Metasploit development team <http://www.metasploit.com>
@@ -1,6 +1,7 @@
1
1
 
2
+ require 'msf/core'
2
3
  require 'rex/text'
3
- require 'rex/exploitation/obfuscatejs'
4
+ require 'rex/exploitation/jsobfu'
4
5
 
5
6
  module Rex
6
7
  module Exploitation
@@ -24,7 +25,7 @@ module Exploitation
24
25
  # ua_ver_gt(a, b): returns true if a > b
25
26
  # ua_ver_eq(a, b): returns true if a == b
26
27
  #
27
- class JavascriptOSDetect < ObfuscateJS
28
+ class JavascriptOSDetect < JSObfu
28
29
 
29
30
  def initialize(custom_js = '', opts = {})
30
31
  clients = ::Msf::HttpClients
@@ -874,26 +875,6 @@ function ua_ver_eq(a, b) {
874
875
  }
875
876
  ENDJS
876
877
  super @js
877
- update_opts(opts) if (opts)
878
- update_opts({'Symbols' => {
879
- 'Variables' => [
880
- 'os_name', 'os_flavor',
881
- 'os_sp', 'os_lang',
882
- 'arch',
883
- 'ua_name',
884
- 'ua_version',
885
- 'found_version',
886
- 'needle',
887
- 'haystack',
888
- ],
889
- 'Methods' => [
890
- 'getVersion',
891
- 'searchVersion'
892
- ]
893
- }
894
- })
895
-
896
- #self.obfuscate
897
878
 
898
879
  return @js
899
880
  end
@@ -0,0 +1,489 @@
1
+ ##
2
+ # $Id: jsobfu.rb 12843 2011-06-03 17:52:34Z egypt $
3
+ ##
4
+
5
+ $:.unshift("lib")
6
+ require 'rex/text'
7
+ require 'rkelly'
8
+
9
+ module Rex
10
+ module Exploitation
11
+
12
+
13
+ #
14
+ # Obfuscate JavaScript by randomizing as much as possible and removing
15
+ # easily-signaturable string constants.
16
+ #
17
+ # Example:
18
+ # js = ::Rex::Exploitation::JSObfu.new %Q|
19
+ # var a = "0\\612\\063\\x34\\x35\\x36\\x37\\x38\\u0039";
20
+ # var b = { foo : "foo", bar : "bar" }
21
+ # alert(a);
22
+ # alert(b.foo);
23
+ # |
24
+ # js.obfuscate
25
+ # puts js
26
+ # Example Output:
27
+ # var VwxvESbCgv = String.fromCharCode(0x30,0x31,062,063,064,53,0x36,067,070,0x39);
28
+ # var ToWZPn = {
29
+ # "\146\157\x6f": (function () { var yDyv="o",YnCL="o",Qcsa="f"; return Qcsa+YnCL+yDyv })(),
30
+ # "\142ar": String.fromCharCode(0142,97,0162)
31
+ # };
32
+ # alert(VwxvESbCgv);
33
+ # alert(ToWZPn.foo);
34
+ #
35
+ # NOTE: Variables MUST be declared with a 'var' statement BEFORE first use (or
36
+ # not at all) for this to generate correct code! If variables are not declared
37
+ # they will not be randomized but the generated code will be correct.
38
+ #
39
+ # Bad Example Javascript:
40
+ # a = "asdf"; // this variable hasn't been declared and will not be randomized
41
+ # var a;
42
+ # alert(a); // real js engines will alert "asdf" here
43
+ # Bad Example Obfuscated:
44
+ # a = (function () { var hpHu="f",oyTm="asd"; return oyTm+hpHu })();
45
+ # var zSrnHpEfJZtg;
46
+ # alert(zSrnHpEfJZtg);
47
+ # Notice that the first usage of +a+ (before it was declared) is not
48
+ # randomized. Thus, the obfuscated version will alert 'undefined' instead of
49
+ # "asdf".
50
+ #
51
+ class JSObfu
52
+
53
+ #
54
+ # Abstract Syntax Tree generated by RKelly::Parser#parse
55
+ #
56
+ attr_reader :ast
57
+
58
+ #
59
+ # Saves +code+ for later obfuscation with #obfuscate
60
+ #
61
+ def initialize(code)
62
+ @code = code
63
+ @funcs = {}
64
+ @vars = {}
65
+ @debug = false
66
+ end
67
+
68
+ #
69
+ # Add +str+ to the un-obfuscated code.
70
+ #
71
+ # Calling this method after #obfuscate is undefined
72
+ #
73
+ def <<(str)
74
+ @code << str
75
+ end
76
+
77
+ #
78
+ # Return the (possibly obfuscated) code as a string.
79
+ #
80
+ # If #obfuscate has not been called before this, returns the parsed,
81
+ # unobfuscated code. This can be useful for example to remove comments and
82
+ # standardize spacing.
83
+ #
84
+ def to_s
85
+ parse if not @ast
86
+ @ast.to_ecma
87
+ end
88
+
89
+ #
90
+ # Return the obfuscated name of a symbol
91
+ #
92
+ # You MUST call #obfuscate before this method!
93
+ #
94
+ def sym(lookup)
95
+ if @vars[lookup]
96
+ ret = @vars[lookup]
97
+ elsif @funcs[lookup]
98
+ ret = @funcs[lookup]
99
+ else
100
+ ret = lookup
101
+ end
102
+ ret
103
+ end
104
+
105
+ #
106
+ # Parse and obfuscate
107
+ #
108
+ def obfuscate
109
+ parse
110
+ obfuscate_r(@ast)
111
+ end
112
+
113
+ protected
114
+
115
+ #
116
+ # Recursive method to obfuscate the given +ast+.
117
+ #
118
+ # +ast+ should be the result of RKelly::Parser#parse
119
+ #
120
+ def obfuscate_r(ast)
121
+ ast.each do |node|
122
+ #if node.respond_to? :value and node.value.kind_of? String and node.value =~ /bodyOnLoad/i
123
+ # $stdout.puts("bodyOnLoad: #{node.class}: #{node.value}")
124
+ #end
125
+
126
+ case node
127
+ when nil
128
+ nil
129
+
130
+ when ::RKelly::Nodes::SourceElementsNode
131
+ # Recurse
132
+ obfuscate_r(node.value)
133
+
134
+ #when ::RKelly::Nodes::ObjectLiteralNode
135
+ # TODO
136
+ #$stdout.puts(node.methods - Object.new.methods)
137
+ #$stdout.puts(node.value.inspect)
138
+
139
+ when ::RKelly::Nodes::PropertyNode
140
+ # Property names must be bare words or string literals NOT
141
+ # expressions! Can't use transform_string() here
142
+ if node.name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/
143
+ n = '"'
144
+ node.name.unpack("C*") { |c|
145
+ case rand(3)
146
+ when 0; n << "\\x%02x"%(c)
147
+ when 1; n << "\\#{c.to_s 8}"
148
+ when 2; n << [c].pack("C")
149
+ end
150
+ }
151
+ n << '"'
152
+ node.name = n
153
+ end
154
+
155
+ # Variables
156
+ when ::RKelly::Nodes::VarDeclNode
157
+ if @vars[node.name].nil?
158
+ #@vars[node.name] = "var_#{Rex::Text.rand_text_alpha(3+rand(12))}_#{node.name}"
159
+ @vars[node.name] = "#{Rex::Text.rand_text_alpha(3+rand(12))}"
160
+ end
161
+ node.name = @vars[node.name]
162
+ when ::RKelly::Nodes::ParameterNode
163
+ if @vars[node.value].nil?
164
+ #@vars[node.value] = "param_#{Rex::Text.rand_text_alpha(3+rand(12))}_#{node.value}"
165
+ @vars[node.value] = "#{Rex::Text.rand_text_alpha(3+rand(12))}"
166
+ end
167
+ node.value = @vars[node.value]
168
+ when ::RKelly::Nodes::ResolveNode
169
+ #$stdout.puts("Resolve bodyOnload: #{@vars[node.value]}") if "bodyOnLoad" == node.value
170
+ node.value = @vars[node.value] if @vars[node.value]
171
+ when ::RKelly::Nodes::DotAccessorNode
172
+ case node.value
173
+ when ::RKelly::Nodes::ResolveNode
174
+ if @vars[node.value.value]
175
+ node.value.value = @vars[node.value.value]
176
+ end
177
+ #else
178
+ # $stderr.puts("Non-resolve node as target of dotaccessor: #{node.value.class}")
179
+ end
180
+
181
+ # Functions
182
+ when ::RKelly::Nodes::FunctionDeclNode
183
+ #$stdout.puts("FunctionDecl: #{node.value}")
184
+ # Functions can also act as objects, so store them in the vars
185
+ # and the functions list so we can replace them in both places
186
+ if @funcs[node.value].nil? and not @funcs.values.include?(node.value)
187
+ #@funcs[node.value] = "func_#{Rex::Text.rand_text_alpha(3+rand(12))}_#{node.value}"
188
+ @funcs[node.value] = "#{Rex::Text.rand_text_alpha(3+rand(12))}"
189
+ if @vars[node.value].nil?
190
+ @vars[node.value] = @funcs[node.value]
191
+ end
192
+ node.value = @funcs[node.value]
193
+ end
194
+ when ::RKelly::Nodes::FunctionCallNode
195
+ # The value of a FunctionCallNode is some sort of accessor node or a ResolveNode
196
+ # so this is basically useless
197
+ #$stdout.puts("Function call: #{node.name} => #{@funcs[node.name]}")
198
+ #node.value = @funcs[node.value] if @funcs[node.value]
199
+
200
+ # Transformers
201
+ when ::RKelly::Nodes::NumberNode
202
+ node.value = transform_number(node.value)
203
+ when ::RKelly::Nodes::StringNode
204
+ node.value = transform_string(node.value)
205
+ else
206
+ #$stderr.puts "#{node.class}: #{node.value}"
207
+ #$stderr.puts "#{node.class}"
208
+ end
209
+
210
+ #unless node.kind_of? ::RKelly::Nodes::SourceElementsNode
211
+ # $stderr.puts "#{node.class}: #{node.value}"
212
+ #end
213
+ end
214
+
215
+ nil
216
+ end
217
+
218
+ #
219
+ # Generate an Abstract Syntax Tree (#ast) for later obfuscation
220
+ #
221
+ def parse
222
+ parser = RKelly::Parser.new
223
+ @ast = parser.parse(@code)
224
+ end
225
+
226
+ #
227
+ # Convert a number to a random base (decimal, octal, or hexedecimal).
228
+ #
229
+ # Given 10 as input, the possible return values are:
230
+ # "10"
231
+ # "0xa"
232
+ # "012"
233
+ #
234
+ def rand_base(num)
235
+ case rand(3)
236
+ when 0; num.to_s
237
+ when 1; "0%o" % num
238
+ when 2; "0x%x" % num
239
+ end
240
+ end
241
+
242
+ #
243
+ # Return a mathematical expression that will evaluate to the given number
244
+ # +num+.
245
+ #
246
+ # +num+ can be a float or an int, but should never be negative.
247
+ #
248
+ def transform_number(num)
249
+ case num
250
+ when Fixnum
251
+ if num == 0
252
+ r = rand(10) + 1
253
+ transformed = "('#{Rex::Text.rand_text_alpha(r)}'.length - #{r})"
254
+ elsif num > 0 and num < 10
255
+ # use a random string.length for small numbers
256
+ transformed = "'#{Rex::Text.rand_text_alpha(num)}'.length"
257
+ else
258
+ transformed = "("
259
+ divisor = rand(num) + 1
260
+ a = num / divisor.to_i
261
+ b = num - (a * divisor)
262
+ # recurse half the time for a
263
+ a = (rand(2) == 0) ? transform_number(a) : rand_base(a)
264
+ # recurse half the time for divisor
265
+ divisor = (rand(2) == 0) ? transform_number(divisor) : rand_base(divisor)
266
+ transformed << "#{a}*#{divisor}"
267
+ transformed << "+#{b}"
268
+ transformed << ")"
269
+ end
270
+ when Float
271
+ transformed = "(#{num - num.floor} + #{rand_base(num.floor)})"
272
+ end
273
+
274
+ #puts("#{num} == #{transformed}")
275
+
276
+ transformed
277
+ end
278
+
279
+ #
280
+ # Convert a javascript string into something that will generate that string.
281
+ #
282
+ # Randomly calls one of the +transform_string_*+ methods
283
+ #
284
+ def transform_string(str)
285
+ quote = str[0,1]
286
+ # pull off the quotes
287
+ str = str[1,str.length - 2]
288
+ return quote*2 if str.length == 0
289
+
290
+ case rand(2)
291
+ when 0
292
+ transformed = transform_string_split_concat(str, quote)
293
+ when 1
294
+ transformed = transform_string_fromCharCode(str)
295
+ #when 2
296
+ # # Currently no-op
297
+ # transformed = transform_string_unescape(str)
298
+ end
299
+
300
+ #$stderr.puts "Obfuscating str: #{str.ljust 30} #{transformed}"
301
+ transformed
302
+ end
303
+
304
+ #
305
+ # Split a javascript string, +str+, without breaking escape sequences.
306
+ #
307
+ # The maximum length of each piece of the string is half the total length
308
+ # of the string, ensuring we (almost) always split into at least two
309
+ # pieces. This won't always be true when given a string like "AA\x41",
310
+ # where escape sequences artificially increase the total length (escape
311
+ # sequences are considered a single character).
312
+ #
313
+ # Returns an array of two-element arrays. The zeroeth element is a
314
+ # randomly generated variable name, the first is a piece of the string
315
+ # contained in +quote+s.
316
+ #
317
+ # See #escape_length
318
+ #
319
+ def safe_split(str, quote)
320
+ parts = []
321
+ max_len = str.length / 2
322
+ while str.length > 0
323
+ len = 0
324
+ loop do
325
+ e_len = escape_length(str[len..-1])
326
+ e_len = 1 if e_len.nil?
327
+ len += e_len
328
+ # if we've reached the end of the string, bail
329
+ break unless str[len]
330
+ break if len > max_len
331
+ # randomize the length of each part
332
+ break if (rand(4) == 0)
333
+ end
334
+
335
+ part = str.slice!(0, len)
336
+
337
+ var = Rex::Text.rand_text_alpha(4)
338
+ parts.push( [ var, "#{quote}#{part}#{quote}" ] )
339
+ end
340
+
341
+ parts
342
+ end
343
+
344
+ #
345
+ # Stolen from obfuscatejs.rb
346
+ #
347
+ # Determines the length of an escape sequence
348
+ #
349
+ def escape_length(str)
350
+ esc_len = nil
351
+ if str[0,1] == "\\"
352
+ case str[1,1]
353
+ when "u"; esc_len = 6 # unicode \u1234
354
+ when "x"; esc_len = 4 # hex, \x41
355
+ when /[0-7]/ # octal, \123, \0
356
+ str[1,3] =~ /([0-7]{1,3})/
357
+ if $1.to_i(8) > 255
358
+ str[1,3] =~ /([0-7]{1,2})/
359
+ end
360
+ esc_len = 1 + $1.length
361
+ else; esc_len = 2 # \" \n, etc.
362
+ end
363
+ end
364
+ esc_len
365
+ end
366
+
367
+ #
368
+ # Split a javascript string, +str+, into multiple randomly-ordered parts
369
+ # and return an anonymous javascript function that joins them in the
370
+ # correct order. This method can be called safely on strings containing
371
+ # escape sequences. See #safe_split.
372
+ #
373
+ def transform_string_split_concat(str, quote)
374
+ parts = safe_split(str, quote)
375
+ func = "(function () { var "
376
+ ret = "; return "
377
+ parts.sort { |a,b| rand }.each do |part|
378
+ func << "#{part[0]}=#{part[1]},"
379
+ end
380
+ func.chop!
381
+
382
+ ret << parts.map{|part| part[0]}.join("+")
383
+ final = func + ret + " })()"
384
+
385
+ final
386
+ end
387
+
388
+
389
+ # TODO
390
+ #def transform_string_unescape(str)
391
+ # str
392
+ #end
393
+
394
+ #
395
+ # Return a call to String.fromCharCode() with each char of the input as arguments
396
+ #
397
+ # Example:
398
+ # input : "A\n"
399
+ # output: String.fromCharCode(0x41, 10)
400
+ #
401
+ def transform_string_fromCharCode(str)
402
+ buf = "String.fromCharCode("
403
+ bytes = str.unpack("C*")
404
+ len = 0
405
+ while str.length > 0
406
+ if str[0,1] == "\\"
407
+ str.slice!(0,1)
408
+ # then this is an escape sequence and we need to deal with all
409
+ # the special cases
410
+ case str[0,1]
411
+ # For chars that contain their non-escaped selves, step past
412
+ # the backslash and let the rand_base() below decide how to
413
+ # represent the character.
414
+ when '"', "'", "\\", " "
415
+ char = str.slice!(0,1).unpack("C").first
416
+ # For symbolic escapes, use the known value
417
+ when "n"; char = 0x0a; str.slice!(0,1)
418
+ when "t"; char = 0x09; str.slice!(0,1)
419
+ # Lastly, if it's a hex, unicode, or octal escape, pull out the
420
+ # real value and use that
421
+ when "x"
422
+ # Strip the x
423
+ str.slice!(0,1)
424
+ char = str.slice!(0,2).to_i 16
425
+ when "u"
426
+ # This can potentially lose information in the case of
427
+ # characters like \u0041, but since regular ascii is stored
428
+ # as unicode internally, String.fromCharCode(0x41) will be
429
+ # represented as 00 41 in memory anyway, so it shouldn't
430
+ # matter.
431
+ str.slice!(0,1)
432
+ char = str.slice!(0,4).to_i 16
433
+ when /[0-7]/
434
+ # Octals are a bit harder since they are variable width and
435
+ # don't necessarily mean what you might think. For example,
436
+ # "\61" == "1" and "\610" == "10". 610 is a valid octal
437
+ # number, but not a valid ascii character. Javascript will
438
+ # interpreter as much as it can as a char and use the rest
439
+ # as a literal. Boo.
440
+ str =~ /([0-7]{1,3})/
441
+ char = $1.to_i 8
442
+ if char > 255
443
+ str =~ /([0-7]{1,2})/
444
+ char = $1.to_i 8
445
+ end
446
+ str.slice!(0,$1.length)
447
+ end
448
+ else
449
+ char = str.slice!(0,1).unpack("C").first
450
+ end
451
+ buf << "#{rand_base(char)},"
452
+ end
453
+ # Strip off the last comma
454
+ buf = buf[0,buf.length-1] + ")"
455
+ transformed = buf
456
+
457
+ transformed
458
+ end
459
+
460
+
461
+ end
462
+ end
463
+ end
464
+
465
+
466
+ =begin
467
+ if __FILE__ == $0
468
+ if ARGV[0]
469
+ code = File.read(ARGV[0])
470
+ else
471
+ #require 'rex/exploitation/javascriptosdetect'
472
+ #code = Rex::Exploitation::JavascriptOSDetect.new.to_s
473
+ code = <<-EOS
474
+ // Should alert "0123456789"
475
+ var a = "0\\612\\063\\x34\\x35\\x36\\x37\\x38\\u0039";
476
+ var a,b=2,c=3;
477
+ alert(a);
478
+ // should alert "asdfjkl;"
479
+ var d = (function() { var foo = "jkl;", blah = "asdf"; return blah + foo; })();
480
+ alert(d);
481
+ EOS
482
+ end
483
+ js = Rex::Exploitation::JSObfu.new(code)
484
+ js.obfuscate
485
+ puts js.to_s
486
+
487
+ end
488
+
489
+ =end