Class: Rex::RandomIdentifier::Generator

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/random_identifier/generator.rb

Overview

A quick way to produce unique random strings that follow the rules of identifiers, i.e., begin with a letter and contain only alphanumeric characters and underscore.

The advantage of using this class over, say, Text.rand_text_alpha each time you need a new identifier is that it ensures you don’t have collisions.

Examples:

vars = Rex::RandomIdentifier::Generator.new
asp_code = "  Sub \#{vars[:func]}()\n    Dim \#{vars[:fso]}\n    Set \#{vars[:fso]} = CreateObject(\"Scripting.FileSystemObject\")\n    ...\n  End Sub\n  \#{vars[:func]}\n"

Defined Under Namespace

Classes: ExhaustedSpaceError

Constant Summary collapse

DefaultOpts =

Default options

{
  # Arbitrary
  :max_length => 12,
  :min_length => 3,
  # This should be pretty universal for identifier rules
  :char_set => Rex::Text::AlphaNumeric+"_",
  :first_char_set => Rex::Text::LowerAlpha,
  :forbidden => [].freeze,
  :prefix => ''
}
JavaOpts =
DefaultOpts.merge(
  forbidden: (
    DefaultOpts[:forbidden] +
    %w[
      abstract assert boolean break byte case catch char class const
      continue default do double else enum extends false final finally
      float for goto if implements import instanceof int interface long
      native new null package private protected public return short
      static strictfp super switch synchronized this throw throws
      transient true try void volatile while _
    ]
  ).uniq.freeze
)
JSPOpts =
JavaOpts.merge(
  forbidden: (
    JavaOpts[:forbidden] +
    # Reserved Words for Implicit Objects
    # https://docs.oracle.com/cd/E13222_01/wls/docs90/webapp/reference.html#66991
    %w[
      application config out page pageContext request response session var
    ]
  ).uniq.freeze
)
JavaScriptOpts =
DefaultOpts.merge(
  forbidden: (
      # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words
      # https://developer.mozilla.org/en-US/docs/Web/API/Window Instance methods
      %w[
        const continue debugger default delete do else export extends false finally for function if import in
        instanceof new null return super switch this throw true try typeof var void while with let static yield
        await arguments as async eval from get of set enum implements interface package private protected public
        abstract boolean byte char double final float goto int long native short synchronized throws transient
        volatile atob alert blur btoa cancelAnimationFrame cancelIdleCallback clearInterval clearTimeout close confirm
        createImageBitmap dump fetch find focus getComputedStyle getDefaultComputedStyle getScreenDetails getSelection
        matchMedia moveBy moveTo open postMessage print prompt queryLocalFonts queueMicrotask reportError
        requestAnimationFrame requestIdleCallback resizeBy resizeTo scroll scrollBy scrollByLines scrollByPages
        scrollTo setInterval setTimeout showDirectoryPicker showOpenFilePicker showSaveFilePicker sizeToContent
        stop structuredClone updateCommands
    ]
  ).uniq.freeze
)
PythonOpts =
DefaultOpts.merge(
  forbidden: (
    # words generated for Python 3.9+ using the keyword module
    # https://docs.python.org/3/library/keyword.html
    # import keyword; print(' '.join(sorted(word for word in (keyword.kwlist + keyword.softkwlist + dir(__builtins__)) if not word.startswith('_'))))
    %w[
      ArithmeticError AssertionError AttributeError BaseException BaseExceptionGroup BlockingIOError BrokenPipeError
      BufferError BytesWarning ChildProcessError ConnectionAbortedError ConnectionError ConnectionRefusedError
      ConnectionResetError DeprecationWarning EOFError Ellipsis EncodingWarning EnvironmentError Exception
      ExceptionGroup False FileExistsError FileNotFoundError FloatingPointError FutureWarning GeneratorExit IOError
      ImportError ImportWarning IndentationError IndexError InterruptedError IsADirectoryError KeyError
      KeyboardInterrupt LookupError MemoryError ModuleNotFoundError NameError None NotADirectoryError NotImplemented
      NotImplementedError OSError OverflowError PendingDeprecationWarning PermissionError ProcessLookupError
      RecursionError ReferenceError ResourceWarning RuntimeError RuntimeWarning StopAsyncIteration StopIteration
      SyntaxError SyntaxWarning SystemError SystemExit TabError TimeoutError True TypeError UnboundLocalError
      UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning ValueError
      Warning ZeroDivisionError abs aiter all and anext any as ascii assert async await bin bool break breakpoint
      bytearray bytes callable case chr class classmethod compile complex continue copyright credits def del delattr
      dict dir divmod elif else enumerate eval except exec exit filter finally float for format from frozenset getattr
      global globals hasattr hash help hex id if import in input int is isinstance issubclass iter lambda len license
      list locals map match max memoryview min next nonlocal not object oct open or ord pass pow print property quit
      raise range repr return reversed round set setattr slice sorted staticmethod str sum super try tuple type type
      vars while with yield zip
    ] + # plus words specific to Python 2
    %w[
      StandardError basestring cmp coerce execfile exit file intern long print raw_input reduce reload unichr unicode
      xrange
    ]
  ).freeze
)
PHPOpts =
DefaultOpts.merge(
  prefix: '$',
  first_char_set: Rex::Text::Alpha + '_',
  # see: https://www.php.net/manual/en/reserved.php
  # see: https://www.php.net/manual/en/reserved.variables.php
  forbidden: (
    %w[
      $GLOBALS $_SERVER $_GET $_POST $_FILES $_REQUEST $_SESSION $_ENV $_COOKIE
      $HTTP_GET_VARS $HTTP_POST_VARS $HTTP_COOKIE_VARS $HTTP_SERVER_VARS
      $HTTP_ENV_VARS $HTTP_SESSION_VARS $HTTP_POST_FILES $HTTP_RAW_POST_DATA
      $php_errormsg $http_response_header $argc $argv $this
    ]
  )
)
PowershellOpts =
DefaultOpts.merge(
  forbidden: (
    # PowerShell reserved words and language keywords
    # https://docs.microsoft.com/en-us/powershell/scripting/lang-spec/chapter-02
    # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_reserved_words
    %w[
      assembly base begin break catch class command configuration continue data define do dynamicparam else elseif
      end enum exit filter finally for foreach from function hidden if in inlinescript interface module namespace
      parallel param private process public return sequence static switch throw trap try type until using var while
      workflow bool byte char decimal double float int long object sbyte short string uint ulong ushort
    ] +
      # Common .NET type names used in PowerShell
      # https://learn.microsoft.com/en-us/dotnet/api/system#classes
      %w[
      accessviolationexception activator aggregateexception appcontext appdomain appdomainsetup appdomainunloadedexception
      applicationexception applicationid argumentexception argumentnullexception argumentoutofrangeexception
      arithmeticexception array arraytypemismatchexception assemblyloadeventargs attribute attributeusageattribute
      badimageformatexception bitconverter buffer cannotunloadappdomainexception charenumerator clscompliantattribute
      console consolecanceleventargs contextboundobject contextmarshalexception contextstaticattribute convert
      datamisalignedexception dbnull delegate dividebyzeroexception dllnotfoundexception duplicatewaitobjectexception
      entrypointnotfoundexception enum environment eventargs exception executionengineexception fieldaccessexception
      filestyleuriparser flagsattribute formatexception formattablestring ftpstyleuriparser gc genericheruriparser
      gopherstyleuriparser httpstyleuriparser indexoutofrangeexception insufficientexecutionstackexception
      insufficientmemoryexception invalidcastexception invalidoperationexception invalidprogramexception
      invalidtimezoneexception lazy ldapstyleuriparser loaderoptimizationattribute localdatastoreslot
      marshalbyrefobject math mathf memberaccessexception memoryextensions methodaccessexception missingfieldexception
      missingmemberexception missingmethodexception mtathreadattribute multicastdelegate multicastnotsupportedexception
      netpipestyleuriparser nettcpstyleuriparser newsstyleuriparser nonserializedattribute notfinitenumberexception
      notimplementedexception notsupportedexception nullable nullreferenceexception object objectdisposedexception
      obsoleteattribute operatingsystem operationcanceledexception outofmemoryexception overflowexception
      paramarrayattribute platformnotsupportedexception progress random rankexception resolveeventargs
      serializableattribute stackoverflowexception stathreadattribute string stringcomparer stringnormalizationextensions
      systemexception threadstaticattribute timeoutexception timeprovider timezone timezoneinfo
      timezonenotfoundexception tuple tupleextensions type typeaccessexception typeinitializationexception
      typeloadexception typeunloadedexception unauthorizedaccessexception unhandledexceptioneventargs uri uribuilder
      uriformatexception uriparser uritypeconverter valuetype version weakreference
      array datetime hashtable psobject scriptblock timespan void xml
    ] +
      # Common .NET struct types used in PowerShell
      # https://learn.microsoft.com/en-us/dotnet/api/system#structs
      %w[
      argiterator boolean byte char consolekey consolekeyinfo datetime datetimeoffset dayofweek decimal double
      guid int16 int32 int64 intptr memory nullable range rune runtime sbyte single timeonly timespan
      typedreference uint16 uint32 uint64 uintptr valuetuple void arithmeticexception argumentexception
      argumentnullexception argumentoutofrangeexception badimageformatexception cannotunloadappdomainexception
      contextmarshalexception datamisalignedexception dividebyzeroexception dllnotfoundexception
      duplicatewaitobjectexception entrypointnotfoundexception executionengineexception fieldaccessexception
      formatexception indexoutofrangeexception insufficientexecutionstackexception insufficientmemoryexception
      invalidcastexception invalidoperationexception invalidprogramexception invalidtimezoneexception
      memberaccessexception methodaccessexception missingfieldexception missingmemberexception
      missingmethodexception multicastnotsupportedexception notfinitenumberexception notimplementedexception
      notsupportedexception nullreferenceexception objectdisposedexception operationcanceledexception
      outofmemoryexception overflowexception platformnotsupportedexception rankexception stackoverflowexception
      systemexception timeoutexception typeaccessexception typeinitializationexception typeloadexception
      typeunloadedexception unauthorizedaccessexception
    ] +
      # Common .NET interface types used in PowerShell
      # https://learn.microsoft.com/en-us/dotnet/api/system#interfaces
      %w[
      iasyncresult icloneable icomparable icomparer iconvertible icustomformatter idisposable iequalitycomparer
      iformattable iformatprovider iserviceprovider ienumerable ienumerator icollection ilist idictionary
      ireadonlycollection ireadonlylist ireadonlydictionary iset iproducerconsumercollection
      iconcurrentcollection inotifypropertychanged inotifycollectionchanged iobserver iobservable
      ituple istructuralcomparable istructuralequatable ispanformattable ibinaryinteger ibinaryfloatingpoint
      ibitwiseoperators icomparisonoperators iequalityoperators iincrementoperators iminmaxvalue
      inumberbase iadditionoperators isubtractionoperators imultiplicationoperators idivisionoperators
      imodaoperators ishiftoperators iunaryoperators iparsable ispanparsable iformatable
    ] +
      # Common .NET enum types used in PowerShell
      # https://learn.microsoft.com/en-us/dotnet/api/system#enums
      %w[
      attributetargets base64formattingoptions consolecolor datetimekind dayofweek environmentspecialfolder
      environmentvariabletarget gctype gcgeneration genotificationstatus globalizationmode
      loaderoption midsreamstarttype normalizedform operatingsystem platformid processorarchitecture
      stringcomparison stringsplitopions typecode urikind uricomponents urihostnametype uriformat
      comparetoptions culturedupinfallbackstyles datetimestyles numberstyles runtimeidentifiertype
      securityruleset targetframeworkmoniker timezonefindtype unmanagedfunctionpointercallingconvention
      fileattributes fileaccess filemode fileshare fileaction filetype searchoption directoryoption
      consolecanceltype consolespecialkey consolemodifiers
    ] +
      # Common .NET delegate types used in PowerShell
      # https://learn.microsoft.com/en-us/dotnet/api/system#delegates
      %w[
      action func predicate comparison converter eventhandler asynccallback crossapplicationstringproc
      waitortimercallback timerproc unhandledexceptioneventhandler assemblybindingprovider resolvehandler
      convolecallback appdomainunloadhandler assemblyhandler modulehandler contexthandler
      loadeventhandler unloadeventhandler begininvokedelegate endinvokedelegate asyncresultdelegate
      predefineddelegate delegateserial multidelgate singletondelegate
    ] +
      # PowerShell variable scope keywords
      # https://docs.microsoft.com/en-us/powershell/scripting/lang-spec/chapter-02
      %w[
      global local private script using workflow
    ] +
      # PowerShell automatic variables
      # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables
      %w[
      args consolefilename error executioncontext false home host input lastexitcode matches myinvocation nestedpromptlevel
      null pid profile pscmdlet pscommandpath pshome psscriptroot psversiontable pwd shellid stacktrace true
    ] +
      # PowerShell preference variables
      # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables
      %w[
      confirmpreference debugpreference erroractionpreference errorview formatenumerationlimit informationpreference
      logcommandhealthevent logcommandlifecycleevent lopenginehealthevent logenginelifecycleevent logproviderhealthevent
      logproviderlifecycleevent maximumhistorycount ofs outputencoding progresspreference psdefaultparametervalues
      psemailserver psmoduleautoloadingpreference psnativecommandargumentpassing psnativecommanduseerroractionpreference
      pssessionapplicationname pssessionconfigurationname pssessionoption psstyle transcript verbosepreference
     specsapreference whatifpreference
    ] +
      # Common PowerShell cmdlets that should be avoided as identifiers
      # https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands
      %w[
      add clear copy export find format get import invoke join move new out read remove rename select set sort
      split start stop test where write compare group measure tee convertfrom convertto foreach object sort unique
      first last skip skipuntil takewhile
    ] +
      # Common PowerShell cmdlet aliases and shortnames
      # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_aliases
      %w[
      cat cd chdir clc clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cwmi dbp del diff dir echo epal epcsv
      fc fhx fl foreach ft fw gal gbp gc gci gcm gcs gdr ghy gi gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv
      gwmi h history icm iex ihy ii ipal ipcsv irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr
      ni nmo npssc nsn nv ogv oh popd pushd pwd r rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rv
      rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start sv swmi
      tee type wget where wjb write
    ] +
      # PowerShell operators (word-based)
      # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators
      %w[
      and band bnot bor bxor not or xor eq ne gt ge lt le like notlike match notmatch contains notcontains in notin
      replace split join is isnot as
    ]
  ).uniq.freeze
)
Opts =
{
  default: DefaultOpts,
  java: JavaOpts,
  jsp: JSPOpts,
  javascript: JavaScriptOpts,
  python: PythonOpts,
  powershell: PowershellOpts
}

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Generator

Returns a new instance of Generator.

Parameters:

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

    Options, see DefaultOpts for default values

Options Hash (opts):

  • :language (Symbol)

    See the Opts keys for supported languages

  • :max_length (Fixnum)
  • :min_length (Fixnum)
  • :char_set (String)
  • :forbidden (Array)


281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/rex/random_identifier/generator.rb', line 281

def initialize(opts={})
  # Holds all identifiers.
  @value_by_name = {}
  # Inverse of value_by_name so we can ensure uniqueness without
  # having to search through the whole list of values
  @name_by_value = {}

  language = opts[:language] || :default
  unless Opts.has_key?(language)
    raise ArgumentError, "Language option #{language} is not supported. Expected one of #{Opts.keys}"
  end
  @opts = Opts[language]
  @opts = @opts.merge(opts)
  if @opts[:min_length] < 1 || @opts[:max_length] < 1 || @opts[:max_length] < @opts[:min_length]
    raise ArgumentError, "Invalid length options"
  end

  # This is really just the maximum number of shortest names. This
  # will still be a pretty big number most of the time, so don't
  # bother calculating the real one, which will potentially be
  # expensive, since we're talking about a 36-digit decimal number to
  # represent the total possibilities for the range of 10- to
  # 20-character identifiers.
  #
  # 26 because the first char is lowercase alpha, (min_length - 1) and
  # not just min_length because it includes that first alpha char.
  @max_permutations = 26 * (@opts[:char_set].length ** (@opts[:min_length]-1))
  # The real number of permutations could be calculated thusly:
  #((@opts[:min_length]-1) .. (@opts[:max_length]-1)).reduce(0) { |a, e|
  # a + (26 * @opts[:char_set].length ** e)
  #}
end

Instance Method Details

#forbid_id?(ident = nil) ⇒ Boolean

Check if an identifier is forbidden

Parameters:

  • str (String)

    String for which to check permissions

Returns:

  • (Boolean)

    Is identifier forbidden?



429
430
431
# File 'lib/rex/random_identifier/generator.rb', line 429

def forbid_id?(ident = nil)
  ident.nil? or @opts[:forbidden].any? {|f| f.match(/^#{ident}$/i) }
end

#generate(len = nil) {|String| ... } ⇒ String

Note:

Calling this method with a block that returns only values that this generator already contains will result in an infinite loop.

Create a random string that satisfies most languages’ requirements for identifiers. In particular, with a default configuration, the first character will always be lowercase alpha (unless modified by a block), and the whole thing will contain only a-zA-Z0-9_ characters.

If called with a block, the block will be given the identifier before uniqueness checks. The block’s return value will be the new identifier. Note that the block may be called multiple times if it returns a non-unique value.

Examples:

rig = Rex::RandomIdentifier::Generator.new
const = rig.generate { |val| val.capitalize }
rig.insert(:SOME_CONSTANT, const)
ruby_code = "  \#{rig[:SOME_CONSTANT]} = %q^generated ruby constant^\n  def \#{rig[:my_method]}; ...; end\n"

Parameters:

  • len (Fixnum) (defaults to: nil)

    Avoid setting this unless a specific size is necessary. Default is random within range of min .. max

Yields:

  • (String)

    The identifier before uniqueness checks. This allows you to modify the value and still avoid collisions.

Returns:

  • (String)

    A string that matches [a-z][a-zA-Z0-9_]*

Raises:

  • (ArgumentError)


396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/rex/random_identifier/generator.rb', line 396

def generate(len = nil)
  raise ArgumentError, "len must be positive integer" if len && len < 1
  raise ExhaustedSpaceError if @value_by_name.length >= @max_permutations

  # pick a random length within the limits
  len ||= rand(@opts[:min_length] .. (@opts[:max_length]))

  ident = ''

  # XXX: Infinite loop if block returns only values we've already
  # generated.
  loop do
    ident  = +''
    ident << @opts[:prefix]
    ident << Rex::Text.rand_base(1, "", @opts[:first_char_set])
    ident << Rex::Text.rand_base(len-1, "", @opts[:char_set])
    if block_given?
      ident = yield ident
    end
    # Try to make another one if it collides with a previously
    # generated one.
    break unless @name_by_value.key?(ident) or forbid_id?(ident)
  end

  ident
end

#get(name, len = nil) ⇒ String Also known as: [], init_var

Return a unique random identifier for name, generating a new one if necessary.

Parameters:

  • name (Symbol)

    A descriptive, intention-revealing name for an identifier. This is what you would normally call the variable if you weren't generating it.

Returns:

  • (String)


328
329
330
331
332
333
334
335
# File 'lib/rex/random_identifier/generator.rb', line 328

def get(name, len = nil)
  return @value_by_name[name] if @value_by_name[name]

  @value_by_name[name] = generate(len)
  @name_by_value[@value_by_name[name]] = name

  @value_by_name[name]
end

#store(name, value) ⇒ void

Note:

This should be called before any calls to #get to avoid potential collisions. If you do hit a collision, this method will raise.

This method returns an undefined value.

Add a new identifier. Its name will be checked for uniqueness among previously-generated names.

Parameters:

  • value (String)

    The identifier that will be returned by subsequent calls to #get with the sane name.

  • name (Symbol)

    A descriptive, intention-revealing name for an identifier. This is what you would normally call the variable if you weren't generating it.

Raises:

  • RuntimeError if value already exists



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/rex/random_identifier/generator.rb', line 351

def store(name, value)

  case @name_by_value[value]
  when name
    # we already have this value and it is associated with this name
    # nothing to do here
  when nil
    # don't have this value yet, so go ahead and just insert
    @value_by_name[name] = value
    @name_by_value[value] = name
  else
    # then the caller is trying to insert a duplicate
    raise RuntimeError, "Value is not unique!"
  end

  self
end

#to_hHash

Returns the @value_by_name hash

Returns:

  • (Hash)


317
318
319
# File 'lib/rex/random_identifier/generator.rb', line 317

def to_h
  return @value_by_name
end