Refactor Msf::Payload::UUID, use this in reverse_http
parent
0d1fe37710
commit
378e867486
|
@ -212,14 +212,18 @@ protected
|
||||||
#
|
#
|
||||||
def on_request(cli, req, obj)
|
def on_request(cli, req, obj)
|
||||||
resp = Rex::Proto::Http::Response.new
|
resp = Rex::Proto::Http::Response.new
|
||||||
|
|
||||||
print_status("#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}...")
|
|
||||||
|
|
||||||
info = process_uri_resource(req.relative_resource)
|
info = process_uri_resource(req.relative_resource)
|
||||||
|
uuid = info[:uuid] || Msf::Payload::UUID.new
|
||||||
|
|
||||||
|
# Configure the UUID architecture and payload if necessary
|
||||||
|
uuid.arch = obj.arch if uuid.arch.nil?
|
||||||
|
uuid.platform = obj.platform if uuid.platform.nil?
|
||||||
|
|
||||||
|
print_status "#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}... (UUID:#{uuid.to_s})"
|
||||||
|
|
||||||
conn_id = nil
|
conn_id = nil
|
||||||
if info[:mode] && info[:mode] != :connect
|
if info[:mode] && info[:mode] != :connect
|
||||||
conn_id = generate_uri_connect_uuid(info[:uuid], obj.arch, obj.platform)
|
conn_id = generate_uri_connect_uuid(uuid)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Process the requested resource.
|
# Process the requested resource.
|
||||||
|
@ -255,6 +259,7 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:uuid => uuid
|
||||||
})
|
})
|
||||||
self.pending_connections += 1
|
self.pending_connections += 1
|
||||||
|
|
||||||
|
@ -282,7 +287,8 @@ protected
|
||||||
:url => url,
|
:url => url,
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?
|
:ssl => ssl?,
|
||||||
|
:uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
when :init_native
|
when :init_native
|
||||||
|
@ -318,6 +324,7 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
when :connect
|
when :connect
|
||||||
|
@ -333,6 +340,7 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
@ -46,48 +46,25 @@ module Msf
|
||||||
# Figure out the mode based on the checksum
|
# Figure out the mode based on the checksum
|
||||||
uri_csum = Rex::Text.checksum8(uri_bare)
|
uri_csum = Rex::Text.checksum8(uri_bare)
|
||||||
|
|
||||||
|
# Extract the UUID if the URI is long enough
|
||||||
uri_uuid = nil
|
uri_uuid = nil
|
||||||
|
|
||||||
if uri_bare.length >= URI_CHECKSUM_UUID_MIN_LEN
|
if uri_bare.length >= URI_CHECKSUM_UUID_MIN_LEN
|
||||||
uri_uuid =
|
uri_uuid = Msf::Payload::UUID.new(uri: uri_bare)
|
||||||
Msf::Payload::UUID.payload_uuid_parse_raw(
|
|
||||||
Rex::Text.decode_base64url(
|
|
||||||
uri_bare[0, Msf::Payload::UUID::UriLength]))
|
|
||||||
|
|
||||||
# Verify the uri_uuid fields and unset everything but
|
|
||||||
# the unique ID itself unless it looks wonky.
|
|
||||||
if uri_uuid[:timestamp] > (Time.now.utc.to_i + (24*3600*365)) ||
|
|
||||||
uri_uuid[:timestamp] < (Time.now.utc.to_i - (24*3600*365)) ||
|
|
||||||
(uri_uuid[:arch].nil? && uri_uuid[:platform].nil?)
|
|
||||||
uri_uuid = { puid: uri_uuid[:puid] }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
uri_mode = URI_CHECKSUM_MODES[uri_csum]
|
uri_mode = URI_CHECKSUM_MODES[uri_csum]
|
||||||
|
|
||||||
# Return a hash of URI attributes to the caller
|
# Return a hash of URI attributes
|
||||||
{
|
{ uri: uri_bare, sum: uri_csum, uuid: uri_uuid, mode: uri_mode }
|
||||||
uri: uri_bare,
|
|
||||||
sum: uri_csum,
|
|
||||||
uuid: uri_uuid,
|
|
||||||
mode: uri_mode
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create a URI that matches the :connect mode with optional UUID, Arch, and Platform
|
# Create a URI that matches the :connect mode with optional UUID, Arch, and Platform
|
||||||
#
|
#
|
||||||
# @param uuid [Hash] An optional hash with the UUID parameters
|
# @param uuid [Msf::Payload::UUID] A valid UUID object
|
||||||
# @param arch [String] An optional architecture name to use if no UUID is provided
|
# @return [String] The URI string for connections
|
||||||
# @param platform [String] An optional platform name to use if no UUID is provided
|
def generate_uri_connect_uuid(uuid)
|
||||||
# @return [String] The URI string that checksums to the given value
|
|
||||||
def generate_uri_connect_uuid(uuid=nil, arch=nil, platform=nil)
|
|
||||||
curl_uri_len = URI_CHECKSUM_UUID_MIN_LEN+rand(URI_CHECKSUM_CONN_MAX_LEN-URI_CHECKSUM_UUID_MIN_LEN)
|
curl_uri_len = URI_CHECKSUM_UUID_MIN_LEN+rand(URI_CHECKSUM_CONN_MAX_LEN-URI_CHECKSUM_UUID_MIN_LEN)
|
||||||
curl_prefix = Rex::Text.encode_base64url(
|
curl_prefix = uuid.to_uri
|
||||||
Msf::Payload::UUID.payload_uuid_generate_raw(
|
|
||||||
uuid: uuid[:puid],
|
|
||||||
arch: uuid[:arch] || arch,
|
|
||||||
platform: uuid[:platform] || platform,
|
|
||||||
timestamp: uuid[:timestamp] ))
|
|
||||||
|
|
||||||
# Pad out the URI and make the checksum match :connect
|
# Pad out the URI and make the checksum match :connect
|
||||||
"/" + generate_uri_checksum(URI_CHECKSUM_CONN, curl_uri_len, curl_prefix)
|
"/" + generate_uri_checksum(URI_CHECKSUM_CONN, curl_uri_len, curl_prefix)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding => binary -*-
|
# -*- coding => binary -*-
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
require 'msf/core/module/platform'
|
require 'msf/core/module/platform'
|
||||||
require 'rex/constants'
|
require 'rex/constants'
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
|
@ -10,6 +11,11 @@ require 'rex/text'
|
||||||
#
|
#
|
||||||
class Msf::Payload::UUID
|
class Msf::Payload::UUID
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Constants
|
||||||
|
#
|
||||||
|
|
||||||
Architectures = {
|
Architectures = {
|
||||||
0 => nil,
|
0 => nil,
|
||||||
1 => ARCH_X86,
|
1 => ARCH_X86,
|
||||||
|
@ -62,33 +68,57 @@ class Msf::Payload::UUID
|
||||||
23 => 'firefox'
|
23 => 'firefox'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The raw length of the UUID structure
|
||||||
RawLength = 16
|
RawLength = 16
|
||||||
|
|
||||||
|
# The base64url-encoded length of the UUID structure
|
||||||
UriLength = 22
|
UriLength = 22
|
||||||
|
|
||||||
|
# Validity constraints for UUID timestamps in UTC
|
||||||
|
TimestampMaxFuture = Time.now.utc.to_i + (30*24*3600) # Up to 30 days in the future
|
||||||
|
TimestampMaxPast = 1420070400 # Since 2015-01-01 00:00:00 UTC
|
||||||
|
|
||||||
|
#
|
||||||
|
# Class Methods
|
||||||
|
#
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp
|
# Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp
|
||||||
#
|
#
|
||||||
# @options opts [String] :seed A optional string to use for generated the unique payload ID, deterministic
|
# @options opts [String] :seed An optional string to use for generated the unique payload ID, deterministic
|
||||||
|
# @options opts [String] :puid An optional 8-byte string to use as the unique payload ID
|
||||||
# @options opts [String] :arch The hardware architecture for this payload
|
# @options opts [String] :arch The hardware architecture for this payload
|
||||||
# @options opts [String] :platform The operating system platform for this payload
|
# @options opts [String] :platform The operating system platform for this payload
|
||||||
# @options opts [String] :timestamp The timestamp in UTC Unix epoch format
|
# @options opts [String] :timestamp The timestamp in UTC Unix epoch format
|
||||||
|
# @options opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation
|
||||||
|
# @options opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation
|
||||||
# @return [String] The encoded payoad UUID as a binary string
|
# @return [String] The encoded payoad UUID as a binary string
|
||||||
#
|
#
|
||||||
def self.payload_uuid_generate_raw(opts={})
|
def self.generate_raw(opts={})
|
||||||
plat_id = find_platform_id(opts[:platform]) || 0
|
plat_id = find_platform_id(opts[:platform]) || 0
|
||||||
arch_id = find_architecture_id(opts[:arch]) || 0
|
arch_id = find_architecture_id(opts[:arch]) || 0
|
||||||
seed = opts[:seed] || Rex::Text.rand_text(16)
|
|
||||||
tstamp = opts[:timestamp] || Time.now.utc.to_i
|
tstamp = opts[:timestamp] || Time.now.utc.to_i
|
||||||
|
puid = opts[:puid]
|
||||||
|
|
||||||
plat_xor = rand(255)
|
if opts[:seed]
|
||||||
arch_xor = rand(255)
|
puid = seed_to_puid(opts[:seed])
|
||||||
|
end
|
||||||
|
|
||||||
|
puid ||= Rex::Text.rand_text(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
|
# 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
|
time_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first
|
||||||
|
|
||||||
# Combine the last 64-bits of the SHA1 of seed with the arch/platform
|
# Combine the payload UID with the arch/platform and use xor to
|
||||||
# Use XOR to obscure the platform, architecture, and timestamp
|
# obscure the platform, architecture, and timestamp
|
||||||
Rex::Text.sha1_raw(seed)[12,8] +
|
puid +
|
||||||
[
|
[
|
||||||
plat_xor, arch_xor,
|
plat_xor, arch_xor,
|
||||||
plat_xor ^ plat_id,
|
plat_xor ^ plat_id,
|
||||||
|
@ -103,23 +133,57 @@ class Msf::Payload::UUID
|
||||||
# @param raw [String] The raw 16-byte payload UUID to parse
|
# @param raw [String] The raw 16-byte payload UUID to parse
|
||||||
# @return [Hash] A hash containing the Payload ID, platform, architecture, and timestamp
|
# @return [Hash] A hash containing the Payload ID, platform, architecture, and timestamp
|
||||||
#
|
#
|
||||||
def self.payload_uuid_parse_raw(raw)
|
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')
|
puid, plat_xor, arch_xor, plat_id, arch_id, tstamp = raw.unpack('A8C4N')
|
||||||
plat = find_platform_name(plat_xor ^ plat_id)
|
plat = find_platform_name(plat_xor ^ plat_id)
|
||||||
arch = find_architecture_name(arch_xor ^ arch_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_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first
|
||||||
time = time_xor ^ tstamp
|
time = time_xor ^ tstamp
|
||||||
{ puid: puid, platform: plat, arch: arch, timestamp: time }
|
{ puid: puid, platform: plat, arch: arch, timestamp: time, xor1: plat_xor, xor2: arch_xor }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Alias for the class method
|
#
|
||||||
def payload_uuid_generate_raw(opts)
|
# Generate a 8-byte payload ID given a seed string
|
||||||
self.class.payload_uuid_generate_raw(opts)
|
#
|
||||||
|
# @param seed [String] The seed to use to calculate a deterministic payload ID
|
||||||
|
# @return [String] The 8-byte payload ID
|
||||||
|
#
|
||||||
|
def self.seed_to_puid(seed)
|
||||||
|
Rex::Text.sha1_raw(seed)[12,8]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Alias for the class method
|
#
|
||||||
def parse_payload_uuid_raw(raw)
|
# Filter out UUIDs with obviously invalid fields and return either
|
||||||
self.class.payload_uuid_parse_raw(raw)
|
# a validated UUID or a UUID with the arch, platform, and timestamp
|
||||||
|
# fields strippped out.
|
||||||
|
#
|
||||||
|
# @param uuid [Hash] The UUID in hash format
|
||||||
|
# @return [Hash] The filtered UUID in hash format
|
||||||
|
#
|
||||||
|
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
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parse a 22-byte base64url-encoded payload UUID and return the hash
|
||||||
|
#
|
||||||
|
# @param uri [String] The 22-byte base64url-encoded payload UUID to parse
|
||||||
|
# @return [Hash] A hash containing the Payload ID, platform, architecture, and timestamp
|
||||||
|
#
|
||||||
|
def self.parse_uri(uri)
|
||||||
|
parse_raw(Rex::Text.decode_base64url(uri))
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_platform_id(platform)
|
def self.find_platform_id(platform)
|
||||||
|
@ -130,17 +194,16 @@ class Msf::Payload::UUID
|
||||||
|
|
||||||
# Map a platform abbreviation to the real name
|
# Map a platform abbreviation to the real name
|
||||||
name = Msf::Platform::Abbrev[platform]
|
name = Msf::Platform::Abbrev[platform]
|
||||||
|
( Platforms.keys.select{ |k|
|
||||||
Platforms.keys.select{ |k|
|
|
||||||
Platforms[k] == name
|
Platforms[k] == name
|
||||||
}.first || Platforms[0]
|
}.first || Platforms[0] ).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_architecture_id(name)
|
def self.find_architecture_id(name)
|
||||||
name = name.first if name.kind_of? ::Array
|
name = name.first if name.kind_of? ::Array
|
||||||
Architectures.keys.select{ |k|
|
( Architectures.keys.select{ |k|
|
||||||
Architectures[k] == name
|
Architectures[k] == name
|
||||||
}.first || Architectures[0]
|
}.first || Architectures[0] ).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_platform_name(num)
|
def self.find_platform_name(num)
|
||||||
|
@ -151,4 +214,118 @@ class Msf::Payload::UUID
|
||||||
Architectures[num]
|
Architectures[num]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Instance methods
|
||||||
|
#
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
if opts[:seed]
|
||||||
|
self.puid = self.class.seed_to_puid(opts[:seed])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate some sensible defaults
|
||||||
|
self.puid ||= Rex::Text.rand_text(8)
|
||||||
|
self.xor1 ||= rand(256)
|
||||||
|
self.xor2 ||= rand(256)
|
||||||
|
self.timestamp ||= Time.now.utc.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_raw(raw)
|
||||||
|
self.class.filter_invalid(self.class.parse_raw(raw))
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_uri(uri)
|
||||||
|
self.class.filter_invalid(self.class.parse_uri(uri))
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_new
|
||||||
|
self.class.parse_raw(self.class.generate_raw())
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{
|
||||||
|
puid: self.puid,
|
||||||
|
arch: self.arch, platform: self.platform,
|
||||||
|
timestamp: self.timestamp,
|
||||||
|
xor1: self.xor1, xor2: self.xor2
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_raw
|
||||||
|
self.class.generate_raw(self.to_h)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_uri
|
||||||
|
Rex::Text.encode_base64url(self.to_raw)
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_reset
|
||||||
|
self.xor1 = self.xor2 = nil
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def puid_hex
|
||||||
|
self.puid.unpack('H*').first
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :arch
|
||||||
|
attr_reader :platform
|
||||||
|
|
||||||
|
def arch=(arch_str)
|
||||||
|
if arch_str.nil?
|
||||||
|
@arch = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
arch_id = self.class.find_architecture_id(arch_str)
|
||||||
|
if arch_id == 0
|
||||||
|
raise ArgumentError, "Invalid architecture: '#{arch_str}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
arch_name = self.class.find_architecture_name(arch_id)
|
||||||
|
@arch = arch_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def platform=(plat_str)
|
||||||
|
if plat_str.nil?
|
||||||
|
@platform = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
plat_id = self.class.find_platform_id(plat_str)
|
||||||
|
if plat_id == 0
|
||||||
|
raise ArgumentError, "Invalid platform: '#{plat_str}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
plat_name = self.class.find_platform_name(plat_id)
|
||||||
|
@platform = plat_name
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :timestamp
|
||||||
|
attr_accessor :puid
|
||||||
|
attr_accessor :xor1
|
||||||
|
attr_accessor :xor2
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue