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 => Rex::Arch::ARCH_X86,
   2 => Rex::Arch::ARCH_X64, # removed Rex::Arch::ARCH_X86_64, now consistent across the board
   3 => Rex::Arch::ARCH_X64,
   4 => Rex::Arch::ARCH_MIPS,
   5 => Rex::Arch::ARCH_MIPSLE,
   6 => Rex::Arch::ARCH_MIPSBE,
   7 => Rex::Arch::ARCH_PPC,
   8 => Rex::Arch::ARCH_PPC64,
   9 => Rex::Arch::ARCH_CBEA,
  10 => Rex::Arch::ARCH_CBEA64,
  11 => Rex::Arch::ARCH_SPARC,
  12 => Rex::Arch::ARCH_ARMLE,
  13 => Rex::Arch::ARCH_ARMBE,
  14 => Rex::Arch::ARCH_CMD,
  15 => Rex::Arch::ARCH_PHP,
  16 => Rex::Arch::ARCH_TTY,
  17 => Rex::Arch::ARCH_JAVA,
  18 => Rex::Arch::ARCH_RUBY,
  19 => Rex::Arch::ARCH_DALVIK,
  20 => Rex::Arch::ARCH_PYTHON,
  21 => Rex::Arch::ARCH_NODEJS,
  22 => Rex::Arch::ARCH_FIREFOX,
  23 => Rex::Arch::ARCH_ZARCH,
  24 => Rex::Arch::ARCH_AARCH64,
  25 => Rex::Arch::ARCH_MIPS64,
  26 => Rex::Arch::ARCH_PPC64LE,
  27 => Rex::Arch::ARCH_R,
  28 => Rex::Arch::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


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

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


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

def arch
  @arch
end

#nameObject

Returns the value of attribute name


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

def name
  @name
end

#platformObject

Returns the value of attribute platform


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

def platform
  @platform
end

#puidObject

Returns the value of attribute puid


416
417
418
# File 'lib/msf/core/payload/uuid.rb', line 416

def puid
  @puid
end

#registeredObject

Returns the value of attribute registered


378
379
380
# File 'lib/msf/core/payload/uuid.rb', line 378

def registered
  @registered
end

#timestampObject

Returns the value of attribute timestamp


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

def timestamp
  @timestamp
end

#xor1Object

Returns the value of attribute xor1


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

def xor1
  @xor1
end

#xor2Object

Returns the value of attribute xor2


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

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


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

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


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

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


241
242
243
# File 'lib/msf/core/payload/uuid.rb', line 241

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


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

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


237
238
239
# File 'lib/msf/core/payload/uuid.rb', line 237

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


108
109
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
# File 'lib/msf/core/payload/uuid.rb', line 108

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


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

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


196
197
198
# File 'lib/msf/core/payload/uuid.rb', line 196

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


166
167
168
# File 'lib/msf/core/payload/uuid.rb', line 166

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

Instance Method Details

#load_newObject


296
297
298
# File 'lib/msf/core/payload/uuid.rb', line 296

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


282
283
284
# File 'lib/msf/core/payload/uuid.rb', line 282

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


292
293
294
# File 'lib/msf/core/payload/uuid.rb', line 292

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


366
367
368
# File 'lib/msf/core/payload/uuid.rb', line 366

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

#session_typeObject

Return a string that represents the Meterpreter arch/platform


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

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


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

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


348
349
350
# File 'lib/msf/core/payload/uuid.rb', line 348

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


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

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


357
358
359
# File 'lib/msf/core/payload/uuid.rb', line 357

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

#xor_resetObject

Clears the two random XOR keys used for obfuscation


373
374
375
376
# File 'lib/msf/core/payload/uuid.rb', line 373

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