Class: JSObfu::Scope

Inherits:
Hash
  • Object
show all
Defined in:
lib/jsobfu/scope.rb

Overview

A single Javascript scope, used as a key-value store to maintain uniqueness of members in generated closures. For speed this class is implemented as a subclass of Hash.

Constant Summary collapse

RESERVED_KEYWORDS =

these keywords should never be used as a random var name source: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Reserved_Words

%w(
  break case catch continue debugger default delete do else finally
  for function if in instanceof new return switch this throw try
  typeof var void while with class enum export extends import super
  implements interface let package private protected public static yield
  const let
)
BUILTIN_VARS =

these vars should not be shadowed as they in the exploit code, and generating them would cause problems.

%w(
  String window unescape location chrome document navigator location
  frames ActiveXObject XMLHttpRequest Function eval Object Math CSS
  parent opener event frameElement Error TypeError setTimeout setInterval
  top arguments Array Date
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Scope

Returns a new instance of Scope.

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :parent (Rex::Exploitation::JSObfu::Scope)

    an optional parent scope, sometimes necessary to prevent needless var shadowing

  • :min_len (Integer)

    minimum length of the var names



38
39
40
41
42
43
44
# File 'lib/jsobfu/scope.rb', line 38

def initialize(opts={})
  @parent         = opts[:parent]
  @first_char_set = opts[:first_char_set] || [*'A'..'Z']+[*'a'..'z']+['_', '$']
  @char_set       = opts[:first_char_set] || @first_char_set + [*'0'..'9']
  @min_len        = opts[:min_len] || 1
  @renames        = {}
end

Instance Attribute Details

#parentJSObfu::Scope

Returns parent that spawned this scope.

Returns:



29
30
31
# File 'lib/jsobfu/scope.rb', line 29

def parent
  @parent
end

#renamesHash

Returns mapping old var names to random ones.

Returns:

  • (Hash)

    mapping old var names to random ones



32
33
34
# File 'lib/jsobfu/scope.rb', line 32

def renames
  @renames
end

Instance Method Details

#empty?Boolean

Returns scope has members.

Returns:

  • (Boolean)

    scope has members



98
99
100
# File 'lib/jsobfu/scope.rb', line 98

def empty?
  self.keys.empty? and (parent.nil? or parent.empty?)
end

#has_key?(key) ⇒ Boolean

Check if we’ve used this var before. This will also check any attached parent scopes (and their parents, recursively)

Returns:

  • (Boolean)

    whether var is in scope



117
118
119
# File 'lib/jsobfu/scope.rb', line 117

def has_key?(key)
  super or (parent and parent.has_key?(key))
end

#pop!Object

“Consumes” the parent and replaces self with it



134
135
136
137
138
139
140
141
# File 'lib/jsobfu/scope.rb', line 134

def pop!
  clear
  if @parent
    merge! @parent
    @renames = @parent.renames
    @parent = @parent.parent
  end
end

#push!Object

replaces this Scope in the “parent” chain with a copy, empties current scope, and returns. Essentially an in-place push operation



124
125
126
127
128
129
130
131
# File 'lib/jsobfu/scope.rb', line 124

def push!
  replacement = dup
  replacement.parent = @parent
  replacement.renames = @renames
  @renames = {}
  @parent = replacement
  clear
end

#random_string(len) ⇒ String

Returns a random string that can be used as a var.

Returns:

  • (String)

    a random string that can be used as a var



144
145
146
# File 'lib/jsobfu/scope.rb', line 144

def random_string(len)
  @first_char_set.sample + (len-1).times.map { @char_set.sample }.join
end

#random_var_nameString

Generates a unique, “safe” random variable

Returns:

  • (String)

    a unique random var name that is not a reserved keyword



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/jsobfu/scope.rb', line 48

def random_var_name
  len = @min_len
  loop do
    text = random_string(len)
    unless has_key?(text) or
      RESERVED_KEYWORDS.include?(text) or
      BUILTIN_VARS.include?(text)

      self[text] = nil

      return text
    end
    len += 1
  end
end

#rename_var(var_name, opts = {}) ⇒ String

Re-maps your var_name to a unique, random names in the current scope

Parameters:

  • var_name (String)

    the name you want to replace. This name will be remembered in the #renames hash

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :generate (Boolean)

    if the variable was not explicitly renamed before, in this scope or any parent scope, generate a new random name

Returns:

  • (String)

    the randomly generated replacement name

  • nil if generate=false and var_name was not already replaced



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/jsobfu/scope.rb', line 76

def rename_var(var_name, opts={})
  return var_name if BUILTIN_VARS.include?(var_name)

  generate = opts.fetch(:generate, true)
  unresolved = opts.fetch(:unresolved, [])
  renamed = @renames[var_name]

  if renamed.nil? and parent
    renamed = parent.rename_var(var_name, :generate => false)
  end

  if renamed.nil? and generate
    @renames[var_name] = random_var_name
    renamed = @renames[var_name]
  end

  #puts "Mapped #{var_name} => #{renamed}" if renamed

  renamed
end

#topObject



107
108
109
110
111
# File 'lib/jsobfu/scope.rb', line 107

def top
  p = self
  p = p.parent until p.parent.nil?
  p
end

#top?Boolean

Returns scope has no parent.

Returns:

  • (Boolean)

    scope has no parent



103
104
105
# File 'lib/jsobfu/scope.rb', line 103

def top?
  parent.nil?
end