220 lines
4.7 KiB
Ruby
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
|