# Workspace to separate different collections of {#hosts}. Can be used to separate pentests against different networks # or different clients as reports are normally generated against all records in a workspace. class Mdm::Workspace < ApplicationRecord # # CONSTANTS # DEFAULT = 'default' # # Associations # # Automatic exploitation runs against this workspace. has_many :automatic_exploitation_runs, class_name: 'MetasploitDataModels::AutomaticExploitation::Run', inverse_of: :workspace # Automatic exploitation match sets generated against {#hosts} and {#services} in this workspace. has_many :automatic_exploitation_match_sets, class_name: 'MetasploitDataModels::AutomaticExploitation:MatchSet', inverse_of: :workspace # @deprecated Use `Mdm::Workspace#core_credentials` defined by `Metasploit::Credential::Engine` to get # `Metasploit::Credential::Core`s gathered from this workspace's {#hosts} and {#services}. # # Creds gathered from this workspace's {#hosts} and {#services}. has_many :creds, :through => :services, :class_name => 'Mdm::Cred' # Events that occurred in this workspace. has_many :events, dependent: :delete_all, :class_name => 'Mdm::Event' # Hosts in this workspace. has_many :hosts, :dependent => :destroy, :class_name => 'Mdm::Host' # Listeners running for this workspace. has_many :listeners, :dependent => :destroy, :class_name => 'Mdm::Listener' # Notes about this workspace. has_many :notes, :class_name => 'Mdm::Note' # User that owns this workspace and has full permissions within this workspace even if they are not an # {Mdm::User#admin administrator}. belongs_to :owner, :class_name => 'Mdm::User', :foreign_key => 'owner_id' # Tasks run inside this workspace. has_many :tasks, -> { order('created_at DESC') }, class_name: 'Mdm::Task', dependent: :destroy # Users that are allowed to use this workspace. Does not necessarily include all users, as an {Mdm::User#admin # administrator} can access any workspace, even ones where they are not a member. has_and_belongs_to_many :users, -> { distinct }, class_name: 'Mdm::User', join_table: 'workspace_members' # # through: :hosts # # Social engineering campaign or browser autopwn clients from {#hosts} in this workspace. has_many :clients, :through => :hosts, :class_name => 'Mdm::Client' # Hosts exploited in this workspace. has_many :exploited_hosts, :through => :hosts, :class_name => 'Mdm::ExploitedHost' # Loot gathered from {#hosts} in this workspace. has_many :loots, :through => :hosts, :class_name => 'Mdm::Loot' # Services running on {#hosts} in this workspace. has_many :services, class_name: 'Mdm::Service', foreign_key: :service_id, through: :hosts # Vulnerabilities found on {#hosts} in this workspace. has_many :vulns, :through => :hosts, :class_name => 'Mdm::Vuln' # Sessions opened on {#hosts} in this workspace. has_many :sessions, :through => :hosts, :class_name => 'Mdm::Session' # # Attributes # # @!attribute boundary # Comma separated list of IP ranges (in various formats) and IP addresses that users of this workspace are allowed # to interact with if {#limit_to_network} is `true`. # # @return [String] # @!attribute description # Long description (beyond {#name}) that explains the purpose of this workspace. # # @return [String] # @!attribute limit_to_network # Whether {#boundary} is respected. # # @return [false] do not limit interactions to {#boundary}. # @return [true] limit interactions to {#boundary}. # @!attribute name # Name of this workspace. # # @return [String] # @!attribute created_at # When this workspace was created. # # @return [DateTime] # @!attribute updated_at # The last time this workspace was updated. # # @return [DateTime] # # Callbacks # before_save :normalize # # Validations # validates :name, :presence => true, :uniqueness => true, :length => {:maximum => 255} validates :description, :length => {:maximum => 4096} # # Instance Methods # # @deprecated Use `Mdm::Workspace#credential_cores` when `Metasploit::Credential::Engine` is installed to get # `Metasploit::Credential::Core`s. Use `Mdm::Service#logins` when `Metasploit::Credential::Engine` is installed to # get `Metasploit::Credential::Login`s. # # @return [ActiveRecord::Relation] def creds Mdm::Cred .joins(service: :host) .where(hosts: { workspace_id: self.id }) end # Returns default {Mdm::Workspace}. # # @return [Mdm::Workspace] def self.default where(name: DEFAULT).first_or_create end # Whether this is the {default} workspace. # # @return [true] if this is the {default} workspace. # @return [false] if this is not the {default} workspace. def default? name == DEFAULT end # @deprecated Use `workspace.credential_cores.each` when `Metasploit::Credential::Engine` is installed to enumerate # `Metasploit::Credential::Core`s. Use `service.logins.each` when `Metasploit::Credential::Engine` is installed to # enumerate `Metasploit::Credential::Login`s. # # Enumerates each element of {#creds}. # # @yield [cred] # @yieldparam cred [Mdm::Cred] Cred associated with {#hosts a host} or {#services a service} in this workspace. # @yieldreturn [void] # @return [void] def each_cred(&block) creds.each do |cred| block.call(cred) end end # Enumerates each element of {#host_tags}. # # @yield [tag] # @yieldparam tag [Mdm::Tag] a tag on {#hosts}. # @yieldreturn [void] # @return [void] def each_host_tag(&block) host_tags.each do |host_tag| block.call(host_tag) end end # Tags on {#hosts}. # # @return [ActiveRecord::Relation] def host_tags Mdm::Tag .joins(:hosts) .where(hosts: { workspace_id: self.id }) end # Web forms found on {#web_sites}. # # @return [ActiveRecord::Relation] def web_forms Mdm::WebForm.joins( Mdm::WebForm.join_association(:web_site), Mdm::WebSite.join_association(:service), Mdm::Service.join_association(:host), Mdm::Host.join_association(:workspace) ).where(Mdm::Workspace[:id].eq(id)).distinct end # Web pages found on {#web_sites}. # # @return [ActiveRecord::Relation] def web_pages Mdm::WebPage.joins( Mdm::WebPage.join_association(:web_site), Mdm::WebSite.join_association(:service), Mdm::Service.join_association(:host), Mdm::Host.join_association(:workspace) ).where(Mdm::Workspace[:id].eq(id)).distinct end # Web sites running on {#services}. # # @return [ActiveRecord::Relation] def web_sites Mdm::WebSite.joins( Mdm::WebSite.join_association(:service), Mdm::Service.join_association(:host), Mdm::Host.join_association(:workspace) ).where(Mdm::Workspace[:id].eq(id)).distinct end # Web vulnerability found on {#web_sites}. # # @return [ActiveRecord::Relation] def web_vulns Mdm::WebVuln.joins( Mdm::WebVuln.join_association(:web_site), Mdm::WebSite.join_association(:service), Mdm::Service.join_association(:host), Mdm::Host.join_association(:workspace) ).where(Mdm::Workspace[:id].eq(id)).distinct end # Web forms on {#web_sites}. # # @return [ActiveRecord::Relation] def unique_web_forms web_forms.select('web_forms.id, web_forms.web_site_id, web_forms.path, web_forms.method, web_forms.query') end # {#unique_web_forms} hosted on `addrs`. # # @param addrs [Array] {Mdm::Host#address} for the {Mdm::Service#host} for the {Mdm::WebSite#service} # for the {Mdm::WebForm#web_site}. # @return [Array] def web_unique_forms(addrs=nil) forms = unique_web_forms if addrs forms.to_a.reject!{|f| not addrs.include?( f.web_site.service.host.address.to_s ) } end forms end private # Strips {#boundary}. # # @return [void] def normalize boundary.strip! if boundary end public Metasploit::Concern.run(self) end