1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-11-05 14:57:30 +01:00

basic workings of auto-recon

git-svn-id: file:///home/svn/incoming/trunk@2987 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Matt Miller 2005-10-31 15:56:59 +00:00
parent 1adc00a17d
commit 27b8f880ff
13 changed files with 431 additions and 155 deletions

View File

@ -105,9 +105,9 @@ Things that would be useful to have completed, but not a requirement:
- framework-core
- handler sharing
- exploits using the same payload/handler can share (ref count)
- framework-base
- event correlation
- recon events correlations
X - framework-base
X - event correlation
X - recon events correlations
- user interfaces
- msfd
- daemon interface, provides command line interaction and proxying

View File

@ -165,7 +165,9 @@ class EventDispatcher
def on_host_changed(context, host, change_type)
subscribers_rwlock.synchronize_read {
recon_event_subscribers.each { |subscriber|
next if (subscriber.include?(Msf::ReconEvent::HostSubscriber) == false)
cls = (subscriber.kind_of?(Class)) ? subscriber : subscriber.class
next if (cls.include?(Msf::ReconEvent::HostSubscriber) == false)
subscriber.on_host_changed(context, host, change_type)
}
@ -180,7 +182,9 @@ class EventDispatcher
def on_service_changed(context, host, service, change_type)
subscribers_rwlock.synchronize_read {
recon_event_subscribers.each { |subscriber|
next if (subscriber.include?(Msf::ReconEvent::ServiceSubscriber) == false)
cls = (subscriber.kind_of?(Class)) ? subscriber : subscriber.class
next if (cls.include?(Msf::ReconEvent::ServiceSubscriber) == false)
subscriber.on_service_changed(context, host, service, change_type)
}

View File

@ -171,16 +171,12 @@ protected
else
self[name] = dup
end
# Automatically subscribe a wrapper around this module to the necessary
# event providers based on whatever events it wishes to receive.
auto_subscribe_module(dup)
# Notify the framework that a module was loaded
framework.events.on_module_load(name, dup)
# Invalidate the sorted array
invalidate_cache
# Return the duplicated instance for use
dup
end
#
@ -191,50 +187,6 @@ protected
mod_ranked = nil
end
#
# This method automatically subscribes a module to whatever event providers
# it wishes to monitor. This can be used to allow modules to automatically
# execute or perform other tasks when certain events occur. For instance,
# when a new host is detected, other recon modules may wish to run such
# that they can collect more information about the host that was detected.
#
def auto_subscribe_module(mod)
# If auto-subscribe has been disabled
if (framework.datastore['DisableAutoSubscribe'] and
framework.datastore['DisableAutoSubscribe'] =~ /^(y|1|t)/)
return
end
# If auto-subscription is enabled (which it is by default), figure out
# if it subscribes to any particular interfaces.
#
# Recon event subscriber check
#
[
Msf::ReconEvent::HostSubscriber,
Msf::ReconEvent::ServiceSubscriber,
].each { |iface|
if (mod.include?(iface))
framework.events.add_recon_subscriber(mod)
end
}
#
# Exploit event subscriber check
#
if (mod.include?(ExploitEvent))
framework.events.add_exploit_subscriber(mod)
end
#
# Session event subscriber check
#
if (mod.include?(SessionEvent))
framework.events.add_session_subscriber(mod)
end
end
attr_writer :module_type
attr_accessor :mod_arch_hash, :mod_platform_hash
attr_accessor :mod_sorted, :mod_ranked
@ -414,6 +366,25 @@ class ModuleManager < ModuleSet
mod
end
#
# Overrides the module set method for adding a module so that some extra
# steps can be taken to subscribe the module and notify the event
# dispatcher.
#
def add_module(mod, name, file_path = nil)
# Call the module set implementation of add_module
dup = super
# Automatically subscribe a wrapper around this module to the necessary
# event providers based on whatever events it wishes to receive. We
# only do this if we are the module manager instance, as individual
# module sets need not subscribe.
auto_subscribe_module(dup)
# Notify the framework that a module was loaded
framework.events.on_module_load(name, dup)
end
protected
#
@ -628,6 +599,51 @@ protected
module_sets[type].add_module(mod, name, file_path)
end
#
# This method automatically subscribes a module to whatever event providers
# it wishes to monitor. This can be used to allow modules to automatically
# execute or perform other tasks when certain events occur. For instance,
# when a new host is detected, other recon modules may wish to run such
# that they can collect more information about the host that was detected.
#
def auto_subscribe_module(mod)
# If auto-subscribe has been disabled
if (framework.datastore['DisableAutoSubscribe'] and
framework.datastore['DisableAutoSubscribe'] =~ /^(y|1|t)/)
return
end
# If auto-subscription is enabled (which it is by default), figure out
# if it subscribes to any particular interfaces.
inst = nil
#
# Recon event subscriber check
#
[
Msf::ReconEvent::HostSubscriber,
Msf::ReconEvent::ServiceSubscriber,
].each { |iface|
if (mod.include?(iface) == true)
framework.events.add_recon_subscriber((inst) ? inst : (inst = mod.new))
end
}
#
# Exploit event subscriber check
#
if (mod.include?(ExploitEvent) == true)
framework.events.add_exploit_subscriber((inst) ? inst : (inst = mod.new))
end
#
# Session event subscriber check
#
if (mod.include?(SessionEvent) == true)
framework.events.add_session_subscriber((inst) ? inst : (inst = mod.new))
end
end
attr_accessor :modules, :module_sets
attr_accessor :module_paths
attr_accessor :module_history, :module_history_mtime

View File

@ -34,7 +34,7 @@ module ReconEvent
# This routine is called when a change is made to a host, such as it being
# added, modified, or removed.
#
def self.on_host_changed(context, host, change_type)
def on_host_changed(context, host, change_type)
case change_type
when EntityChangeType::Add
on_new_host(context, host)
@ -45,65 +45,30 @@ module ReconEvent
end
end
#
# Calls the class method.
#
def on_host_changed(context, host, change_type)
self.class.on_host_changed(context, host, change_type)
end
#
# This routine is called whenever a new host is found.
#
def self.on_new_host(context, host)
end
#
# Calls the class methods.
#
def on_new_host(context, host)
self.class.on_new_host(context, host)
end
#
# This routine is called whenever a change is made to an existing
# host.
#
def self.on_updated_host(context, host)
end
#
# Calls the class method.
#
def on_updated_host(context, host)
self.class.on_updated_host(context, host)
end
#
# Called when a host is considered to be dead after having
# previously been valid.
#
def self.on_dead_host(context, host)
end
#
# Calls the class method.
#
def on_dead_host(context, host)
self.class.on_dead_host(context, host)
end
#
# This routine is called whenever a host attribute is found.
#
def self.on_new_host_attribute(context, host, attribute)
end
#
# Calls the class method.
#
def on_new_host_attribute(context, host, attribute)
self.class.on_new_host_attribute(context, host, attribute)
end
end
@ -123,7 +88,7 @@ module ReconEvent
# This routine is called when a change is made to a service, such as it being
# added, modified, or removed.
#
def self.on_service_changed(context, host, service, change_type)
def on_service_changed(context, host, service, change_type)
case change_type
when EntityChangeType::Add
on_new_service(context, host, service)
@ -134,67 +99,32 @@ module ReconEvent
end
end
#
# Calls the class method.
#
def on_service_changed(context, host, service, change_type)
self.class.on_service_changed(context, host, service, change_type)
end
#
# This routine is called whenever a new service is found.
#
def self.on_new_service(context, host, service)
end
#
# Calls the class method.
#
def on_new_service(context, host, service)
self.class.on_new_service(context, host, service)
end
#
# This routine is called whenever a change is made to an existing
# service.
#
def self.on_updated_service(context, host, service)
end
#
# Calls the class method.
#
def on_updated_service(context, host, service)
self.class.on_updated_service(context, host, service)
end
#
# Called when a service is considered to be dead after having
# previously been valid.
#
def self.on_dead_service(context, host, service)
end
#
# Calls the class method.
#
def on_dead_service(context, host, service)
self.class.on_dead_service(context, host, service)
end
#
# This routine is called whenever a service attribute is found.
#
def self.on_new_service_attribute(context, host, service, attribute)
def on_new_service_attribute(context, host, service, attribute)
end
#
# Calls the class method.
#
def on_new_service_attribute(context, host, service)
self.class.on_new_service_attribute(context, host, service)
end
end

View File

@ -28,6 +28,20 @@ module Container
self._attr_hash = Hash.new
end
#
# Wraps get_attribute.
#
def [](key)
get_attribute(key)
end
#
# Wraps set_attribute.
#
def []=(key, val)
set_attribute(key, val)
end
#
# Sets the value of an attribute with the supplied name.
#

View File

@ -55,6 +55,7 @@ class Discoverer < Msf::Recon
end
require 'msf/core/recon/discoverer/host'
require 'msf/core/recon/discoverer/service'
def initialize(info = {})
super
@ -258,7 +259,7 @@ protected
if (discovery_threads.length == 0)
# Call the discovery complete method
discovery_complete(false)
discovery_thread_event.set
end
}

View File

@ -0,0 +1,96 @@
module Msf
class Recon
class Discoverer
###
#
# Service
# -------
#
# This class provides a base class for all recon modules that attempt to
# discover the presence of a service on a host.
#
###
class Service < Msf::Recon::Discoverer
def initialize(info = {})
super
# Register the options that this particular discovery module uses
register_options(
[
Opt::RHOST
], Msf::Recon::Discoverer::Service)
end
#
# This method returns that this is a service discoverer recon module.
#
def discoverer_type
Type::Service
end
#
# By default, service discoverer recon modules do no support
# multi-threading.
#
def discoverer_flags
0
end
#
# Probes a host entity to see what services it has open. Extended modules
# should report service state changes directly via the report_service_state
# instance method.
#
def probe_host(host)
end
protected
#
# Wraps the probing of a host.
#
def discovery_thread
if ((host = framework.reconmgr.get_host(datastore['RHOST'])) == nil)
host = Msf::Recon::Entity::Host.new(datastore['RHOST'])
end
probe_host(host)
end
#
# This method reports the state of a service to the recon manager so that
# it can be tracked appropriately.
#
def report_service_state(host, proto, port, istate)
state = istate
context = nil
# If the state passed in as an argument is a hash, then create an event
# context that we'll pass along to the recon manager in case other
# subscribers might be able to make use of it.
if (istate.kind_of?(Hash))
context = Msf::Recon::EventContext.new
context.from_hash(istate)
state = istate['state']
end
# Log that we detected an up service
if (state == ServiceState::Up)
dlog("Found port #{port} (#{proto}) open on #{host.address}.", "core",
LEV_2)
end
# Pass the normalized service state notifications to the recon manager.
framework.reconmgr.report_service_state(self, host, proto, port,
state, context)
end
end
end
end
end

View File

@ -50,6 +50,8 @@ class Entity
'unknown'
end
attr_accessor :needs_register
end
end

View File

@ -2,6 +2,9 @@ module Msf
class Recon
class Entity
class Group
end
###
#
# Container
@ -17,20 +20,14 @@ module Container
# Initializes the array of entities.
#
def initialize_entities
self._entity_list = Array.new
self._entity_sub_containers = Hash.new
self._entity_hash = Hash.new
end
#
# This routine adds a sub-container of entities to this entity container.
#
def add_entity_subcontainer(name, container)
self._entity_sub_containers[name] = container
instance_eval("
def #{name}
_entity_sub_containers['#{name}']
end")
def add_entity_subcontainer(name, container = Group.new)
add_entity(name, container)
end
#
@ -43,31 +40,47 @@ module Container
#
# Adds an entity to the container.
#
def add_entity(entity)
self._entity_list << entity
end
def add_entity(name, entity)
self._entity_hash[name] = entity
def delete_entity(entity)
self._entity_list.delete(entity)
if (respond_to?(name) == false)
instance_eval("
def #{name}
_entity_hash[#{name}]
end
")
end
entity
end
#
# Returns the list of entities to the caller.
# Returns the entity associated with the supplied name.
#
def get_entity(name)
_entity_hash[name]
end
#
# Removes an entity from the hash of entities.
#
def delete_entity(entity)
self._entity_hash.delete(entity)
end
#
# Returns the hash of entities to the caller.
#
def entities
_entity_list
_entity_hash
end
protected
#
# The protected entity list itself.
# The protected entity hash itself.
#
attr_accessor :_entity_list
#
# The hash of entity sub-containers.
#
attr_accessor :_entity_sub_containers
attr_accessor :_entity_hash
end

View File

@ -49,7 +49,7 @@ class Host < Entity
# Holds the address of the host that this entity instance is associated
# with.
self.address = address;
self.address = address
# Add an attribute group that will contain system information for this
# host.

View File

@ -0,0 +1,53 @@
module Msf
class Recon
class Entity
###
#
# Service
# -------
#
# This class represents a logical service entity. Services symbolize remote
# functionality provided by a host by means of some network-based protocol,
# such as TCP over IP. Information about a service, such as its protocol,
# port, banner, and other information is conveyed through attributes of the
# service entity.
#
###
class Service < Entity
def initialize(proto, port = nil)
super()
#
# Initialize the local attributes
#
self.proto = proto
self.port = port
end
#
# This method returns a pretty string representation of the service.
#
def pretty
"#{port} (#{proto})"
end
#
# The protocol this service is using, such as 'tcp'.
#
attr_reader :proto
#
# The port this service is listening on, if applicable.
#
attr_reader :port
protected
attr_writer :proto, :port # :nodoc:
end
end
end
end

View File

@ -26,6 +26,30 @@ module HostState
Unknown = "unknown"
end
###
#
# ServiceState
# ---------
#
# 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
###
#
# ReconManager
@ -76,7 +100,7 @@ class ReconManager
# 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 = address
hash_key = host_hash_key(address)
# If a host already exists with this information, then check to see what
# status we received.
@ -99,8 +123,58 @@ class ReconManager
# 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
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.
#
@ -118,6 +192,9 @@ protected
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)

View File

@ -0,0 +1,70 @@
module Msf
###
#
# Nmap
# ----
#
# This recon modules uses nmap to detect the services that are running on a
# given host.
#
###
class Recon::Service::PortScanner::Nmap < Msf::Recon::Discoverer::Service
def initialize(info = {})
super(merge_info(info,
'Name' => 'Nmap port scanner',
'Description' => %q{
This module uses nmap to detect the services that are running
on a given host.
},
'Author' => 'skape',
'Version' => '$Revision$'))
end
def probe_host(host)
# If we are running as root, use nmap to do a SYN scan
if (Process.euid == 0)
cmd = "nmap -sS #{host.address}"
# Otherwise, if we're non-root, use the standard tcp connect() scan
else
cmd = "nmap -sT #{host.address}"
end
# Fire it off...
p = IO.popen(cmd)
begin
# Read each line, extracting the ones that contain open port
# information
while (buf = p.gets)
if (buf =~ /^(\d+)\/(tcp|udp)\s+open/i)
report_service_state(host, $2, $1, ServiceState::Up)
end
end
ensure
p.close
end
end
##
#
# Automated launching
#
##
include Msf::ReconEvent::HostSubscriber
#
# This method is automatically called when a new host is found.
#
def on_new_host(context, host)
# TODO: check if auto-service-probe should be enabled
# TODO: precedence of who should be able to search for services
# TODO: evasion qualifications
probe_host(host)
end
end
end