# A service, such as an ssh server or web server, running on a {#host}. class Mdm::Service < ActiveRecord::Base include Metasploit::Model::Search # # CONSTANTS # # Valid values for {#proto}. PROTOS = %w{tcp udp} # Valid values for {#state}. STATES = ['open', 'closed', 'filtered', 'unknown'] # # Associations # # @!attribute creds # Credentials gathered from this service. # # @return [ActiveRecord::Relation] has_many :creds, class_name: 'Mdm::Cred', dependent: :destroy, inverse_of: :service # @!attribute exploit_attempts # Exploit attempts against this service. # # @return [ActiveRecord::Relation] has_many :exploit_attempts, class_name: 'Mdm::ExploitAttempt', dependent: :destroy, inverse_of: :service # @!attribute exploited_hosts # @todo MSP-2732 # @return [Array] has_many :exploited_hosts, class_name: 'Mdm::ExploitedHost', dependent: :destroy, inverse_of: :service # @!attribute host # The host on which this service runs. # # @return [Mdm::Host] belongs_to :host, class_name: 'Mdm::Host', counter_cache: :service_count, inverse_of: :services # @!attribute loots # Loot gathers from this service. # # @return [ActiveRecord::Relation] has_many :loots, class_name: 'Mdm::Loot', dependent: :destroy, inverse_of: :service # @!attribute notes # Notes about this service. # # @return [ActiveRecord::Relation] has_many :notes, class_name: 'Mdm::Note', dependent: :destroy, inverse_of: :service # @!attribute [rw] task_services # Details about what Tasks touched this service # # @return [Array] has_many :task_services, class_name: 'Mdm::TaskService', dependent: :destroy, inverse_of: :service # @!attribute vulns # Vulnerabilities found in this service. # # @return [ActiveRecord::Relation] has_many :vulns, class_name: 'Mdm::Vuln', dependent: :destroy, inverse_of: :service # @!attribute web_sites # Web sites running on top of this service. # # @return [ActiveRecord::Relation] has_many :web_sites, class_name: 'Mdm::WebSite', dependent: :destroy, inverse_of: :service # # through: :task_services # # @!attribute [rw] tasks # Tasks that touched this service # # @return [Array] has_many :tasks, :through => :task_services, :class_name => 'Mdm::Task' # # Through :web_sites # # @!attribute [r] web_pages # Web pages in the {#web_sites} on top of this service. # # @return [Array] has_many :web_pages, :through => :web_sites, :class_name => 'Mdm::WebPage' # @!attribute [r] web_forms # Form in the {#web_sites} on top of this service. # # @return [Array] has_many :web_forms, :through => :web_sites, :class_name => 'Mdm::WebForm' # @!attribute [r] web_vulns # Vulnerabilities found in the {#web_sites} on top of this service. # # @return [Array] has_many :web_vulns, :through => :web_sites, :class_name => 'Mdm::WebVuln' # # Attributes # # @!attribute [rw] info # Additional information about the service that does not fit in the {#name} or {#proto}. # # @return [String] # @!attribute [rw] port # The port on which this service runs on the {#host}. # # @return [Integer] # @!attribute [rw] name # The name of the service. # # @return [String] # @!attribute [rw] proto # The protocol used by this service # # @return [String] # @!attribute [rw] state # Whether this service is opened, closed, filtered, or in an unknown state. # # @return [String] element of {STATES}. # # Callbacks # after_save :normalize_host_os # # Scopes # scope :inactive, -> { where("services.state != 'open'") } scope :with_state, lambda { |a_state| where("services.state = ?", a_state)} scope :search, lambda { |*args| where([ "services.name ILIKE ? OR " + "services.info ILIKE ? OR " + "services.proto ILIKE ? OR " + "services.port = ? ", "%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%", (args[0].to_i > 0) ? args[0].to_i : 99999 ]) } # # # Search # # # # Search Associations # search_associations host: :tags # # Search Attributes # search_attribute :info, type: :string search_attribute :name, type: :string search_attribute :proto, type: { set: :string } # # Search Withs # search_with MetasploitDataModels::Search::Operator::Port::List # # Validations # validates :port, numericality: { only_integer: true } validates :port, uniqueness: { message: 'already exists on this host and protocol', scope: [ :host_id, :proto ] } validates :proto, inclusion: { in: PROTOS } # # Class Methods # # Set of searchable values for {#proto}. # # @return [Set] {PROTOS} as a `Set`. # @see Metasploit::Model::Search::Operation::Set#membership # @see Metasploit::Model::Search::Operator::Attribute#attribute_set def self.proto_set @proto_set ||= Set.new(PROTOS) end # # Instance Methods # # {Mdm::Host::OperatingSystemNormalization#normalize_os Normalizes the host operating system} whenever {#info} has # changed. # # @return [void] def normalize_host_os if info_changed? host.normalize_os end end Metasploit::Concern.run(self) end