metasploit-framework/lib/msf/core/recon_manager.rb

220 lines
4.7 KiB
Ruby

require 'thread'
require 'msf/core/recon'
module Msf
###
#
# The states that a host can be in.
#
###
module HostState
#
# The host is alive.
#
Alive = "alive"
#
# The host is dead.
#
Dead = "down"
#
# The host state is unknown.
#
Unknown = "unknown"
end
###
#
# The states that a service can be in.
#
###
module ServiceState
#
# The service is alive.
#
Up = "up"
#
# The service is dead.
#
Dead = "down"
#
# The service state is unknown.
#
Unknown = "unknown"
end
###
#
# This class manages the tracking of entities and the dispatching of events
# pertaining to recon information collection. When hosts are discovered, the
# recon module tracks them and dispatches the appropriate events to the
# framework event manager so that subscribers can be notified.
#
###
class ReconManager
include Framework::Offspring
###
#
# This mixin is used to extend Host entity instances such that the recon
# manager can track internal state information.
#
###
module ExtendedHostState
#
# Tracks the number of manager state unknowns returned.
#
attr_accessor :_mgr_state_unknown
end
def initialize(framework)
self.framework = framework;
self.host_hash = Hash.new
self.host_hash_mutex = Mutex.new
end
##
#
# Host reporting
#
##
#
# Reports a host as being in a given state by address.
#
def report_host_state(mod, address, state, context = nil)
# TODO: use the current thread's Comm as part of the hash key to support
# conflicting addresses in different networks (established through
# different comms).
hash_key = host_hash_key(address)
# If a host already exists with this information, then check to see what
# status we received.
if (host = host_hash[hash_key])
if (state == HostState::Unknown)
host._mgr_state_unknown += 1
elsif (state == HostState::Dead)
dead_host(host, context)
else
host._mgr_state_unknown = 0
end
# Otherwise, if we have no host yet, get one and start handling it.
elsif (state == HostState::Alive)
host = Recon::Entity::Host.new(address)
new_host(hash_key, host, context)
end
# TODO: evalulate _mgr_state_unknown to determine if the host should be
# dead
end
#
# This method reports a host's service state.
#
def report_service_state(mod, host, proto, port, state, context = nil)
# If the supplied host object has not yet been registered, do so now.
# This occurs when a service module happens to discover a host that was
# not originally thought to have existed
if (host.needs_register != false and
state == ServiceState::Up)
new_host(host_hash_key(host.address), host, context)
end
# Define the service entity name
ename = "port_#{port}"
# Get the proto subcontainer for this entity, or add it if it isn't
# already defined.
p = host.services.add_entity_subcontainer(proto.downcase)
# Now that we have the protocol subcontainer, get the service instance
# associated witht he port (if one has been defined).
if (service = p.get_entity(ename))
# TODO: update
elsif (state == ServiceState::Up)
service = Recon::Entity::Service.new(proto, port)
p.add_entity("port_#{ename}", service)
# TODO: Notify
end
end
#
# This method returns the host object associated with the supplied address.
# If one does not exist, it is created.
#
def get_host(address)
host_hash[host_hash_key(address)]
end
#
# This method iterates the host hash calling the supplied block with the
# address and host instances of each entry in the host hash.
#
def each_host(&block)
host_hash.each_pair { |k, host|
block.call(host.address, host)
}
end
protected
#
# This method returns the hash key to use with the supplied address.
#
# TODO: make this use comm info
#
def host_hash_key(address)
address
end
#
# Called when a new host is detected.
#
def new_host(hash_key, host, context)
# Extend the host and initialize it properly
host.extend(ExtendedHostState)
host._mgr_state_unknown = 0
# Add the host tot he host hash
host_hash_mutex.synchronize {
self.host_hash[hash_key] = host
}
ilog("recon: New host discoverered: #{host.pretty}", "core",
LEV_1)
# Now that the host has registered, we can't flag it up.
host.needs_register = false
# Notify any host event subscribes of our new found fate.
framework.events.on_host_changed(
context, host, ReconEvent::EntityChangeType::Add)
end
#
# Processes cleanup necessary when a dead host is encountered.
#
def dead_host(host, context)
host_hash_mutex.synchronize {
self.host_hash.delete(host)
}
# Notify any host event subscribers that the host has died.
framework.events.on_host_changed(
context, host, ReconEvent::EntityChangeType::Remove)
end
attr_accessor :host_hash
attr_accessor :host_hash_mutex
end
end