Land #5394, UUID registration and tracking
commit
c241018ff6
|
@ -589,8 +589,11 @@ class ReadableText
|
|||
sess_via = session.via_exploit.to_s
|
||||
sess_type = session.type.to_s
|
||||
sess_uuid = session.payload_uuid.to_s
|
||||
sess_puid = session.payload_uuid.respond_to?(:puid_hex) ? session.payload_uuid.puid_hex : nil
|
||||
|
||||
sess_checkin = "<none>"
|
||||
sess_machine_id = session.machine_id.to_s
|
||||
sess_registration = "No"
|
||||
|
||||
if session.respond_to? :platform
|
||||
sess_type << (" " + session.platform)
|
||||
|
@ -600,6 +603,13 @@ class ReadableText
|
|||
sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"
|
||||
end
|
||||
|
||||
if session.payload_uuid.respond_to?(:puid_hex) && (uuid_info = framework.uuid_db[sess_puid])
|
||||
sess_registration = "Yes"
|
||||
if uuid_info['name']
|
||||
sess_registration << " - Name=\"#{uuid_info['name']}\""
|
||||
end
|
||||
end
|
||||
|
||||
out << " Session ID: #{sess_id}\n"
|
||||
out << " Type: #{sess_type}\n"
|
||||
out << " Info: #{sess_info}\n"
|
||||
|
@ -608,6 +618,10 @@ class ReadableText
|
|||
out << " UUID: #{sess_uuid}\n"
|
||||
out << " MachineID: #{sess_machine_id}\n"
|
||||
out << " CheckIn: #{sess_checkin}\n"
|
||||
out << " Registered: #{sess_registration}\n"
|
||||
|
||||
|
||||
|
||||
out << "\n"
|
||||
end
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class Framework
|
|||
require 'msf/core/plugin_manager'
|
||||
require 'msf/core/db_manager'
|
||||
require 'msf/core/event_dispatcher'
|
||||
|
||||
require 'rex/json_hash_file'
|
||||
|
||||
#
|
||||
# Creates an instance of the framework context.
|
||||
|
@ -91,6 +91,7 @@ class Framework
|
|||
self.datastore = DataStore.new
|
||||
self.jobs = Rex::JobContainer.new
|
||||
self.plugins = PluginManager.new(self)
|
||||
self.uuid_db = Rex::JSONHashFile.new(::File.join(Msf::Config.config_directory, "payloads.json"))
|
||||
|
||||
# Configure the thread factory
|
||||
Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self)
|
||||
|
@ -187,6 +188,12 @@ class Framework
|
|||
# unloading of plugins.
|
||||
#
|
||||
attr_reader :plugins
|
||||
#
|
||||
# The framework instance's payload uuid database. The payload uuid
|
||||
# database is used to record and match the unique ID values embedded
|
||||
# into generated payloads.
|
||||
#
|
||||
attr_reader :uuid_db
|
||||
|
||||
# The framework instance's db manager. The db manager
|
||||
# maintains the database db and handles db events
|
||||
|
@ -243,6 +250,7 @@ protected
|
|||
attr_writer :jobs # :nodoc:
|
||||
attr_writer :plugins # :nodoc:
|
||||
attr_writer :db # :nodoc:
|
||||
attr_writer :uuid_db # :nodoc:
|
||||
end
|
||||
|
||||
class FrameworkEventSubscriber
|
||||
|
|
|
@ -56,7 +56,8 @@ module ReverseHttp
|
|||
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']),
|
||||
OptInt.new('ReverseListenerBindPort', [ false, 'The port to bind to on the local system if different from LPORT' ]),
|
||||
OptBool.new('OverrideRequestHost', [ false, 'Forces clients to connect to LHOST:LPORT instead of keeping original payload host', false ]),
|
||||
OptString.new('HttpUnknownRequestResponse', [ false, 'The returned HTML response body when the handler receives a request that is not from a payload', '<html><body><h1>It works!</h1></body></html>' ])
|
||||
OptString.new('HttpUnknownRequestResponse', [ false, 'The returned HTML response body when the handler receives a request that is not from a payload', '<html><body><h1>It works!</h1></body></html>' ]),
|
||||
OptBool.new('IgnoreUnknownPayloads', [false, 'Whether to drop connections from payloads using unknown UUIDs', false])
|
||||
], Msf::Handler::ReverseHttp)
|
||||
end
|
||||
|
||||
|
@ -153,6 +154,10 @@ module ReverseHttp
|
|||
|
||||
print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}")
|
||||
lookup_proxy_settings
|
||||
|
||||
if datastore['IgnoreUnknownPayloads']
|
||||
print_status("Handler is ignoring unknown payloads, there are #{framework.uuid_db.keys.length} UUIDs whitelisted")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -228,6 +233,21 @@ protected
|
|||
conn_id = generate_uri_uuid(URI_CHECKSUM_CONN, uuid)
|
||||
end
|
||||
|
||||
# Validate known UUIDs for all requests if IgnoreUnknownPayloads is set
|
||||
if datastore['IgnoreUnknownPayloads'] && ! framework.uuid_db[uuid.puid_hex]
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Ignoring request with unknown UUID")
|
||||
info[:mode] = :unknown_uuid
|
||||
end
|
||||
|
||||
# Validate known URLs for all session init requests if IgnoreUnknownPayloads is set
|
||||
if datastore['IgnoreUnknownPayloads'] && info[:mode].to_s =~ /^init_/
|
||||
allowed_urls = framework.uuid_db[uuid.puid_hex]['urls'] || []
|
||||
unless allowed_urls.include?(req.relative_resource)
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Ignoring request with unknown UUID URL #{req.relative_resource}")
|
||||
info[:mode] = :unknown_uuid_url
|
||||
end
|
||||
end
|
||||
|
||||
self.pending_connections += 1
|
||||
|
||||
# Process the requested resource.
|
||||
|
@ -374,7 +394,9 @@ protected
|
|||
})
|
||||
|
||||
else
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...")
|
||||
unless [:unknown_uuid, :unknown_uuid_url].include?(info[:mode])
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} with UA #{req.headers['User-Agent']}...")
|
||||
end
|
||||
resp.code = 200
|
||||
resp.message = "OK"
|
||||
resp.body = datastore['HttpUnknownRequestResponse'].to_s
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/php/send_uuid'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -16,7 +16,7 @@ module Msf
|
|||
module Payload::Php::ReverseTcp
|
||||
|
||||
include Msf::Payload::Php::SendUUID
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
##
|
||||
# This module contains helper functions for creating the transport
|
||||
|
@ -8,7 +8,7 @@ require 'msf/core/payload/uuid_options'
|
|||
##
|
||||
module Msf::Payload::TransportConfig
|
||||
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
def transport_config_reverse_tcp(opts={})
|
||||
config = transport_config_bind_tcp(opts)
|
||||
|
|
|
@ -7,7 +7,7 @@ require 'rex/payloads/meterpreter/uri_checksum'
|
|||
#
|
||||
# This module provides datastore option definitions and helper methods for payload modules that support UUIDs
|
||||
#
|
||||
module Msf::Payload::UUIDOptions
|
||||
module Msf::Payload::UUID::Options
|
||||
|
||||
include Rex::Payloads::Meterpreter::UriChecksum
|
||||
|
||||
|
@ -17,6 +17,8 @@ module Msf::Payload::UUIDOptions
|
|||
[
|
||||
Msf::OptString.new('PayloadUUIDSeed', [ false, 'A string to use when generating the payload UUID (deterministic)']),
|
||||
Msf::OptString.new('PayloadUUIDRaw', [ false, 'A hex string representing the raw 8-byte PUID value for the UUID']),
|
||||
Msf::OptString.new('PayloadUUIDName', [ false, 'A human-friendly name to reference this unique payload (requires tracking)']),
|
||||
Msf::OptBool.new('PayloadUUIDTracking', [ true, 'Whether or not to automatically register generated UUIDs', false]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -31,7 +33,7 @@ module Msf::Payload::UUIDOptions
|
|||
def generate_uri_uuid_mode(mode,len=nil)
|
||||
sum = uri_checksum_lookup(mode)
|
||||
|
||||
# The URI length may not have room for an embedded checksum
|
||||
# The URI length may not have room for an embedded UUID
|
||||
if len && len < URI_CHECKSUM_UUID_MIN_LEN
|
||||
# Throw an error if the user set a seed, but there is no room for it
|
||||
if datastore['PayloadUUIDSeed'].to_s.length > 0 ||datastore['PayloadUUIDRaw'].to_s.length > 0
|
||||
|
@ -40,7 +42,11 @@ module Msf::Payload::UUIDOptions
|
|||
return "/" + generate_uri_checksum(sum, len, prefix="")
|
||||
end
|
||||
|
||||
generate_uri_uuid(sum, generate_payload_uuid, len)
|
||||
uuid = generate_payload_uuid
|
||||
uri = generate_uri_uuid(sum, uuid, len)
|
||||
record_payload_uuid_url(uuid, uri)
|
||||
|
||||
uri
|
||||
end
|
||||
|
||||
# Generate a Payload UUID
|
||||
|
@ -66,7 +72,48 @@ module Msf::Payload::UUIDOptions
|
|||
conf[:puid] = puid_raw
|
||||
end
|
||||
|
||||
Msf::Payload::UUID.new(conf)
|
||||
if datastore['PayloadUUIDName'].to_s.length > 0 && ! datastore['PayloadUUIDTracking']
|
||||
raise ArgumentError, "The PayloadUUIDName value is ignored unless PayloadUUIDTracking is enabled"
|
||||
end
|
||||
|
||||
# Generate the UUID object
|
||||
uuid = Msf::Payload::UUID.new(conf)
|
||||
record_payload_uuid(uuid)
|
||||
|
||||
uuid
|
||||
end
|
||||
|
||||
# Store a UUID in the JSON database if tracking is enabled
|
||||
def record_payload_uuid(uuid, info={})
|
||||
return unless datastore['PayloadUUIDTracking']
|
||||
|
||||
uuid_info = info.merge({
|
||||
arch: uuid.arch,
|
||||
platform: uuid.platform,
|
||||
timestamp: uuid.timestamp,
|
||||
payload: self.fullname,
|
||||
datastore: self.datastore
|
||||
})
|
||||
|
||||
if datastore['PayloadUUIDSeed'].to_s.length > 0
|
||||
uuid_info[:seed] = datastore['PayloadUUIDSeed']
|
||||
end
|
||||
|
||||
if datastore['PayloadUUIDName'].to_s.length > 0
|
||||
uuid_info[:name] = datastore['PayloadUUIDName']
|
||||
end
|
||||
|
||||
framework.uuid_db[uuid.puid_hex] = uuid_info
|
||||
end
|
||||
|
||||
# Store a UUID URL in the JSON database if tracking is enabled
|
||||
def record_payload_uuid_url(uuid, url)
|
||||
return unless datastore['PayloadUUIDTracking']
|
||||
uuid_info = framework.uuid_db[uuid.puid_hex]
|
||||
uuid_info['urls'] ||= []
|
||||
uuid_info['urls'] << url
|
||||
uuid_info['urls'].uniq!
|
||||
framework.uuid_db[uuid.puid_hex] = uuid_info
|
||||
end
|
||||
|
||||
end
|
|
@ -4,7 +4,7 @@ require 'msf/core'
|
|||
require 'msf/core/payload/transport_config'
|
||||
require 'msf/core/payload/windows/block_api'
|
||||
require 'msf/core/payload/windows/exitfunk'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -20,7 +20,7 @@ module Payload::Windows::ReverseHttp
|
|||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::BlockApi
|
||||
include Msf::Payload::Windows::Exitfunk
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
#
|
||||
# Register reverse_http specific options
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'msf/core'
|
|||
require 'msf/core/payload/transport_config'
|
||||
require 'msf/core/payload/windows/x64/block_api'
|
||||
require 'msf/core/payload/windows/x64/exitfunk'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -20,7 +20,7 @@ module Payload::Windows::ReverseHttp_x64
|
|||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::BlockApi_x64
|
||||
include Msf::Payload::Windows::Exitfunk_x64
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
#
|
||||
# Register reverse_http specific options
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
# -*- coding => binary -*-
|
||||
|
||||
require 'json'
|
||||
require 'fileutils'
|
||||
|
||||
#
|
||||
# This class provides a thread-friendly hash file store in JSON format
|
||||
#
|
||||
module Rex
|
||||
class JSONHashFile
|
||||
|
||||
attr_accessor :path
|
||||
|
||||
def initialize(path)
|
||||
self.path = path
|
||||
@lock = Mutex.new
|
||||
@hash = {}
|
||||
@last = 0
|
||||
::FileUtils.mkdir_p(::File.dirname(path))
|
||||
synced_update
|
||||
end
|
||||
|
||||
def [](k)
|
||||
synced_update
|
||||
@hash[k]
|
||||
end
|
||||
|
||||
def []=(k,v)
|
||||
synced_update do
|
||||
@hash[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
def keys
|
||||
synced_update
|
||||
@hash.keys
|
||||
end
|
||||
|
||||
def delete(k)
|
||||
synced_update do
|
||||
@hash.delete(k)
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
synced_update do
|
||||
@hash.clear
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Save the file, but prevent thread & process contention
|
||||
def synced_update(&block)
|
||||
@lock.synchronize do
|
||||
::File.open(path, ::File::RDWR|::File::CREAT) do |fd|
|
||||
fd.flock(::File::LOCK_EX)
|
||||
|
||||
# Reload and merge if the file has changed recently
|
||||
if fd.stat.mtime.to_f > @last
|
||||
parse_data(fd.read).merge(@hash).each_pair do |k,v|
|
||||
@hash[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
res = nil
|
||||
|
||||
# Update the file on disk if new data is written
|
||||
if block_given?
|
||||
res = block.call
|
||||
fd.rewind
|
||||
fd.write(JSON.pretty_generate(@hash))
|
||||
fd.sync
|
||||
fd.truncate(fd.pos)
|
||||
end
|
||||
|
||||
@last = fd.stat.mtime.to_f
|
||||
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def parse_data(data)
|
||||
return {} if data.to_s.strip.length == 0
|
||||
begin
|
||||
JSON.parse(data)
|
||||
rescue JSON::ParserError => e
|
||||
# elog("JSONHashFile @ #{path} was corrupt: #{e.class} #{e}"
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
|
@ -13,7 +13,7 @@ module Metasploit3
|
|||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Java
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 742
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
require 'msf/core/payload/uuid_options'
|
||||
require 'msf/core/payload/uuid/options'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
|
@ -13,7 +13,7 @@ module Metasploit3
|
|||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::UUIDOptions
|
||||
include Msf::Payload::UUID::Options
|
||||
|
||||
def self.handler_type_alias
|
||||
"reverse_http_proxy_pstore"
|
||||
|
|
Loading…
Reference in New Issue