librex 0.0.35 → 0.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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