Fixes #2444. Adds an ExploitedHost table, adds the db_exploited command, adds the report_exploit() function.

Tested with meterpreter, shell, and clientside exploit sessions. 


git-svn-id: file:///home/svn/framework3/trunk@10130 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Tod Beardsley 2010-08-24 21:57:04 +00:00
parent 8741179e8a
commit 1db9d8eb01
9 changed files with 178 additions and 4 deletions

View File

@ -0,0 +1,16 @@
class AddExploitedTable < ActiveRecord::Migration
def self.up
create_table :exploited_hosts do |t|
t.integer :host_id, :null => false
t.integer :service_id
t.string :session_uuid, :limit => 8
t.string :name, :limit => 2048
t.string :payload, :limit => 2048
t.timestamps
end
end
def self.down
drop_table :exploited_hosts
end
end

View File

@ -453,6 +453,13 @@ class DBManager
)
end
#
# This method returns a list of all exploited hosts in the database.
#
def exploited_hosts(wspace=workspace)
wspace.exploited_hosts
end
#
# This method iterates the notes table calling the supplied block with the
# note instance of each entry.
@ -685,6 +692,12 @@ class DBManager
end
end
def each_exploited_host(wspace=workspace,&block)
wspace.exploited_hosts.each do |eh|
block.call(eh)
end
end
#
# Find or create a vuln matching this service/name
#
@ -802,6 +815,70 @@ class DBManager
Ref.find_by_name(name)
end
def report_exploit(opts={})
return if not active
raise ArgumentError.new("Missing required option :host") if opts[:host].nil?
wait = opts[:wait]
wspace = opts.delete(:workspace) || workspace
host = nil
addr = nil
sname = opts.delete(:sname)
port = opts.delete(:port)
proto = opts.delete(:proto) || "tcp"
name = opts.delete(:name)
payload = opts.delete(:payload)
session_uuid = opts.delete(:session_uuid)
if opts[:host].kind_of? Host
host = opts[:host]
else
report_host({:workspace => wspace, :host => opts[:host]})
addr = opts[:host]
end
if opts[:service].kind_of? Service
service = opts[:service]
elsif port
report_service(:host => host, :port => port, :proto => proto, :name => sname)
service = get_service(wspace, host, proto, port)
else
service = nil
end
ret = {}
task = queue(
Proc.new {
if host
host.updated_at = host.created_at
host.state = HostState::Alive
host.save!
else
host = get_host(:workspace => wspace, :address => addr)
end
exploit_info = {
:workspace => wspace,
:host_id => host.id,
:name => name,
:payload => payload,
}
exploit_info[:service_id] = service.id if service
exploit_info[:session_uuid] = session_uuid if session_uuid
exploit_record = ExploitedHost.create(exploit_info)
exploit_record.save!
ret[:exploit] = exploit_record
}
)
if wait
return nil if task.wait() != :done
return ret[:exploit]
end
return task
end
#
# Deletes a host and associated data matching this address/comm

View File

@ -318,14 +318,32 @@ class FrameworkEventSubscriber
# If the exploit used was multi/handler, though, we don't know what
# it's vulnerable to, so it isn't really useful to save it.
if session.via_exploit and session.via_exploit != "exploit/multi/handler"
wspace = framework.db.find_workspace(session.workspace)
host = wspace.hosts.find_by_address(address)
port = session.exploit_datastore["RPORT"]
service = (port ? host.services.find_by_port(port) : nil)
mod = framework.modules.create(session.via_exploit)
info = {
:host => address,
vuln_info = {
:host => host.address,
:name => session.via_exploit,
:refs => mod.references,
:workspace => framework.db.find_workspace(session.workspace)
:workspace => wspace
}
framework.db.report_vuln(info)
framework.db.report_vuln(vuln_info)
# Exploit info is like vuln info, except it's /just/ for storing
# successful exploits in an unserialized way. Yes, there is
# duplication, but it makes exporting a score card about a
# million times easier. TODO: See if vuln/exploit can get fixed up
# to one useful table.
exploit_info = {
:name => session.via_exploit,
:payload => session.via_payload,
:workspace => wspace,
:host => host,
:service => service,
:session_uuid => session.uuid
}
ret = framework.db.report_exploit(exploit_info)
end
end
end

View File

@ -13,6 +13,7 @@ require 'msf/core/model/service'
require 'msf/core/model/workspace'
require 'msf/core/model/vuln'
require 'msf/core/model/cred'
require 'msf/core/model/exploited_host'
require 'msf/core/model/wmap_target'
require 'msf/core/model/wmap_request'

View File

@ -0,0 +1,12 @@
module Msf
class DBManager
class ExploitedHost < ActiveRecord::Base
include DBSave
belongs_to :host
belongs_to :service
belongs_to :workspace
end
end
end

View File

@ -13,6 +13,7 @@ class Host < ActiveRecord::Base
has_many :service_notes, :through => :services
has_many :creds, :through => :services
has_many :exploited_hosts, :dependent => :destroy
validates_exclusion_of :address, :in => ['127.0.0.1']
validates_uniqueness_of :address, :scope => :workspace_id

View File

@ -6,6 +6,7 @@ class Service < ActiveRecord::Base
has_many :vulns, :dependent => :destroy
has_many :notes, :dependent => :destroy
has_many :creds, :dependent => :destroy
has_many :exploited_hosts, :dependent => :destroy
belongs_to :host
serialize :info

View File

@ -17,6 +17,7 @@ class Workspace < ActiveRecord::Base
has_many :clients, :through => :hosts
has_many :vulns, :through => :hosts
has_many :creds, :dependent => :destroy
has_many :exploited_hosts, :through => :hosts
#has_many :notes, :through => :hosts

View File

@ -53,6 +53,7 @@ class Db
"db_vulns" => "List all vulnerabilities in the database",
"db_notes" => "List all notes in the database",
"db_creds" => "List all credentials in the database",
"db_exploited" => "List all exploited hosts in the database",
"db_add_host" => "Add one or more hosts to the database",
"db_add_port" => "Add a port to a host",
"db_add_note" => "Add a note to a host",
@ -425,6 +426,52 @@ class Db
print_status "Found #{creds_returned} credential#{creds_returned == 1 ? "" : "s"}."
end
# Returns exploited hosts. Takes a similiar set of options as db_creds
def cmd_db_exploited(*args)
return unless active?
if args.size > 1
print_status "Usage: db_exploited [host=1.2.3.4/24|port=1-1024|service=ssh,smb,etc]"
print_status " Note, only one of host, port, or service can be used at a time."
return
end
search_term = nil
search_param = nil
exploited_returned = 0
if args[0] =~ /^[\s]*(host|port|service)=(.*)/i
search_term = $1.downcase
search_param = $2.downcase
end
framework.db.each_exploited_host(framework.db.workspace) do |eh|
case search_term
when "host"
begin
rw = Rex::Socket::RangeWalker.new(search_param)
next unless rw.include? eh.host.address
rescue
print_error "Invalid host parameter."
break
end
when "port"
if search_param =~ /([0-9]+)-([0-9]+)/
ports = Range.new($1,$2)
else
ports = Range.new(search_param,search_param)
end
next unless ports.include? eh.service.port.to_s
when "service"
svcs = search_param.split(/[\s]*,[\s]*/)
next unless svcs.include? eh.service.name
end
if eh.service
print_status("Time: #{eh.updated_at} Host Info: host=#{eh.host.address} port=#{eh.service.port} proto=#{eh.service.proto} sname=#{eh.service.name} exploit=#{eh.name}")
else
print_status("Time: #{eh.updated_at} Host Info: host=#{eh.host.address} exploit=#{eh.name}")
end
exploited_returned += 1
end
print_status "Found #{exploited_returned} exploited host#{exploited_returned == 1 ? "" : "s"}."
end
def cmd_db_notes(*args)
return unless active?
hosts = nil