From 206e3441caa46fc44d940cff15da6fbc475d8c65 Mon Sep 17 00:00:00 2001 From: sjanusz-r7 Date: Tue, 19 May 2026 13:48:21 +0100 Subject: [PATCH 1/3] Add reporting of parent services when creating credential service --- lib/metasploit/credential/creation.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/metasploit/credential/creation.rb b/lib/metasploit/credential/creation.rb index ecfd460..b001df5 100644 --- a/lib/metasploit/credential/creation.rb +++ b/lib/metasploit/credential/creation.rb @@ -617,9 +617,12 @@ def create_credential_service(opts={}) service_name = opts.fetch(:service_name) protocol = opts.fetch(:protocol) workspace_id = opts.fetch(:workspace_id) + parents = opts[:parents] || [] + resource = opts[:resource] host_object = Mdm::Host.where(address: address, workspace_id: workspace_id).first_or_create - service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol, name: service_name).first_or_initialize + service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol, name: service_name, resource: resource).first_or_initialize + service_object.parents = parents service_object.state = "open" service_object.save! From 0aa86f186589f1a772dbff58518a138151c0a088 Mon Sep 17 00:00:00 2001 From: sjanusz-r7 Date: Wed, 20 May 2026 17:39:45 +0100 Subject: [PATCH 2/3] copy Metasploit Framework method for parent services creation --- lib/metasploit/credential/creation.rb | 63 +++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/lib/metasploit/credential/creation.rb b/lib/metasploit/credential/creation.rb index b001df5..945acc4 100644 --- a/lib/metasploit/credential/creation.rb +++ b/lib/metasploit/credential/creation.rb @@ -617,19 +617,74 @@ def create_credential_service(opts={}) service_name = opts.fetch(:service_name) protocol = opts.fetch(:protocol) workspace_id = opts.fetch(:workspace_id) - parents = opts[:parents] || [] - resource = opts[:resource] + resource = opts[:resource] || {} host_object = Mdm::Host.where(address: address, workspace_id: workspace_id).first_or_create - service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol, name: service_name, resource: resource).first_or_initialize - service_object.parents = parents + service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol, name: service_name).first_or_initialize + parents = process_service_chain(host_object, opts.delete(:parents)) if opts[:parents] + if parents + parents.each do |parent| + service_object.parents << parent if parent && !service_object.parents.include?(parent) + end + end + + service_object.resource = resource service_object.state = "open" service_object.save! service_object end + # This is copy-pasted from Metasploit Framework (with small tweaks right now to lalow for :name and :service_name, and :proto/:protocol): + # https://github.com/rapid7/metasploit-framework/blob/2dcfb985ea68ba2384f5309afdc9ec1a17cb80b9/lib/msf/core/db_manager/service.rb#L193 + # In the future, we can potentially migrate this AND the Metasploit Framework code to metasploit_data_models, so that + # both Framework and Credential have access to the shared code. + def process_service_chain(host, services) + return unless host.is_a?(Mdm::Host) + + return if services.nil? + + services = [services] unless services.is_a?(Array) + services.map do |service| + case service + when ::Mdm::Service + service_obj = service + when ::Hash + next if service[:port].nil? || service[:proto].nil? + + parents = nil + if service[:parents]&.any? + parents = process_service_chain(host, service[:parents]) + end + + service_info = { + port: service[:port].to_i, + proto: service[:proto].to_s.downcase, + } + service_info[:name] = service[:name].downcase if service[:name] + service_info[:resource] = service[:resource] if service[:resource] + service_obj = host.services.find_or_create_by(service_info) + if service_obj.id.nil? + # elog("Failed to create service #{service_info.inspect} for host #{host.name} (#{host.address})") + return + end + service_obj.state ||= Msf::ServiceState::Open + service_obj.info = service[:info] ? service[:info] : '' + + if parents + parents.each do |parent| + service_obj.parents << parent if parent && !service_obj.parents.include?(parent) + end + end + else + next + end + + service_obj + end.compact + end + # This method checks to see if a {Metasploit::Credential::Login} exists for a given # set of details. If it does exists, we then appropriately set the status to one of our # failure statuses. From cea6c3344826108ecbcdc16a119a7c26dc3c52de Mon Sep 17 00:00:00 2001 From: adfoster-r7 <60357436+adfoster-r7@users.noreply.github.com> Date: Thu, 28 May 2026 23:13:22 +0100 Subject: [PATCH 3/3] Inline constant value Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/metasploit/credential/creation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metasploit/credential/creation.rb b/lib/metasploit/credential/creation.rb index 945acc4..ac00fb6 100644 --- a/lib/metasploit/credential/creation.rb +++ b/lib/metasploit/credential/creation.rb @@ -669,7 +669,7 @@ def process_service_chain(host, services) # elog("Failed to create service #{service_info.inspect} for host #{host.name} (#{host.address})") return end - service_obj.state ||= Msf::ServiceState::Open + service_obj.state ||= 'open' service_obj.info = service[:info] ? service[:info] : '' if parents