Class: Msf::Payload::UUID

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/core/payload/uuid.rb

Overview

This class provides methods for calculating, extracting, and parsing unique ID values used by payloads.

Defined Under Namespace

Modules: Options

Constant Summary collapse

Architectures =

Constants

{
   0 => nil,
   1 => ARCH_X86,
   2 => ARCH_X64, # removed ARCH_X86_64, now consistent across the board
   3 => ARCH_X64,
   4 => ARCH_MIPS,
   5 => ARCH_MIPSLE,
   6 => ARCH_MIPSBE,
   7 => ARCH_PPC,
   8 => ARCH_PPC64,
   9 => ARCH_CBEA,
  10 => ARCH_CBEA64,
  11 => ARCH_SPARC,
  12 => ARCH_ARMLE,
  13 => ARCH_ARMBE,
  14 => ARCH_CMD,
  15 => ARCH_PHP,
  16 => ARCH_TTY,
  17 => ARCH_JAVA,
  18 => ARCH_RUBY,
  19 => ARCH_DALVIK,
  20 => ARCH_PYTHON,
  21 => ARCH_NODEJS,
  22 => ARCH_FIREFOX,
  23 => ARCH_ZARCH,
  24 => ARCH_AARCH64,
  25 => ARCH_MIPS64,
  26 => ARCH_PPC64LE,
  27 => ARCH_R,
  28 => ARCH_PPCE500V2
}
Platforms =
{
   0 => nil,
   1 => 'windows',
   2 => 'netware',
   3 => 'android',
   4 => 'java',
   5 => 'ruby',
   6 => 'linux',
   7 => 'cisco',
   8 => 'solaris',
   9 => 'osx',
  10 => 'bsd',
  11 => 'openbsd',
  12 => 'bsdi',
  13 => 'netbsd',
  14 => 'freebsd',
  15 => 'aix',
  16 => 'hpux',
  17 => 'irix',
  18 => 'unix',
  19 => 'php',
  20 => 'js',
  21 => 'python',
  22 => 'nodejs',
  23 => 'firefox',
  24 => 'r',
  25 => 'apple_ios',
  26 => 'juniper',
  27 => 'unifi',
  28 => 'brocade',
  29 => 'mikrotik',
  30 => 'arista'
}
RawLength =

The raw length of the UUID structure

16
UriLength =

The base64url-encoded length of the UUID structure

22
TimestampMaxFuture =

Validity constraints for UUID timestamps in UTC

Time.now.utc.to_i + (30*24*3600)
TimestampMaxPast =

Since 2015-01-01 00:00:00 UTC

1420070400

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = nil) ⇒ UUID

Instance methods


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/msf/core/payload/uuid.rb', line 251

def initialize(opts=nil)
  opts = load_new if opts.nil?
  opts = load_uri(opts[:uri]) if opts[:uri]
  opts = load_raw(opts[:raw]) if opts[:raw]

  self.puid      = opts[:puid]
  self.timestamp = opts[:timestamp]
  self.arch      = opts[:arch]
  self.platform  = opts[:platform]
  self.xor1      = opts[:xor1]
  self.xor2      = opts[:xor2]

  self.timestamp  = nil
  self.name       = nil
  self.registered = false

  if opts[:seed]
    self.puid = self.class.seed_to_puid(opts[:seed])
  end

  # Generate some sensible defaults
  self.puid ||= SecureRandom.random_bytes(8)
  self.xor1 ||= rand(256)
  self.xor2 ||= rand(256)
  self.timestamp ||= Time.now.utc.to_i
end

Instance Attribute Details

#archObject

Returns the value of attribute arch


384
385
386
# File 'lib/msf/core/payload/uuid.rb', line 384

def arch
  @arch
end

#nameObject

Returns the value of attribute name


382
383
384
# File 'lib/msf/core/payload/uuid.rb', line 382

def name
  @name
end

#platformObject

Returns the value of attribute platform


385
386
387
# File 'lib/msf/core/payload/uuid.rb', line 385

def platform
  @platform
end

#puidObject

Returns the value of attribute puid


418
419
420
# File 'lib/msf/core/payload/uuid.rb', line 418

def puid
  @puid
end

#registeredObject

Returns the value of attribute registered


380
381
382
# File 'lib/msf/core/payload/uuid.rb', line 380

def registered
  @registered
end

#timestampObject

Returns the value of attribute timestamp


381
382
383
# File 'lib/msf/core/payload/uuid.rb', line 381

def timestamp
  @timestamp
end

#xor1Object

Returns the value of attribute xor1


419
420
421
# File 'lib/msf/core/payload/uuid.rb', line 419

def xor1
  @xor1
end

#xor2Object

Returns the value of attribute xor2


420
421
422
# File 'lib/msf/core/payload/uuid.rb', line 420

def xor2
  @xor2
end

Class Method Details

.filter_invalid(uuid) ⇒ Hash

Filter out UUIDs with obviously invalid fields and return either a validated UUID or a UUID with the arch, platform, and timestamp fields strippped out.

Parameters:

  • uuid (Hash)

    The UUID in hash format

Returns:

  • (Hash)

    The filtered UUID in hash format


180
181
182
183
184
185
186
187
188
189
190
# File 'lib/msf/core/payload/uuid.rb', line 180

def self.filter_invalid(uuid)
  # Verify the UUID fields and return just the Payload ID unless the
  # timestamp is within our constraints and the UUID has either a
  # valid architecture or platform
  if uuid[:timestamp] > TimestampMaxFuture ||
     uuid[:timestamp] < TimestampMaxPast   ||
     (uuid[:arch].nil? && uuid[:platform].nil?)
     return { puid: uuid[:puid] }
  end
  uuid
end

.find_architecture_id(name) ⇒ Integer

Look up the numeric architecture ID given a string as input

Parameters:

  • name (String)

    The name of the architecture to lookup

Returns:

  • (Integer)

    The integer value of this architecture


232
233
234
235
236
237
# File 'lib/msf/core/payload/uuid.rb', line 232

def self.find_architecture_id(name)
  name = name.first if name.kind_of? ::Array
  ( Architectures.keys.select{ |k|
    Architectures[k] == name
  }.first || Architectures[0] ).to_i
end

.find_architecture_name(num) ⇒ Object


243
244
245
# File 'lib/msf/core/payload/uuid.rb', line 243

def self.find_architecture_name(num)
  Architectures[num]
end

.find_platform_id(platform) ⇒ Integer

Look up the numeric platform ID given a string or PlatformList as input

Parameters:

  • platform (String)

    The name of the platform to lookup

Returns:

  • (Integer)

    The integer value of this platform


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/msf/core/payload/uuid.rb', line 209

def self.find_platform_id(platform)
  # Handle a PlatformList input by grabbing the first entry
  if platform.respond_to?(:platforms)
    platform = platform.platforms.first.realname.downcase
  end

  # Map a platform abbreviation to the real name
  name = platform ? Msf::Platform.find_platform(platform) : nil
  if name && name.respond_to?(:realname)
    name = name.realname.downcase
  end

  ( Platforms.keys.select{ |k|
    Platforms[k] == name
  }.first || Platforms[0] ).to_i
end

.find_platform_name(num) ⇒ Object


239
240
241
# File 'lib/msf/core/payload/uuid.rb', line 239

def self.find_platform_name(num)
  Platforms[num]
end

.generate_raw(opts = {}) ⇒ String

Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp

Parameters:

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

    a customizable set of options

Options Hash (opts):

  • :seed (String)

    An optional string to use for generated the unique payload ID, deterministic

  • :puid (String)

    An optional 8-byte string to use as the unique payload ID

  • :arch (String)

    The hardware architecture for this payload

  • :platform (String)

    The operating system platform for this payload

  • :timestamp (String)

    The timestamp in UTC Unix epoch format

  • :xor1 (Integer)

    An optional 8-bit XOR ID for obfuscation

  • :xor2 (Integer)

    An optional 8-bit XOR ID for obfuscation

Returns:

  • (String)

    The encoded payoad UUID as a binary string


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/msf/core/payload/uuid.rb', line 110

def self.generate_raw(opts={})
  plat_id = find_platform_id(opts[:platform]) || 0
  arch_id = find_architecture_id(opts[:arch]) || 0
  tstamp  = opts[:timestamp] || Time.now.utc.to_i
  puid    = opts[:puid]

  if opts[:seed]
    puid = seed_to_puid(opts[:seed])
  end

  puid ||= SecureRandom.random_bytes(8)

  if puid.length != 8
    raise ArgumentError, "The :puid parameter must be exactly 8 bytes"
  end

  plat_xor = opts[:xor1] || rand(256)
  arch_xor = opts[:xor2] || rand(256)

  # Recycle the previous two XOR bytes to keep our output small
  time_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first

  # Combine the payload UID with the arch/platform and use xor to
  # obscure the platform, architecture, and timestamp
  puid +
    [
      plat_xor, arch_xor,
      plat_xor ^ plat_id,
      arch_xor ^ arch_id,
      time_xor ^ tstamp
    ].pack('C4N')
end

.parse_raw(raw) ⇒ Hash

Parse a raw 16-byte payload UUID and return the payload ID, platform, architecture, and timestamp

Parameters:

  • raw (String)

    The raw 16-byte payload UUID to parse

Returns:

  • (Hash)

    A hash containing the Payload ID, platform, architecture, and timestamp


149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/msf/core/payload/uuid.rb', line 149

def self.parse_raw(raw)
  if raw.to_s.length < 16
    raise ArgumentError, "Raw UUID must be at least 16 bytes"
  end

  puid, plat_xor, arch_xor, plat_id, arch_id, tstamp = raw.unpack('a8C4N')
  plat     = find_platform_name(plat_xor ^ plat_id)
  arch     = find_architecture_name(arch_xor ^ arch_id)
  time_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first
  time     = time_xor ^ tstamp
  { puid: puid, platform: plat, arch: arch, timestamp: time, xor1: plat_xor, xor2: arch_xor }
end

.parse_uri(uri) ⇒ Hash

Parse a 22-byte base64url-encoded payload UUID and return the hash

Parameters:

  • uri (String)

    The 22-byte base64url-encoded payload UUID to parse

Returns:

  • (Hash)

    A hash containing the Payload ID, platform, architecture, and timestamp


198
199
200
# File 'lib/msf/core/payload/uuid.rb', line 198

def self.parse_uri(uri)
  parse_raw(Rex::Text.decode_base64url(uri))
end

.seed_to_puid(seed) ⇒ String

Generate a 8-byte payload ID given a seed string

Parameters:

  • seed (String)

    The seed to use to calculate a deterministic payload ID

Returns:

  • (String)

    The 8-byte payload ID


168
169
170
# File 'lib/msf/core/payload/uuid.rb', line 168

def self.seed_to_puid(seed)
  Rex::Text.sha1_raw(seed)[12,8]
end

Instance Method Details

#load_newObject


298
299
300
# File 'lib/msf/core/payload/uuid.rb', line 298

def load_new
 self.class.parse_raw(self.class.generate_raw())
end

#load_raw(raw) ⇒ Hash

Initializes a UUID object given a raw 16+ byte blob

Parameters:

  • raw (String)

    The string containing at least 16 bytes of encoded data

Returns:

  • (Hash)

    The attributes encoded into this UUID


284
285
286
# File 'lib/msf/core/payload/uuid.rb', line 284

def load_raw(raw)
  self.class.filter_invalid(self.class.parse_raw(raw))
end

#load_uri(uri) ⇒ Hash

Initializes a UUID object given a 22+ byte URI

Parameters:

  • uri (String)

    The URI containing at least 22 bytes of encoded data

Returns:

  • (Hash)

    The attributes encoded into this UUID


294
295
296
# File 'lib/msf/core/payload/uuid.rb', line 294

def load_uri(uri)
  self.class.filter_invalid(self.class.parse_uri(uri))
end

#puid_hexString

Provides a hex representation of the Payload UID of the UUID

Returns:

  • (String)

    The 16-byte hex string representing the Payload UID


368
369
370
# File 'lib/msf/core/payload/uuid.rb', line 368

def puid_hex
  self.puid.unpack('H*').first
end

#session_typeObject

Return a string that represents the Meterpreter arch/platform


321
322
323
324
325
326
327
328
329
# File 'lib/msf/core/payload/uuid.rb', line 321

def session_type
  # mini-patch for x86 so that it renders x64 instead. This is
  # mostly to keep various external modules happy.
  arch = self.arch
  if arch == ARCH_X86_64
      arch = ARCH_X64
  end
  "#{arch}/#{self.platform}"
end

#to_hHash

Provides a hash representation of a UUID

Returns:

  • (Hash)

    The hash representation of the UUID suitable for creating a new one


336
337
338
339
340
341
342
343
# File 'lib/msf/core/payload/uuid.rb', line 336

def to_h
  {
      puid: self.puid,
      arch: self.arch, platform: self.platform,
      timestamp: self.timestamp,
      xor1: self.xor1, xor2: self.xor2
  }
end

#to_rawString

Provides a raw byte representation of a UUID

Returns:

  • (String)

    The 16-byte raw encoded version of the UUID


350
351
352
# File 'lib/msf/core/payload/uuid.rb', line 350

def to_raw
  self.class.generate_raw(self.to_h)
end

#to_sString

Provides a string representation of a UUID

Returns:

  • (String)

    The human-readable version of the UUID data


307
308
309
310
311
312
313
314
315
316
# File 'lib/msf/core/payload/uuid.rb', line 307

def to_s
  arch_id   = self.class.find_architecture_id(self.arch).to_s
  plat_id   = self.class.find_platform_id(self.platform).to_s
  [
    self.puid_hex,
    [ self.arch     || "noarch",     arch_id ].join("="),
    [ self.platform || "noplatform", plat_id ].join("="),
    Time.at(self.timestamp.to_i).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
  ].join("/")
end

#to_uriString

Provides a URI-encoded representation of a UUID

Returns:

  • (String)

    The 22-byte URI encoded version of the UUID


359
360
361
# File 'lib/msf/core/payload/uuid.rb', line 359

def to_uri
  Rex::Text.encode_base64url(self.to_raw)
end

#xor_resetObject

Clears the two random XOR keys used for obfuscation


375
376
377
378
# File 'lib/msf/core/payload/uuid.rb', line 375

def xor_reset
  self.xor1 = self.xor2 = nil
  self
end