From b3a184099193be2c7867332112571af66b0e2546 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Thu, 8 May 2014 15:13:01 -0500 Subject: [PATCH 01/15] add metasploit-credential added metaploit-credential to the Gemfile --- Gemfile | 2 ++ Gemfile.lock | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Gemfile b/Gemfile index 9045947d35..954ebe96bb 100755 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,7 @@ source 'https://rubygems.org' +gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.1.2-metasploit-credential' + # Need 3+ for ActiveSupport::Concern gem 'activesupport', '>= 3.0.0', '< 4.0.0' # Needed for some admin modules (cfme_manageiq_evm_pass_reset.rb) diff --git a/Gemfile.lock b/Gemfile.lock index 24c9e87b7f..08d6efb973 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,13 @@ +GIT + remote: github-metasploit-credential:rapid7/metasploit-credential.git + revision: 2f8384cd5f7d0124e276a6e4b7fa8193dd96f56c + tag: v0.1.2-metasploit-credential + specs: + metasploit-credential (0.1.2.pre.metasploit.pre.credential) + metasploit-concern (~> 0.0.4) + metasploit_data_models (~> 0.17.0) + rubyntlm + GEM remote: https://rubygems.org/ specs: @@ -22,6 +32,8 @@ GEM fivemat (1.2.1) i18n (0.6.9) json (1.8.1) + metasploit-concern (0.0.4) + activesupport (~> 3.0, >= 3.0.0) metasploit_data_models (0.17.0) activerecord (>= 3.2.13) activesupport @@ -47,6 +59,7 @@ GEM rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.14.6) + rubyntlm (0.4.0) shoulda-matchers (2.6.0) activesupport (>= 3.0.0) simplecov (0.5.4) @@ -68,6 +81,7 @@ DEPENDENCIES factory_girl (>= 4.1.0) fivemat (= 1.2.1) json + metasploit-credential! metasploit_data_models (~> 0.17.0) msgpack network_interface (~> 0.0.1) From 9efb97d46515697397ba5072de8aa6a818c278b1 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Mon, 19 May 2014 10:00:19 -0500 Subject: [PATCH 02/15] origin creation method added base behaviour for creating generic credential origin objects from report --- lib/msf/core/auxiliary/report.rb | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 53e18d9fe2..eb41e1f0e2 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -14,6 +14,79 @@ module Auxiliary::Report super end + def create_credential(opts={}) + return nil unless framework.db.active + origin = create_credential_origin(opts) + + end + + def create_credential_origin(opts={}) + return nil unless framework.db.active + case opts[:origin_type] + when :import + create_credential_origin_import(opts) + when :manual + create_credential_origin_manual(opts) + when :service + create_credential_origin_service(opts) + when :session + create_credential_origin_session(opts) + else + raise ArgumentError, "Unknown Origin Type #{opts[:origin_type]}" + end + end + + def create_credential_origin_import(opts={}) + return nil unless framework.db.active + task_id = opts.fetch(:task_id) + filename = opts.fetch(:filename) + + origin_object = Metasploit::Credential::Origin::Import.where(filename: filename, task_id: task_id).first_or_create + origin_object.save! + origin_object + end + + def create_credential_origin_manual(opts={}) + return nil unless framework.db.active + user_id = opts.fetch(:user_id) + + origin_object = Metasploit::Credential::Origin::Manual.where(user_id: user_id).first_or_create + origin_object.save! + origin_object + end + + def create_credential_origin_service(opts={}) + return nil unless framework.db.active + address = opts.fetch(:address) + port = opts.fetch(:port) + service_name = opts.fetch(:service_name) + protocol = opts.fetch(:protocol) + module_fullname = opts.fetch(:module_fullname) + workspace_id = opts.fetch(:workspace_id) + + # Find or create the host object we need + host_object = Mdm::Host.where(address: address, workspace_id: workspace_id).first_or_create + host_object.save! + + # Next we find or create the Service object we need + service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol).first_or_create + service_object.name = service_name + service_object.save! + + origin_object = Metasploit::Credential::Origin::Service.where(service_id: service_object.id, module_full_name: module_fullname).first_or_create + origin_object.save! + origin_object + end + + def create_credential_origin_session(opts={}) + session_id = opts.fetch(:session_id) + module_fullname = opts.fetch(:module_fullname) + + origin_object = Metasploit::Credential::Origin::Session.where(session_id: session_id, module_full_name: module_fullname).first_or_create + origin_object.save! + origin_object + end + # Shortcut method for detecting when the DB is active def db framework.db.active From ddfa4f1ee7b399fafc4765bf93e270a3b45c0525 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Mon, 19 May 2014 15:16:02 -0500 Subject: [PATCH 03/15] some origin creation specs started getting working specs for the origin creation methods. feel into the weeds for a bit, but making progress at last. --- Gemfile | 2 +- Gemfile.lock | 8 + db/schema.rb | 827 ++++++++++++++++++++- lib/msf/core/auxiliary/report.rb | 5 - spec/lib/msf/core/auxiliary/report_spec.rb | 118 +++ 5 files changed, 922 insertions(+), 38 deletions(-) create mode 100644 spec/lib/msf/core/auxiliary/report_spec.rb diff --git a/Gemfile b/Gemfile index 25631bee08..fb2fbcd944 100755 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source 'https://rubygems.org' - +gem 'pry' gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.1.2-metasploit-credential' # Need 3+ for ActiveSupport::Concern diff --git a/Gemfile.lock b/Gemfile.lock index 874e3966c8..dad84eea31 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,6 +35,7 @@ GEM arel (3.0.3) bcrypt (3.1.7) builder (3.0.4) + coderay (1.1.0) diff-lcs (1.2.5) erubis (2.7.0) factory_girl (4.4.0) @@ -53,6 +54,7 @@ GEM activerecord (>= 3.2.13, < 4.0.0) activesupport pg + method_source (0.8.2) mini_portile (0.5.3) msgpack (0.5.8) multi_json (1.0.4) @@ -62,6 +64,10 @@ GEM packetfu (1.1.9) pcaprub (0.11.3) pg (0.17.1) + pry (0.9.12.6) + coderay (~> 1.0) + method_source (~> 0.8) + slop (~> 3.4) rack (1.4.5) rack-cache (1.2) rack (>= 0.4) @@ -105,6 +111,7 @@ GEM multi_json (~> 1.0.3) simplecov-html (~> 0.5.3) simplecov-html (0.5.3) + slop (3.4.7) sprockets (2.2.2) hike (~> 1.2) multi_json (~> 1.0) @@ -135,6 +142,7 @@ DEPENDENCIES packetfu (= 1.1.9) pcaprub pg (>= 0.11) + pry railties rake (>= 10.0.0) redcarpet diff --git a/db/schema.rb b/db/schema.rb index f71e75026b..5784602421 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -17,6 +17,85 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.text "token" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.string "name" + end + + create_table "app_categories", :force => true do |t| + t.string "name" + end + + create_table "app_categories_apps", :force => true do |t| + t.integer "app_id" + t.integer "app_category_id" + t.string "name" + end + + add_index "app_categories_apps", ["app_category_id"], :name => "index_app_categories_apps_on_app_category_id" + add_index "app_categories_apps", ["app_id"], :name => "index_app_categories_apps_on_app_id" + + create_table "app_runs", :force => true do |t| + t.datetime "started_at" + t.datetime "stopped_at" + t.integer "app_id" + t.text "config" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state" + t.integer "workspace_id" + t.boolean "hidden", :default => false + end + + add_index "app_runs", ["app_id"], :name => "index_app_runs_on_app_id" + add_index "app_runs", ["workspace_id"], :name => "index_app_runs_on_workspace_id" + + create_table "apps", :force => true do |t| + t.string "name" + t.text "description" + t.float "rating" + t.string "symbol" + t.boolean "hidden", :default => false + end + + create_table "automatic_exploitation_match_results", :force => true do |t| + t.integer "match_id" + t.integer "run_id" + t.string "state", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "automatic_exploitation_match_sets", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "automatic_exploitation_match_sets", ["user_id"], :name => "index_automatic_exploitation_match_sets_on_user_id" + add_index "automatic_exploitation_match_sets", ["workspace_id"], :name => "index_automatic_exploitation_match_sets_on_workspace_id" + + create_table "automatic_exploitation_matches", :force => true do |t| + t.integer "vuln_id" + t.integer "module_detail_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "match_set_id" + t.integer "nexpose_data_exploit_id" + t.string "matchable_type" + t.integer "matchable_id" + end + + add_index "automatic_exploitation_matches", ["module_detail_id"], :name => "index_automatic_exploitation_matches_on_ref_id" + add_index "automatic_exploitation_matches", ["nexpose_data_exploit_id"], :name => "index_automatic_exploitation_matches_on_nexpose_data_exploit_id" + add_index "automatic_exploitation_matches", ["vuln_id"], :name => "index_automatic_exploitation_matches_on_vuln_id" + + create_table "automatic_exploitation_runs", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.integer "match_set_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state" end create_table "clients", :force => true do |t| @@ -28,6 +107,17 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.datetime "updated_at" end + create_table "cred_files", :force => true do |t| + t.integer "workspace_id", :default => 1, :null => false + t.string "path", :limit => 1024 + t.string "ftype", :limit => 16 + t.string "created_by" + t.string "name", :limit => 512 + t.string "desc", :limit => 1024 + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "creds", :force => true do |t| t.integer "service_id", :null => false t.datetime "created_at", :null => false @@ -41,6 +131,32 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.string "source_type" end + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0 + t.integer "attempts", :default => 0 + t.text "handler" + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + + create_table "egadz_result_ranges", :force => true do |t| + t.integer "task_id" + t.string "target_host" + t.integer "start_port" + t.integer "end_port" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state" + end + create_table "events", :force => true do |t| t.integer "workspace_id" t.integer "host_id" @@ -51,6 +167,8 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.boolean "seen" t.string "username" t.text "info" + t.text "module_rhost" + t.text "module_name" end create_table "exploit_attempts", :force => true do |t| @@ -79,6 +197,33 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.datetime "updated_at", :null => false end + create_table "exports", :force => true do |t| + t.integer "workspace_id", :null => false + t.string "created_by" + t.string "export_type" + t.string "name" + t.string "state" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "file_path", :limit => 1024 + t.boolean "mask_credentials", :default => false + t.datetime "completed_at" + t.text "included_addresses" + t.text "excluded_addresses" + t.datetime "started_at" + end + + create_table "generated_payloads", :force => true do |t| + t.string "state" + t.string "file" + t.text "options" + t.integer "workspace_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "generator_error" + t.string "payload_class" + end + create_table "host_details", :force => true do |t| t.integer "host_id" t.integer "nx_console_id" @@ -115,6 +260,8 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.integer "host_detail_count", :default => 0 t.integer "exploit_attempt_count", :default => 0 t.integer "cred_count", :default => 0 + t.integer "nexpose_data_asset_id" + t.integer "history_count", :default => 0 end add_index "hosts", ["name"], :name => "index_hosts_on_name" @@ -129,6 +276,15 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.integer "tag_id" end + create_table "known_ports", :force => true do |t| + t.integer "port", :null => false + t.string "proto", :default => "tcp", :null => false + t.string "name", :null => false + t.text "info" + end + + add_index "known_ports", ["port"], :name => "index_known_ports_on_port" + create_table "listeners", :force => true do |t| t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -355,6 +511,209 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.text "name" end + create_table "nexpose_data_assets", :force => true do |t| + t.integer "nexpose_data_site_id", :null => false + t.string "asset_id", :null => false + t.string "url" + t.text "host_names" + t.string "os_name" + t.text "mac_addresses" + t.datetime "last_scan_date" + t.datetime "next_scan_date" + t.string "last_scan_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_assets", ["asset_id"], :name => "index_nexpose_data_assets_on_asset_id" + add_index "nexpose_data_assets", ["nexpose_data_site_id"], :name => "index_nexpose_data_assets_on_nexpose_data_site_id" + + create_table "nexpose_data_exploits", :force => true do |t| + t.integer "module_detail_id" + t.string "nexpose_exploit_id" + t.string "skill_level" + t.text "description" + t.string "source_key" + t.string "source" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_exploits", ["nexpose_exploit_id"], :name => "index_nexpose_data_exploits_on_nexpose_exploit_id", :unique => true + add_index "nexpose_data_exploits", ["source", "source_key"], :name => "index_nexpose_data_exploits_on_source_and_source_key" + + create_table "nexpose_data_exploits_vulnerability_definitions", :id => false, :force => true do |t| + t.integer "exploit_id" + t.integer "vulnerability_definition_id" + end + + add_index "nexpose_data_exploits_vulnerability_definitions", ["exploit_id", "vulnerability_definition_id"], :name => "index_nx_data_exploits_vuln_defs_on_exploit_id_and_vuln_def_id" + add_index "nexpose_data_exploits_vulnerability_definitions", ["vulnerability_definition_id", "exploit_id"], :name => "index_nx_data_exploits_vuln_defs_on_vuln_def_id_and_exploit_id" + + create_table "nexpose_data_import_runs", :force => true do |t| + t.integer "user_id" + t.integer "workspace_id" + t.string "state" + t.integer "nx_console_id" + t.boolean "metasploitable_only", :default => true + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "import_state" + end + + add_index "nexpose_data_import_runs", ["nx_console_id"], :name => "index_nexpose_data_import_runs_on_nx_console_id" + + create_table "nexpose_data_ip_addresses", :force => true do |t| + t.integer "nexpose_data_asset_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "address", :limit => nil + end + + add_index "nexpose_data_ip_addresses", ["nexpose_data_asset_id"], :name => "index_nexpose_data_ip_addresses_on_nexpose_data_asset_id" + + create_table "nexpose_data_scan_templates", :force => true do |t| + t.integer "nx_console_id", :null => false + t.string "scan_template_id", :null => false + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_scan_templates", ["nx_console_id"], :name => "index_nexpose_data_scan_templates_on_nx_console_id" + add_index "nexpose_data_scan_templates", ["scan_template_id"], :name => "index_nexpose_data_scan_templates_on_scan_template_id" + + create_table "nexpose_data_sites", :force => true do |t| + t.integer "nexpose_data_import_run_id", :null => false + t.string "site_id", :null => false + t.string "name" + t.text "description" + t.string "importance" + t.string "type" + t.datetime "last_scan_date" + t.datetime "next_scan_date" + t.string "last_scan_id" + t.text "summary" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_sites", ["nexpose_data_import_run_id"], :name => "index_nexpose_data_sites_on_nexpose_data_import_run_id" + add_index "nexpose_data_sites", ["site_id"], :name => "index_nexpose_data_sites_on_site_id" + + create_table "nexpose_data_vulnerabilities", :force => true do |t| + t.integer "nexpose_data_vulnerability_definition_id", :null => false + t.string "vulnerability_id", :null => false + t.string "title" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_vulnerabilities", ["nexpose_data_vulnerability_definition_id"], :name => "index_nx_data_vuln_on_nexpose_data_vuln_def_id" + add_index "nexpose_data_vulnerabilities", ["vulnerability_id"], :name => "index_nexpose_data_vulnerabilities_on_vulnerability_id", :unique => true + + create_table "nexpose_data_vulnerability_definitions", :force => true do |t| + t.string "vulnerability_definition_id" + t.string "title" + t.text "description" + t.date "date_published" + t.integer "severity_score" + t.string "serverity" + t.string "pci_severity_score" + t.string "pci_status" + t.decimal "riskscore" + t.string "cvss_vector" + t.string "cvss_access_vector_id" + t.string "cvss_access_complexity_id" + t.string "cvss_authentication_id" + t.string "cvss_confidentiality_impact_id" + t.string "cvss_integrity_impact_id" + t.string "cvss_availability_impact_id" + t.decimal "cvss_score" + t.decimal "cvss_exploit_score" + t.decimal "cvss_impact_score" + t.boolean "denial_of_service" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "nexpose_data_vulnerability_definitions", ["vulnerability_definition_id"], :name => "index_nx_data_vuln_def_on_vulnerability_definition_id", :unique => true + + create_table "nexpose_data_vulnerability_instances", :force => true do |t| + t.string "vulnerability_id" + t.string "asset_id" + t.integer "nexpose_data_vulnerability_id" + t.integer "nexpose_data_asset_id" + t.string "scan_id" + t.date "date" + t.string "status" + t.text "proof" + t.string "key" + t.string "service" + t.integer "port" + t.string "protocol" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "asset_ip_address", :limit => nil + end + + add_index "nexpose_data_vulnerability_instances", ["asset_id", "vulnerability_id"], :name => "index_nx_data_vuln_inst_on_asset_id_and_vulnerability_id" + add_index "nexpose_data_vulnerability_instances", ["nexpose_data_asset_id"], :name => "index_nx_data_vuln_inst_on_nexpose_data_asset_id" + add_index "nexpose_data_vulnerability_instances", ["nexpose_data_vulnerability_id"], :name => "index_nx_data_vuln_inst_on_nexpose_data_vulnerability_id" + add_index "nexpose_data_vulnerability_instances", ["vulnerability_id", "asset_id"], :name => "index_nx_data_vuln_inst_on_vulnerability_id_and_asset_id" + + create_table "nexpose_data_vulnerability_references", :force => true do |t| + t.integer "nexpose_data_vulnerability_definition_id" + t.string "vulnerability_reference_id" + t.string "source" + t.string "reference" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "nexpose_result_exceptions", :force => true do |t| + t.integer "user_id" + t.string "nx_scope_type" + t.integer "nx_scope_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "automatic_exploitation_match_result_id" + t.integer "nexpose_result_export_run_id" + t.datetime "expiration_date" + t.string "reason" + t.text "comments" + t.boolean "approve" + t.boolean "sent_to_nexpose" + t.datetime "sent_at" + end + + add_index "nexpose_result_exceptions", ["nexpose_result_export_run_id"], :name => "index_nexpose_result_exceptions_on_nexpose_result_export_run_id" + add_index "nexpose_result_exceptions", ["nx_scope_type", "nx_scope_id"], :name => "index_nx_r_exceptions_on_nx_scope_type_and_nx_scope_id" + add_index "nexpose_result_exceptions", ["user_id"], :name => "index_nexpose_result_exceptions_on_user_id" + + create_table "nexpose_result_export_runs", :force => true do |t| + t.string "state" + t.integer "nx_console_id" + t.integer "user_id" + t.integer "workspace_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "nexpose_result_validations", :force => true do |t| + t.integer "user_id" + t.integer "nexpose_data_asset_id" + t.datetime "verified_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "automatic_exploitation_match_result_id" + t.integer "nexpose_result_export_run_id" + t.boolean "sent_to_nexpose" + t.datetime "sent_at" + end + + add_index "nexpose_result_validations", ["nexpose_result_export_run_id"], :name => "index_nx_result_validations_on_nx_result_export_run_id" + create_table "notes", :force => true do |t| t.datetime "created_at" t.string "ntype", :limit => 512 @@ -369,6 +728,32 @@ ActiveRecord::Schema.define(:version => 20140417140933) do add_index "notes", ["ntype"], :name => "index_notes_on_ntype" + create_table "notification_messages", :force => true do |t| + t.integer "workspace_id" + t.integer "task_id" + t.string "title" + t.text "content" + t.string "url" + t.string "kind" + t.datetime "created_at" + end + + create_table "notification_messages_users", :force => true do |t| + t.integer "user_id" + t.integer "message_id" + t.boolean "read", :default => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "pnd_pcap_files", :force => true do |t| + t.integer "task_id" + t.integer "loot_id" + t.string "status" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "profiles", :force => true do |t| t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -387,26 +772,47 @@ ActiveRecord::Schema.define(:version => 20140417140933) do add_index "refs", ["name"], :name => "index_refs_on_name" - create_table "report_templates", :force => true do |t| - t.integer "workspace_id", :default => 1, :null => false + create_table "report_artifacts", :force => true do |t| + t.integer "report_id", :null => false + t.string "file_path", :limit => 1024, :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.datetime "accessed_at" + end + + create_table "report_custom_resources", :force => true do |t| + t.integer "workspace_id", :null => false t.string "created_by" - t.string "path", :limit => 1024 - t.text "name" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.string "resource_type" + t.string "name" + t.string "file_path" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "reports", :force => true do |t| - t.integer "workspace_id", :default => 1, :null => false + t.integer "workspace_id", :null => false t.string "created_by" - t.string "rtype" - t.string "path", :limit => 1024 + t.string "report_type" + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "file_formats" t.text "options" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.datetime "downloaded_at" - t.integer "task_id" - t.string "name", :limit => 63 + t.string "sections" + t.string "report_template" + t.text "included_addresses" + t.string "state" + t.datetime "started_at" + t.datetime "completed_at" + t.text "excluded_addresses" + t.integer "se_campaign_id" + t.integer "app_run_id" + t.string "order_vulns_by" + t.text "usernames_reported" + t.boolean "skip_data_check", :default => false + t.text "email_recipients" + t.text "logo_path" end create_table "routes", :force => true do |t| @@ -415,6 +821,204 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.string "netmask" end + create_table "run_stats", :force => true do |t| + t.string "name" + t.float "data" + t.integer "task_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "scheduled_tasks", :force => true do |t| + t.string "kind" + t.datetime "last_run_at" + t.string "state" + t.string "last_run_status" + t.integer "task_chain_id" + t.integer "position" + t.text "config_hash" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "form_hash" + t.text "report_hash" + t.string "file_upload" + end + + create_table "se_campaign_files", :force => true do |t| + t.integer "attachable_id" + t.string "attachable_type" + t.string "attachment" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "content_disposition" + t.string "type" + t.integer "workspace_id" + t.integer "user_id" + t.string "name" + t.integer "file_size" + end + + create_table "se_campaigns", :force => true do |t| + t.integer "user_id" + t.integer "workspace_id" + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state", :default => "unconfigured" + t.text "prefs" + t.integer "port" + t.datetime "started_at" + t.string "config_type" + t.integer "started_by_user_id" + t.boolean "notification_enabled" + t.string "notification_email_address" + t.text "notification_email_message" + t.string "notification_email_subject" + t.datetime "last_target_interaction_at" + end + + create_table "se_email_openings", :force => true do |t| + t.integer "email_id" + t.integer "human_target_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "address", :limit => nil + end + + create_table "se_email_sends", :force => true do |t| + t.integer "email_id" + t.integer "human_target_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "sent" + t.string "status_message" + end + + create_table "se_email_templates", :force => true do |t| + t.integer "user_id" + t.text "content" + t.string "name" + t.integer "workspace_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "se_emails", :force => true do |t| + t.integer "user_id" + t.text "content" + t.string "name" + t.string "subject" + t.integer "campaign_id" + t.integer "template_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "from_address" + t.string "from_name" + t.integer "target_list_id" + t.integer "email_template_id" + t.text "prefs" + t.string "attack_type" + t.string "status" + t.datetime "sent_at" + t.string "origin_type" + t.string "editor_type" + end + + create_table "se_human_targets", :force => true do |t| + t.string "first_name" + t.string "last_name" + t.string "email_address" + t.integer "workspace_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "se_phishing_results", :force => true do |t| + t.integer "human_target_id" + t.integer "web_page_id" + t.text "data" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "address", :limit => nil + t.text "raw_data" + t.string "browser_name" + t.string "browser_version" + t.string "os_name" + t.string "os_version" + end + + create_table "se_portable_files", :force => true do |t| + t.integer "campaign_id" + t.string "name" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "prefs" + t.string "file_name" + t.string "exploit_module_path" + t.boolean "dynamic_stagers", :default => false + end + + create_table "se_target_list_human_targets", :force => true do |t| + t.integer "target_list_id" + t.integer "human_target_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "se_target_lists", :force => true do |t| + t.string "name" + t.string "file_name" + t.integer "user_id" + t.integer "workspace_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "se_tracking_links", :force => true do |t| + t.string "external_destination_url" + t.integer "email_id" + t.integer "web_page_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "se_visits", :force => true do |t| + t.integer "human_target_id" + t.integer "web_page_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "email_id" + t.string "address", :limit => nil + end + + create_table "se_web_pages", :force => true do |t| + t.integer "campaign_id" + t.string "path" + t.text "content" + t.string "clone_url" + t.boolean "online" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "name" + t.text "prefs" + t.integer "template_id" + t.string "attack_type" + t.string "origin_type" + t.string "phishing_redirect_origin" + end + + create_table "se_web_templates", :force => true do |t| + t.string "name" + t.integer "workspace_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "content" + t.string "clone_url" + t.string "origin_type" + end + create_table "services", :force => true do |t| t.integer "host_id" t.datetime "created_at" @@ -455,6 +1059,7 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.string "close_reason" t.integer "local_id" t.datetime "last_seen" + t.integer "campaign_id" end create_table "tags", :force => true do |t| @@ -468,6 +1073,26 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.datetime "updated_at", :null => false end + create_table "task_chains", :force => true do |t| + t.text "schedule" + t.string "name" + t.datetime "last_run_at" + t.datetime "next_run_at" + t.integer "user_id" + t.integer "workspace_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "state", :default => "ready" + t.boolean "clear_workspace_before_run" + t.boolean "legacy", :default => true + t.integer "active_task_id" + t.text "schedule_hash" + t.integer "active_scheduled_task_id" + t.integer "active_report_id" + t.integer "last_run_task_id" + t.integer "last_run_report_id" + end + create_table "task_creds", :force => true do |t| t.integer "task_id", :null => false t.integer "cred_id", :null => false @@ -512,6 +1137,7 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.text "result" t.string "module_uuid", :limit => 8 t.binary "settings" + t.integer "app_run_id" end create_table "users", :force => true do |t| @@ -519,14 +1145,15 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.string "crypted_password" t.string "password_salt" t.string "persistence_token" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "fullname" t.string "email" t.string "phone" t.string "company" - t.string "prefs", :limit => 524288 - t.boolean "admin", :default => true, :null => false + t.string "prefs", :limit => 524288 + t.boolean "admin", :default => true, :null => false + t.integer "notification_center_count", :default => 0 end create_table "vuln_attempts", :force => true do |t| @@ -572,19 +1199,51 @@ ActiveRecord::Schema.define(:version => 20140417140933) do t.datetime "created_at" t.string "name" t.datetime "updated_at" - t.string "info", :limit => 65536 + t.string "info", :limit => 65536 t.datetime "exploited_at" - t.integer "vuln_detail_count", :default => 0 - t.integer "vuln_attempt_count", :default => 0 + t.integer "vuln_detail_count", :default => 0 + t.integer "vuln_attempt_count", :default => 0 + t.integer "nexpose_data_vuln_def_id" end add_index "vulns", ["name"], :name => "index_vulns_on_name" + add_index "vulns", ["nexpose_data_vuln_def_id"], :name => "index_vulns_on_nexpose_data_vuln_def_id" create_table "vulns_refs", :force => true do |t| t.integer "ref_id" t.integer "vuln_id" end + create_table "web_attack_cross_site_scriptings", :force => true do |t| + t.string "encloser_type", :null => false + t.string "escaper_type", :null => false + t.string "evader_type", :null => false + t.string "executor_type", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "web_cookies", :force => true do |t| + t.string "name", :null => false + t.string "value", :null => false + t.integer "request_group_id", :null => false + t.string "domain", :null => false + t.string "path" + t.boolean "secure", :default => false, :null => false + t.boolean "http_only", :default => false, :null => false + t.integer "version" + t.string "commnet" + t.string "comment_url" + t.boolean "discard", :default => false, :null => false + t.text "ports" + t.integer "max_age" + t.datetime "expires_at" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "web_cookies", ["request_group_id", "name"], :name => "index_web_cookies_on_request_group_id_and_name" + create_table "web_forms", :force => true do |t| t.integer "web_site_id", :null => false t.datetime "created_at", :null => false @@ -597,6 +1256,14 @@ ActiveRecord::Schema.define(:version => 20140417140933) do add_index "web_forms", ["path"], :name => "index_web_forms_on_path" + create_table "web_headers", :force => true do |t| + t.boolean "attack_vector", :null => false + t.string "name", :null => false + t.string "value", :null => false + t.integer "position", :null => false + t.integer "request_group_id", :null => false + end + create_table "web_pages", :force => true do |t| t.integer "web_site_id", :null => false t.datetime "created_at", :null => false @@ -617,6 +1284,41 @@ ActiveRecord::Schema.define(:version => 20140417140933) do add_index "web_pages", ["path"], :name => "index_web_pages_on_path" add_index "web_pages", ["query"], :name => "index_web_pages_on_query" + create_table "web_parameters", :force => true do |t| + t.boolean "attack_vector", :null => false + t.string "name", :null => false + t.string "value", :null => false + t.integer "request_id", :null => false + t.integer "position", :null => false + end + + create_table "web_proofs", :force => true do |t| + t.string "image" + t.text "text" + t.integer "vuln_id", :null => false + end + + create_table "web_request_groups", :force => true do |t| + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "user_id", :null => false + t.integer "workspace_id", :null => false + end + + create_table "web_requests", :force => true do |t| + t.string "method", :null => false + t.integer "virtual_host_id", :null => false + t.string "path", :null => false + t.boolean "attack", :default => true + t.boolean "requested" + t.boolean "attack_vector" + t.integer "request_group_id" + t.integer "cross_site_scripting_id" + end + + add_index "web_requests", ["cross_site_scripting_id"], :name => "index_web_requests_on_cross_site_scripting_id" + add_index "web_requests", ["request_group_id"], :name => "index_web_requests_on_request_group_id" + create_table "web_sites", :force => true do |t| t.integer "service_id", :null => false t.datetime "created_at", :null => false @@ -630,31 +1332,92 @@ ActiveRecord::Schema.define(:version => 20140417140933) do add_index "web_sites", ["options"], :name => "index_web_sites_on_options" add_index "web_sites", ["vhost"], :name => "index_web_sites_on_vhost" + create_table "web_transmitted_cookies", :force => true do |t| + t.boolean "transmitted" + t.integer "request_id" + t.integer "cookie_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "web_transmitted_headers", :force => true do |t| + t.boolean "transmitted" + t.integer "request_id" + t.integer "header_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "web_virtual_hosts", :force => true do |t| + t.string "name", :null => false + t.integer "service_id", :null => false + end + + add_index "web_virtual_hosts", ["service_id", "name"], :name => "index_web_virtual_hosts_on_service_id_and_name", :unique => true + + create_table "web_vuln_category_metasploits", :force => true do |t| + t.string "name", :null => false + t.string "summary", :null => false + end + + add_index "web_vuln_category_metasploits", ["name"], :name => "index_web_vuln_category_metasploits_on_name", :unique => true + + create_table "web_vuln_category_owasps", :force => true do |t| + t.string "detectability", :null => false + t.string "exploitability", :null => false + t.string "impact", :null => false + t.string "name", :null => false + t.string "prevalence", :null => false + t.integer "rank", :null => false + t.string "summary", :null => false + t.string "target", :null => false + t.string "version", :null => false + end + + add_index "web_vuln_category_owasps", ["target", "version", "rank"], :name => "index_web_vuln_category_owasps_on_target_and_version_and_rank", :unique => true + + create_table "web_vuln_category_projection_metasploit_owasps", :force => true do |t| + t.integer "metasploit_id", :null => false + t.integer "owasp_id", :null => false + end + + add_index "web_vuln_category_projection_metasploit_owasps", ["metasploit_id", "owasp_id"], :name => "index_web_vuln_category_project_metasploit_id_and_owasp_id", :unique => true + create_table "web_vulns", :force => true do |t| - t.integer "web_site_id", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.text "path", :null => false - t.string "method", :limit => 1024, :null => false - t.text "params", :null => false + t.integer "web_site_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.text "path", :null => false + t.string "method", :limit => 1024, :null => false + t.text "params", :null => false t.text "pname" - t.integer "risk", :null => false - t.string "name", :limit => 1024, :null => false + t.integer "risk", :null => false + t.string "name", :limit => 1024, :null => false t.text "query" - t.text "category", :null => false - t.integer "confidence", :null => false + t.text "legacy_category" + t.integer "confidence", :null => false t.text "description" t.text "blame" t.binary "request" - t.binary "proof", :null => false t.string "owner" t.text "payload" + t.integer "request_id" + t.integer "category_id" end add_index "web_vulns", ["method"], :name => "index_web_vulns_on_method" add_index "web_vulns", ["name"], :name => "index_web_vulns_on_name" add_index "web_vulns", ["path"], :name => "index_web_vulns_on_path" + create_table "wizard_procedures", :force => true do |t| + t.text "config_hash" + t.string "state" + t.integer "task_chain_id" + t.string "type" + t.integer "workspace_id" + t.integer "user_id" + end + create_table "wmap_requests", :force => true do |t| t.string "host" t.string "address", :limit => nil diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index eb41e1f0e2..e87d8c3f95 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -9,11 +9,6 @@ module Msf module Auxiliary::Report - - def initialize(info = {}) - super - end - def create_credential(opts={}) return nil unless framework.db.active origin = create_credential_origin(opts) diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb new file mode 100644 index 0000000000..b9c7876279 --- /dev/null +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' +require 'msf/core/auxiliary/report' + +describe Msf::Auxiliary::Report do + include_context 'Msf::DBManager' + let(:dummy_class) { + Class.new do + include Msf::Auxiliary::Report + + attr_accessor :framework + def initialize(framework_instance) + @framework = framework_instance + end + end + } + let(:task) { FactoryGirl.create(:mdm_task)} + + let(:user) { FactoryGirl.create(:mdm_user)} + + subject(:test_object) { dummy_class.new(framework) } + + context '#create_credential_origin_import' do + it 'creates a Metasploit::Credential::Origin object' do + opts = { + filename: "test_import.xml", + task_id: task.id + } + expect { test_object.create_credential_origin_import(opts)}.to change{Metasploit::Credential::Origin::Import.count}.by(1) + end + + context 'when called twice with the same options' do + it 'does not create duplicate objects' do + opts = { + filename: "test_import.xml", + task_id: task.id + } + test_object.create_credential_origin_import(opts) + expect { test_object.create_credential_origin_import(opts)}.to_not change{Metasploit::Credential::Origin::Import.count} + end + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = { + filename: "test_import.xml" + } + expect{ test_object.create_credential_origin_import(opts)}.to raise_error KeyError + end + end + + + end + + context '#create_credential_origin_manual' do + it 'creates a Metasploit::Credential::Origin object' do + opts = { + user_id: user.id + } + expect { test_object.create_credential_origin_manual(opts)}.to change{Metasploit::Credential::Origin::Manual.count}.by(1) + end + + context 'when called twice with the same options' do + it 'does not create duplicate objects' do + opts = { + user_id: user.id + } + test_object.create_credential_origin_manual(opts) + expect { test_object.create_credential_origin_manual(opts)}.to_not change{Metasploit::Credential::Origin::Manual.count} + end + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = {} + expect{ test_object.create_credential_origin_manual(opts)}.to raise_error KeyError + end + end + end + + context '#create_credential_origin_service' do + it 'creates a Metasploit::Credential::Origin object' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + expect { test_object.create_credential_origin_service(opts)}.to change{Metasploit::Credential::Origin::Service.count}.by(1) + end + + context 'when called twice with the same options' do + it 'does not create duplicate objects' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + test_object.create_credential_origin_service(opts) + expect { test_object.create_credential_origin_service(opts)}.to_not change{Metasploit::Credential::Origin::Service.count} + end + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = {} + expect{ test_object.create_credential_origin_service(opts)}.to raise_error KeyError + end + end + end + +end \ No newline at end of file From b84aaaad1990a2db51e1eaa5ae71afa01ab3785e Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 20 May 2014 09:59:15 -0500 Subject: [PATCH 04/15] specs and fixes for origin creation --- lib/msf/core/auxiliary/report.rb | 6 +- spec/lib/msf/core/auxiliary/report_spec.rb | 94 ++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index e87d8c3f95..6cf8643b00 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -74,10 +74,10 @@ module Auxiliary::Report end def create_credential_origin_session(opts={}) - session_id = opts.fetch(:session_id) - module_fullname = opts.fetch(:module_fullname) + session_id = opts.fetch(:session_id) + post_reference_name = opts.fetch(:post_reference_name) - origin_object = Metasploit::Credential::Origin::Session.where(session_id: session_id, module_full_name: module_fullname).first_or_create + origin_object = Metasploit::Credential::Origin::Session.where(session_id: session_id, post_reference_name: post_reference_name).first_or_create origin_object.save! origin_object end diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index b9c7876279..2c505950ab 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -13,6 +13,9 @@ describe Msf::Auxiliary::Report do end end } + + let(:session) { FactoryGirl.create(:mdm_session) } + let(:task) { FactoryGirl.create(:mdm_task)} let(:user) { FactoryGirl.create(:mdm_user)} @@ -91,6 +94,69 @@ describe Msf::Auxiliary::Report do expect { test_object.create_credential_origin_service(opts)}.to change{Metasploit::Credential::Origin::Service.count}.by(1) end + context 'when there is a matching host record' do + it 'creates a new host record' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + FactoryGirl.create(:mdm_host, address: opts[:address], workspace_id: opts[:workspace_id]) + expect { test_object.create_credential_origin_service(opts)}.to_not change{Mdm::Host.count} + end + end + + context 'when there is not a matching host record' do + it 'uses the existing host record' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + expect { test_object.create_credential_origin_service(opts)}.to change{Metasploit::Credential::Origin::Service.count}.by(1) + end + end + + context 'when there is a matching service record' do + it 'uses the existing service record' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + host = FactoryGirl.create(:mdm_host, address: opts[:address], workspace_id: opts[:workspace_id]) + FactoryGirl.create(:mdm_service, host_id: host.id, port: opts[:port], proto: opts[:protocol]) + expect { test_object.create_credential_origin_service(opts)}.to_not change{Mdm::Service.count} + end + end + + context 'when there is no matching service record' do + it 'creates a new service record' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + expect { test_object.create_credential_origin_service(opts)}.to change{Mdm::Service.count}.by(1) + end + end + context 'when called twice with the same options' do it 'does not create duplicate objects' do opts = { @@ -115,4 +181,32 @@ describe Msf::Auxiliary::Report do end end + context '#create_credential_origin_session' do + it 'creates a Metasploit::Credential::Origin object' do + opts = { + post_reference_name: 'windows/gather/hashdump', + session_id: session.id + } + expect { test_object.create_credential_origin_session(opts)}.to change{Metasploit::Credential::Origin::Session.count}.by(1) + end + + context 'when called twice with the same options' do + it 'does not create duplicate objects' do + opts = { + post_reference_name: 'windows/gather/hashdump', + session_id: session.id + } + test_object.create_credential_origin_session(opts) + expect { test_object.create_credential_origin_session(opts)}.to_not change{Metasploit::Credential::Origin::Session.count} + end + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = {} + expect{ test_object.create_credential_origin_session(opts)}.to raise_error KeyError + end + end + end + end \ No newline at end of file From 9cdddb08d99af9055e67cf339b127618a92d0d03 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 20 May 2014 10:19:03 -0500 Subject: [PATCH 05/15] origin specs for realsies final specs and fixes for the origin creation methods --- lib/msf/core/auxiliary/report.rb | 1 + spec/lib/msf/core/auxiliary/report_spec.rb | 96 ++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 6cf8643b00..fed49b2d4e 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -74,6 +74,7 @@ module Auxiliary::Report end def create_credential_origin_session(opts={}) + return nil unless framework.db.active session_id = opts.fetch(:session_id) post_reference_name = opts.fetch(:post_reference_name) diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 2c505950ab..38381ad621 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -31,6 +31,12 @@ describe Msf::Auxiliary::Report do expect { test_object.create_credential_origin_import(opts)}.to change{Metasploit::Credential::Origin::Import.count}.by(1) end + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_origin_import).to be_nil + end + context 'when called twice with the same options' do it 'does not create duplicate objects' do opts = { @@ -62,6 +68,12 @@ describe Msf::Auxiliary::Report do expect { test_object.create_credential_origin_manual(opts)}.to change{Metasploit::Credential::Origin::Manual.count}.by(1) end + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_origin_manual).to be_nil + end + context 'when called twice with the same options' do it 'does not create duplicate objects' do opts = { @@ -94,6 +106,12 @@ describe Msf::Auxiliary::Report do expect { test_object.create_credential_origin_service(opts)}.to change{Metasploit::Credential::Origin::Service.count}.by(1) end + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_origin_service).to be_nil + end + context 'when there is a matching host record' do it 'creates a new host record' do opts = { @@ -190,6 +208,12 @@ describe Msf::Auxiliary::Report do expect { test_object.create_credential_origin_session(opts)}.to change{Metasploit::Credential::Origin::Session.count}.by(1) end + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_origin_session).to be_nil + end + context 'when called twice with the same options' do it 'does not create duplicate objects' do opts = { @@ -209,4 +233,76 @@ describe Msf::Auxiliary::Report do end end + context '#create_credential_origin' do + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_origin).to be_nil + end + + it 'calls the correct method to create Origin::Import records' do + opts = { + filename: "test_import.xml", + origin_type: :import, + task_id: task.id + } + my_module = test_object + expect(my_module).to receive(:create_credential_origin_import) + my_module.create_credential_origin(opts) + end + + it 'calls the correct method to create Origin::Manual records' do + opts = { + origin_type: :manual, + user_id: user.id + } + my_module = test_object + expect(my_module).to receive(:create_credential_origin_manual) + my_module.create_credential_origin(opts) + end + + it 'calls the correct method to create Origin::Service records' do + opts = { + address: '192.168.172.3', + port: 445, + service_name: 'smb', + protocol: 'tcp', + module_fullname: 'auxiliary/scanner/smb/smb_login', + workspace_id: framework.db.workspace.id, + origin_type: :service + } + my_module = test_object + expect(my_module).to receive(:create_credential_origin_service) + my_module.create_credential_origin(opts) + end + + it 'calls the correct method to create Origin::Session records' do + opts = { + origin_type: :session, + post_reference_name: 'windows/gather/hashdump', + session_id: session.id + } + my_module = test_object + expect(my_module).to receive(:create_credential_origin_session) + my_module.create_credential_origin(opts) + end + + it 'raises an exception if there is no origin type' do + opts = { + post_reference_name: 'windows/gather/hashdump', + session_id: session.id + } + expect{test_object.create_credential_origin(opts)}.to raise_error ArgumentError, "Unknown Origin Type " + end + + it 'raises an exception if given an invalid origin type' do + opts = { + origin_type: 'aaaaa', + post_reference_name: 'windows/gather/hashdump', + session_id: session.id + } + expect{test_object.create_credential_origin(opts)}.to raise_error ArgumentError, "Unknown Origin Type aaaaa" + end + end + end \ No newline at end of file From ce69f742a48b6629f47bff409bb3496cbfff4dd7 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 20 May 2014 11:16:19 -0500 Subject: [PATCH 06/15] add yarddocs to origin methods added YARD docs to the creation methods for Credential::Origins --- lib/msf/core/auxiliary/report.rb | 63 ++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index fed49b2d4e..9274969f7d 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -15,6 +15,32 @@ module Auxiliary::Report end + def create_credential_realm(opts={}) + + end + + # This method is responsible for creating the various Credential::Origin objects. + # It takes a key for the Origin type and delegates to the correct sub-method. + # + # @param opts [Hash] The options hash to use + # @option opts [Symbol] :origin_type The Origin type we are trying to create + # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to + # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to + # @option opts [String] :service_name The service name to use for the {Mdm::Service} + # @option opts [String] :protocol The protocol type of the {Mdm::Service} to link this Origin to + # @option opts [String] :module_fullname The fullname of the Metasploit Module to link this Origin to + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} + # @option opts [Fixnum] :task_id The ID of the {Mdm::Task} to link this Origin to + # @option opts [String] :filename The filename of the file that was imported + # @option opts [Fixnum] :user_id The ID of the {Mdm::User} to link this Origin to + # @option opts [Fixnum] :session_id The ID of the {Mdm::Session} to link this Origin to + # @option opts [String] :post_reference_name The reference name of the Metasploit Post module to link the origin to + # @raise [ArgumentError] if an invalid origin_type was provided + # @return [NilClass] if there is no connected database + # @return [Metasploit::Credential::Origin::Manual] if :origin_type was :manual + # @return [Metasploit::Credential::Origin::Import] if :origin_type was :import + # @return [Metasploit::Credential::Origin::Service] if :origin_type was :service + # @return [Metasploit::Credential::Origin::Session] if :origin_type was :session def create_credential_origin(opts={}) return nil unless framework.db.active case opts[:origin_type] @@ -31,6 +57,14 @@ module Auxiliary::Report end end + # This method is responsible for creating {Metasploit::Credential::Origin::Import} objects. + # + # @param opts [Hash] The options hash to use + # @option opts [Fixnum] :task_id The ID of the {Mdm::Task} to link this Origin to + # @option opts [String] :filename The filename of the file that was imported + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no connected database + # @return [Metasploit::Credential::Origin::Manual] The created {Metasploit::Credential::Origin::Import} object def create_credential_origin_import(opts={}) return nil unless framework.db.active task_id = opts.fetch(:task_id) @@ -41,6 +75,13 @@ module Auxiliary::Report origin_object end + # This method is responsible for creating {Metasploit::Credential::Origin::Manual} objects. + # + # @param opts [Hash] The options hash to use + # @option opts [Fixnum] :user_id The ID of the {Mdm::User} to link this Origin to + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no connected database + # @return [Metasploit::Credential::Origin::Manual] The created {Metasploit::Credential::Origin::Manual} object def create_credential_origin_manual(opts={}) return nil unless framework.db.active user_id = opts.fetch(:user_id) @@ -50,6 +91,20 @@ module Auxiliary::Report origin_object end + # This method is responsible for creating {Metasploit::Credential::Origin::Service} objects. + # If there is not a matching {Mdm::Host} it will create it. If there is not a matching + # {Mdm::Service} it will create that too. + # + # @param opts [Hash] The options hash to use + # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to + # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to + # @option opts [String] :service_name The service name to use for the {Mdm::Service} + # @option opts [String] :protocol The protocol type of the {Mdm::Service} to link this Origin to + # @option opts [String] :module_fullname The fullname of the Metasploit Module to link this Origin to + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no connected database + # @return [Metasploit::Credential::Origin::Service] The created {Metasploit::Credential::Origin::Service} object def create_credential_origin_service(opts={}) return nil unless framework.db.active address = opts.fetch(:address) @@ -73,6 +128,14 @@ module Auxiliary::Report origin_object end + # This method is responsible for creating {Metasploit::Credential::Origin::Session} objects. + # + # @param opts [Hash] The options hash to use + # @option opts [Fixnum] :session_id The ID of the {Mdm::Session} to link this Origin to + # @option opts [String] :post_reference_name The reference name of the Metasploit Post module to link the origin to + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no connected database + # @return [Metasploit::Credential::Origin::Session] The created {Metasploit::Credential::Origin::Session} object def create_credential_origin_session(opts={}) return nil unless framework.db.active session_id = opts.fetch(:session_id) From 2629549f6f8d2c1d22f792ce24a781daaa2af428 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 21 May 2014 11:22:22 -0500 Subject: [PATCH 07/15] added realm creation added method for creating credential realm creation. --- lib/msf/core/auxiliary/report.rb | 5 ++++ spec/lib/msf/core/auxiliary/report_spec.rb | 34 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 9274969f7d..67ecac2d2a 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -16,7 +16,12 @@ module Auxiliary::Report end def create_credential_realm(opts={}) + return nil unless framework.db.active + realm_key = opts.fetch(:realm_key) + realm_value = opts.fetch(:realm_value) + realm_object = Metasploit::Credential::Realm.where(key: realm_key, value: realm_value).first_or_create + realm_object.save! end # This method is responsible for creating the various Credential::Origin objects. diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 38381ad621..509f12334b 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -305,4 +305,38 @@ describe Msf::Auxiliary::Report do end end + context '#create_credential_realm' do + it 'creates a Metasploit::Credential::Realm object' do + opts = { + realm_key: 'Active Directory Domain', + realm_value: 'contosso' + } + expect { test_object.create_credential_realm(opts)}.to change{Metasploit::Credential::Realm.count}.by(1) + end + + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_realm).to be_nil + end + + context 'when called twice with the same options' do + it 'does not create duplicate objects' do + opts = { + realm_key: 'Active Directory Domain', + realm_value: 'contosso' + } + test_object.create_credential_realm(opts) + expect { test_object.create_credential_realm(opts)}.to_not change{Metasploit::Credential::Realm.count} + end + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = {} + expect{ test_object.create_credential_origin_manual(opts)}.to raise_error KeyError + end + end + end + end \ No newline at end of file From 3ea99a9d432b511bc22d8bf2a62c9272400953ac Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 21 May 2014 13:21:56 -0500 Subject: [PATCH 08/15] private creation w/ specs and docs the private creation method is now done with specs and YARD docs --- lib/msf/core/auxiliary/report.rb | 47 ++++++++++++++++++ spec/lib/msf/core/auxiliary/report_spec.rb | 55 ++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 67ecac2d2a..4119ff60ac 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -13,8 +13,54 @@ module Auxiliary::Report return nil unless framework.db.active origin = create_credential_origin(opts) + if opts.has_key?(:realm_key) && opts.has_key?(:realm_value) + realm = create_credential_realm(opts) + end + end + # This method is responsible for the creation of {Metasploit::Credential::Private} objects. + # It will create the correct subclass based on the type. + # + # @param opts [Hash] The options hash to use + # @option opts [String] :private_data The actual data for the private (e.g. password, hash, key etc) + # @option opts [Symbol] :private_type The type of {Metasploit::Credential::Private} to create + # @raise [ArgumentError] if a valid type is not supplied + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Password] if the private_type was :password + # @return [Metasploit::Credential::SSHKey] if the private_type was :ssh_key + # @return [Metasploit::Credential::NTLMHash] if the private_type was :ntlm_hash + # @return [Metasploit::Credential::NonreplayableHash] if the private_type was :nonreplayable_hash + def create_credential_private(opts={}) + return nil unless framework.db.active + private_data = opts.fetch(:private_data) + private_type = opts.fetch(:private_type) + + case private_type + when :password + private_object = Metasploit::Credential::Password.where(data: private_data).first_or_create + when :ssh_key + private_object = Metasploit::Credential::SSHKey.where(data: private_data).first_or_create + when :ntlm_hash + private_object = Metasploit::Credential::NTLMHash.where(data: private_data).first_or_create + when :nonreplayable_hash + private_object = Metasploit::Credential::NonreplayableHash.where(data: private_data).first_or_create + else + raise ArgumentError, "Invalid Private type: #{private_type}" + end + private_object.save! + end + + # This method is responsible for creating the {Metasploit::Credential::Realm} objects + # that may be required. + # + # @param opts [Hash] The options hash to use + # @option opts [String] :realm_key The type of Realm this is (e.g. 'Active Directory Domain') + # @option opts [String] :realm_value The actual Realm name (e.g. contosso) + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Realm] if it successfully creates or finds the object def create_credential_realm(opts={}) return nil unless framework.db.active realm_key = opts.fetch(:realm_key) @@ -41,6 +87,7 @@ module Auxiliary::Report # @option opts [Fixnum] :session_id The ID of the {Mdm::Session} to link this Origin to # @option opts [String] :post_reference_name The reference name of the Metasploit Post module to link the origin to # @raise [ArgumentError] if an invalid origin_type was provided + # @raise [KeyError] if a required option is missing # @return [NilClass] if there is no connected database # @return [Metasploit::Credential::Origin::Manual] if :origin_type was :manual # @return [Metasploit::Credential::Origin::Import] if :origin_type was :import diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 509f12334b..47bade62bb 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -339,4 +339,59 @@ describe Msf::Auxiliary::Report do end end + context '#create_credential_private' do + it 'should return nil if there is no database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_private).to be_nil + end + + context 'when missing an option' do + it 'throws a KeyError' do + opts = {} + expect{ test_object.create_credential_private(opts)}.to raise_error KeyError + end + end + + context 'when :private_type is password' do + it 'creates a Metasploit::Credential::Password' do + opts = { + private_data: 'password1', + private_type: :password + } + expect{ test_object.create_credential_private(opts) }.to change{Metasploit::Credential::Password.count}.by(1) + end + end + + context 'when :private_type is sshkey' do + it 'creates a Metasploit::Credential::SSHKey' do + opts = { + private_data: OpenSSL::PKey::RSA.generate(2048).to_s, + private_type: :ssh_key + } + expect{ test_object.create_credential_private(opts) }.to change{Metasploit::Credential::SSHKey.count}.by(1) + end + end + + context 'when :private_type is ntlmhash' do + it 'creates a Metasploit::Credential::NTLMHash' do + opts = { + private_data: Metasploit::Credential::NTLMHash.data_from_password_data('password1'), + private_type: :ntlm_hash + } + expect{ test_object.create_credential_private(opts) }.to change{Metasploit::Credential::NTLMHash.count}.by(1) + end + end + + context 'when :private_type is nonreplayable_hash' do + it 'creates a Metasploit::Credential::NonreplayableHash' do + opts = { + private_data: '10b222970537b97919db36ec757370d2', + private_type: :nonreplayable_hash + } + expect{ test_object.create_credential_private(opts) }.to change{Metasploit::Credential::NonreplayableHash.count}.by(1) + end + end + end + end \ No newline at end of file From 19e36cccb3980064771a5d389085a9acaa78f751 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 21 May 2014 16:37:13 -0500 Subject: [PATCH 09/15] Credential Core creation now complete --- lib/msf/core/auxiliary/report.rb | 49 +++++++++++++++++++++- spec/lib/msf/core/auxiliary/report_spec.rb | 31 ++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 4119ff60ac..68fe5955ba 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -13,10 +13,42 @@ module Auxiliary::Report return nil unless framework.db.active origin = create_credential_origin(opts) + core_opts = { workspace_id: opts.fetch(:workspace_id) } + if opts.has_key?(:realm_key) && opts.has_key?(:realm_value) - realm = create_credential_realm(opts) + core_opts[:realm] = create_credential_realm(opts) end + if opts.has_key?(:private_type) && opts.has_key(:private_data) + core_opts[:private] = create_credential_private(opts) + end + + if opts.has_key(:username) + core_opts[:public] = create_credential_public(opts) + end + + + end + + # This method is responsible for creating {Metasploit::Credential::Core} objects. + # + # @param opts [Hash] The options hash to use + # @option opts [Metasploit::Credential::Origin] :origin The origin object to tie the core to + # @option opts [Metasploit::Credential::Public] :public The {Metasploit::Credential::Public} component + # @option opts [Metasploit::Credential::Private] :private The {Metasploit::Credential::Private} component + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to tie the Core to + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Core] + def create_credential_core(opts={}) + return nil unless framework.db.active + origin = opts.fetch(:origin) + workspace_id = opts.fetch(:workspace_id) + + core = Metasploit::Credential::Core.where(private_id: opts[:private].id, public_id: opts[:public].id, realm_id: opts[:realm].id, workspace_id: opts[:workspace_id]).first_or_create + if core.origin_id.nil? + core.origin = origin + end + core.save! end # This method is responsible for the creation of {Metasploit::Credential::Private} objects. @@ -52,6 +84,21 @@ module Auxiliary::Report private_object.save! end + # This method is responsible for the creation of {Metasploit::Credential::Public} objects. + # + # @param opts [Hash] The options hash to use + # @option opts [String] :username The username to use for the {Metasploit::Credential::Public} + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Public] + def create_credential_public(opts={}) + return nil unless framework.db.active + username = opts.fetch(:username) + + public_object = Metasploit::Credential::Public.where(username: username).first_or_create + public_object.save! + end + # This method is responsible for creating the {Metasploit::Credential::Realm} objects # that may be required. # diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 47bade62bb..5c51e6b4e1 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -394,4 +394,35 @@ describe Msf::Auxiliary::Report do end end + context '#create_credential_core' do + let(:origin) { FactoryGirl.create(:metasploit_credential_origin_service) } + let(:public) { FactoryGirl.create(:metasploit_credential_public)} + let(:private) { FactoryGirl.create(:metasploit_credential_password)} + let(:realm) { FactoryGirl.create(:metasploit_credential_realm)} + + it 'raises a KeyError if any required option is missing' do + opts = {} + expect{ test_object.create_credential_core(opts)}.to raise_error KeyError + end + + it 'returns nil if there is no active database connection' do + my_module = test_object + expect(my_module.framework.db).to receive(:active).and_return(false) + expect(my_module.create_credential_core).to be_nil + end + + it 'creates a Metasploit::Credential::Core' do + opts = { + origin: origin, + public: public, + private: private, + realm: realm, + workspace_id: origin.service.host.workspace_id + } + expect{test_object.create_credential_core(opts)}.to change{Metasploit::Credential::Core.count}.by(1) + end + + + end + end \ No newline at end of file From ac9af000af2392a2734ed1ba2276f42bbc53148c Mon Sep 17 00:00:00 2001 From: David Maloney Date: Thu, 22 May 2014 13:53:26 -0500 Subject: [PATCH 10/15] full cred creation rotuine done creating Logins as a seperate method, both methods are done and fully documented. --- lib/msf/core/auxiliary/report.rb | 152 +++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 19 deletions(-) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 68fe5955ba..b5326c390e 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -9,25 +9,65 @@ module Msf module Auxiliary::Report + # This method is responsible for creation {Metasploit::Credential::Core} objects + # and all sub-objects that it is dependent upon. + # + # @param opts [Hash] The options hash to use + # @option opts [Symbol] :origin_type The Origin type we are trying to create + # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to + # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to + # @option opts [String] :service_name The service name to use for the {Mdm::Service} + # @option opts [String] :protocol The protocol type of the {Mdm::Service} to link this Origin to + # @option opts [String] :module_fullname The fullname of the Metasploit Module to link this Origin to + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} + # @option opts [Fixnum] :task_id The ID of the {Mdm::Task} to link this Origin to + # @option opts [String] :filename The filename of the file that was imported + # @option opts [Fixnum] :user_id The ID of the {Mdm::User} to link this Origin to + # @option opts [Fixnum] :session_id The ID of the {Mdm::Session} to link this Origin to + # @option opts [String] :post_reference_name The reference name of the Metasploit Post module to link the origin to + # @option opts [String] :private_data The actual data for the private (e.g. password, hash, key etc) + # @option opts [Symbol] :private_type The type of {Metasploit::Credential::Private} to create + # @option opts [String] :username The username to use for the {Metasploit::Credential::Public} + # @raise [KeyError] if a required option is missing + # @raise [ArgumentError] if an invalid :private_type is specified + # @raise [ArgumentError] if an invalid :origin_type is specified + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Core] + # @example Reporting a Bruteforced Credential + # create_credential( + # origin_type: :service, + # address: '192.168.1.100', + # port: 445, + # service_name: 'smb', + # protocol: 'tcp', + # module_fullname: 'auxiliary/scanner/smb/smb_login', + # workspace_id: myworkspace.id, + # private_data: 'password1', + # private_type: :password, + # username: 'Administrator' + # ) def create_credential(opts={}) return nil unless framework.db.active origin = create_credential_origin(opts) - core_opts = { workspace_id: opts.fetch(:workspace_id) } + core_opts = { + origin: origin, + workspace_id: opts.fetch(:workspace_id) + } if opts.has_key?(:realm_key) && opts.has_key?(:realm_value) core_opts[:realm] = create_credential_realm(opts) end - if opts.has_key?(:private_type) && opts.has_key(:private_data) + if opts.has_key?(:private_type) && opts.has_key?(:private_data) core_opts[:private] = create_credential_private(opts) end - if opts.has_key(:username) + if opts.has_key?(:username) core_opts[:public] = create_credential_public(opts) end - + create_credential_core(core_opts) end # This method is responsible for creating {Metasploit::Credential::Core} objects. @@ -44,11 +84,61 @@ module Auxiliary::Report origin = opts.fetch(:origin) workspace_id = opts.fetch(:workspace_id) - core = Metasploit::Credential::Core.where(private_id: opts[:private].id, public_id: opts[:public].id, realm_id: opts[:realm].id, workspace_id: opts[:workspace_id]).first_or_create + if opts[:private] + private_id = opts[:private].id + else + private_id = nil + end + + if opts[:public] + public_id = opts[:public].id + else + public_id = nil + end + + if opts[:realm] + realm_id = opts[:realm].id + else + realm_id = nil + end + + core = Metasploit::Credential::Core.where(private_id: private_id, public_id: public_id, realm_id: realm_id, workspace_id: workspace_id).first_or_create if core.origin_id.nil? core.origin = origin end core.save! + core + end + + # This method is responsible for creating a {Metasploit::Credential::Login} object + # which ties a {Metasploit::Credential::Core} to the {Mdm::Service} it is a valid + # credential for. + # + # @param opts [Hash] The options hash to use + # @option opts [String] :access_level The access level to assign to this login if we know it + # @option opts [String] :address The address of the {Mdm::Host} to link this Login to + # @option opts [Metasploit::Credential::Core] :core The {Metasploit::Credential::Core} to link this login to + # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Login to + # @option opts [String] :service_name The service name to use for the {Mdm::Service} + # @option opts [String] :status The status for the Login object + # @option opts [String] :protocol The protocol type of the {Mdm::Service} to link this Login to + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no active database connection + # @return [Metasploit::Credential::Login] + def create_credential_login(opts) + return nil unless framework.db.active + access_level = opts.fetch(:access_level, nil) + core = opts.fetch(:core) + status = opts.fetch(:status) + + service_object = create_credential_service(opts) + login_object = Metasploit::Credential::Login.where(core_id: core.id, service_id: service_object.id).first_or_create + + login_object.access_level = access_level if access_level + login_object.status = status + login_object.save! + login_object end # This method is responsible for the creation of {Metasploit::Credential::Private} objects. @@ -82,6 +172,7 @@ module Auxiliary::Report raise ArgumentError, "Invalid Private type: #{private_type}" end private_object.save! + private_object end # This method is responsible for the creation of {Metasploit::Credential::Public} objects. @@ -97,6 +188,7 @@ module Auxiliary::Report public_object = Metasploit::Credential::Public.where(username: username).first_or_create public_object.save! + public_object end # This method is responsible for creating the {Metasploit::Credential::Realm} objects @@ -115,6 +207,7 @@ module Auxiliary::Report realm_object = Metasploit::Credential::Realm.where(key: realm_key, value: realm_value).first_or_create realm_object.save! + realm_object end # This method is responsible for creating the various Credential::Origin objects. @@ -200,27 +293,14 @@ module Auxiliary::Report # @option opts [String] :service_name The service name to use for the {Mdm::Service} # @option opts [String] :protocol The protocol type of the {Mdm::Service} to link this Origin to # @option opts [String] :module_fullname The fullname of the Metasploit Module to link this Origin to - # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} # @raise [KeyError] if a required option is missing # @return [NilClass] if there is no connected database # @return [Metasploit::Credential::Origin::Service] The created {Metasploit::Credential::Origin::Service} object def create_credential_origin_service(opts={}) return nil unless framework.db.active - address = opts.fetch(:address) - port = opts.fetch(:port) - service_name = opts.fetch(:service_name) - protocol = opts.fetch(:protocol) module_fullname = opts.fetch(:module_fullname) - workspace_id = opts.fetch(:workspace_id) - # Find or create the host object we need - host_object = Mdm::Host.where(address: address, workspace_id: workspace_id).first_or_create - host_object.save! - - # Next we find or create the Service object we need - service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol).first_or_create - service_object.name = service_name - service_object.save! + service_object = create_credential_service(opts) origin_object = Metasploit::Credential::Origin::Service.where(service_id: service_object.id, module_full_name: module_fullname).first_or_create origin_object.save! @@ -245,6 +325,37 @@ module Auxiliary::Report origin_object end + # This method is responsible for creating a barebones {Mdm::Service} object + # for use by Credential object creation. + # + # @option opts [String] :address The address of the {Mdm::Host} + # @option opts [Fixnum] :port The port number of the {Mdm::Service} + # @option opts [String] :service_name The service name to use for the {Mdm::Service} + # @option opts [String] :protocol The protocol type of the {Mdm::Service} + # @option opts [Fixnum] :workspace_id The ID of the {Mdm::Workspace} to use for the {Mdm::Host} + # @raise [KeyError] if a required option is missing + # @return [NilClass] if there is no connected database + # @return [Mdm::Service] + def create_credential_service(opts={}) + return nil unless framework.db.active + address = opts.fetch(:address) + port = opts.fetch(:port) + service_name = opts.fetch(:service_name) + protocol = opts.fetch(:protocol) + workspace_id = opts.fetch(:workspace_id) + + # Find or create the host object we need + host_object = Mdm::Host.where(address: address, workspace_id: workspace_id).first_or_create + host_object.save! + + # Next we find or create the Service object we need + service_object = Mdm::Service.where(host_id: host_object.id, port: port, proto: protocol).first_or_create + service_object.name = service_name + service_object.save! + service_object + end + + # Shortcut method for detecting when the DB is active def db framework.db.active @@ -618,6 +729,9 @@ module Auxiliary::Report print_status "Collecting #{cred_opts[:user]}:#{cred_opts[:pass]}" framework.db.report_auth_info(cred_opts) end + + + end end From 32b88c2db65192c7bd7c44e98f1b5558a1a96cfa Mon Sep 17 00:00:00 2001 From: David Maloney Date: Fri, 23 May 2014 10:58:21 -0500 Subject: [PATCH 11/15] final fixes to login creation --- lib/msf/core/auxiliary/report.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index b5326c390e..05ed59a600 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -117,6 +117,7 @@ module Auxiliary::Report # @param opts [Hash] The options hash to use # @option opts [String] :access_level The access level to assign to this login if we know it # @option opts [String] :address The address of the {Mdm::Host} to link this Login to + # @option opts [DateTime] :last_attempted_at The last time this Login was attempted # @option opts [Metasploit::Credential::Core] :core The {Metasploit::Credential::Core} to link this login to # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Login to # @option opts [String] :service_name The service name to use for the {Mdm::Service} @@ -128,15 +129,17 @@ module Auxiliary::Report # @return [Metasploit::Credential::Login] def create_credential_login(opts) return nil unless framework.db.active - access_level = opts.fetch(:access_level, nil) - core = opts.fetch(:core) - status = opts.fetch(:status) + access_level = opts.fetch(:access_level, nil) + core = opts.fetch(:core) + last_attempted_at = opts.fetch(:last_attempted_at, nil) + status = opts.fetch(:status) service_object = create_credential_service(opts) login_object = Metasploit::Credential::Login.where(core_id: core.id, service_id: service_object.id).first_or_create - login_object.access_level = access_level if access_level - login_object.status = status + login_object.access_level = access_level if access_level + login_object.last_attempted_at = last_attempted_at if last_attempted_at + login_object.status = status login_object.save! login_object end From c975d4dc4994ffc90f873a5c42537d49faae0cfb Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 28 May 2014 09:26:19 -0500 Subject: [PATCH 12/15] some minor cleanup items --- Gemfile | 1 - spec/lib/msf/core/auxiliary/report_spec.rb | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index fb2fbcd944..246852e70c 100755 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,4 @@ source 'https://rubygems.org' -gem 'pry' gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.1.2-metasploit-credential' # Need 3+ for ActiveSupport::Concern diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 5c51e6b4e1..9016d39bb4 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -113,7 +113,7 @@ describe Msf::Auxiliary::Report do end context 'when there is a matching host record' do - it 'creates a new host record' do + it 'uses the existing host record' do opts = { address: '192.168.172.3', port: 445, @@ -129,7 +129,7 @@ describe Msf::Auxiliary::Report do end context 'when there is not a matching host record' do - it 'uses the existing host record' do + it 'create a new host record' do opts = { address: '192.168.172.3', port: 445, @@ -139,7 +139,7 @@ describe Msf::Auxiliary::Report do workspace_id: framework.db.workspace.id, origin_type: :service } - expect { test_object.create_credential_origin_service(opts)}.to change{Metasploit::Credential::Origin::Service.count}.by(1) + expect { test_object.create_credential_origin_service(opts)}.to change{Mdm::Host.count}.by(1) end end @@ -228,7 +228,7 @@ describe Msf::Auxiliary::Report do context 'when missing an option' do it 'throws a KeyError' do opts = {} - expect{ test_object.create_credential_origin_session(opts)}.to raise_error KeyError + expect{ test_object.create_credential_origin_session(opts) }.to raise_error KeyError end end end From deabd1c3b0ddc2896bf2f7eab76035311f571c34 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 28 May 2014 09:30:45 -0500 Subject: [PATCH 13/15] tidy the YARD some more cleanup, in the YARD docs this time. --- lib/msf/core/auxiliary/report.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index 05ed59a600..c237e24128 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -12,7 +12,6 @@ module Auxiliary::Report # This method is responsible for creation {Metasploit::Credential::Core} objects # and all sub-objects that it is dependent upon. # - # @param opts [Hash] The options hash to use # @option opts [Symbol] :origin_type The Origin type we are trying to create # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to @@ -72,7 +71,6 @@ module Auxiliary::Report # This method is responsible for creating {Metasploit::Credential::Core} objects. # - # @param opts [Hash] The options hash to use # @option opts [Metasploit::Credential::Origin] :origin The origin object to tie the core to # @option opts [Metasploit::Credential::Public] :public The {Metasploit::Credential::Public} component # @option opts [Metasploit::Credential::Private] :private The {Metasploit::Credential::Private} component @@ -114,7 +112,6 @@ module Auxiliary::Report # which ties a {Metasploit::Credential::Core} to the {Mdm::Service} it is a valid # credential for. # - # @param opts [Hash] The options hash to use # @option opts [String] :access_level The access level to assign to this login if we know it # @option opts [String] :address The address of the {Mdm::Host} to link this Login to # @option opts [DateTime] :last_attempted_at The last time this Login was attempted @@ -147,7 +144,6 @@ module Auxiliary::Report # This method is responsible for the creation of {Metasploit::Credential::Private} objects. # It will create the correct subclass based on the type. # - # @param opts [Hash] The options hash to use # @option opts [String] :private_data The actual data for the private (e.g. password, hash, key etc) # @option opts [Symbol] :private_type The type of {Metasploit::Credential::Private} to create # @raise [ArgumentError] if a valid type is not supplied @@ -180,7 +176,6 @@ module Auxiliary::Report # This method is responsible for the creation of {Metasploit::Credential::Public} objects. # - # @param opts [Hash] The options hash to use # @option opts [String] :username The username to use for the {Metasploit::Credential::Public} # @raise [KeyError] if a required option is missing # @return [NilClass] if there is no active database connection @@ -197,7 +192,6 @@ module Auxiliary::Report # This method is responsible for creating the {Metasploit::Credential::Realm} objects # that may be required. # - # @param opts [Hash] The options hash to use # @option opts [String] :realm_key The type of Realm this is (e.g. 'Active Directory Domain') # @option opts [String] :realm_value The actual Realm name (e.g. contosso) # @raise [KeyError] if a required option is missing @@ -216,7 +210,6 @@ module Auxiliary::Report # This method is responsible for creating the various Credential::Origin objects. # It takes a key for the Origin type and delegates to the correct sub-method. # - # @param opts [Hash] The options hash to use # @option opts [Symbol] :origin_type The Origin type we are trying to create # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to @@ -254,7 +247,6 @@ module Auxiliary::Report # This method is responsible for creating {Metasploit::Credential::Origin::Import} objects. # - # @param opts [Hash] The options hash to use # @option opts [Fixnum] :task_id The ID of the {Mdm::Task} to link this Origin to # @option opts [String] :filename The filename of the file that was imported # @raise [KeyError] if a required option is missing @@ -272,7 +264,6 @@ module Auxiliary::Report # This method is responsible for creating {Metasploit::Credential::Origin::Manual} objects. # - # @param opts [Hash] The options hash to use # @option opts [Fixnum] :user_id The ID of the {Mdm::User} to link this Origin to # @raise [KeyError] if a required option is missing # @return [NilClass] if there is no connected database @@ -290,7 +281,6 @@ module Auxiliary::Report # If there is not a matching {Mdm::Host} it will create it. If there is not a matching # {Mdm::Service} it will create that too. # - # @param opts [Hash] The options hash to use # @option opts [String] :address The address of the {Mdm::Host} to link this Origin to # @option opts [Fixnum] :port The port number of the {Mdm::Service} to link this Origin to # @option opts [String] :service_name The service name to use for the {Mdm::Service} @@ -312,7 +302,6 @@ module Auxiliary::Report # This method is responsible for creating {Metasploit::Credential::Origin::Session} objects. # - # @param opts [Hash] The options hash to use # @option opts [Fixnum] :session_id The ID of the {Mdm::Session} to link this Origin to # @option opts [String] :post_reference_name The reference name of the Metasploit Post module to link the origin to # @raise [KeyError] if a required option is missing From d8adb3a814b851248aaad1eb0ae279ed31e4d27e Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 28 May 2014 09:42:05 -0500 Subject: [PATCH 14/15] fix gemfile --- Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile b/Gemfile index 98656dc6df..52e946efb1 100755 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,4 @@ source 'https://rubygems.org' -gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.1.2-metasploit-credential' # Need 3+ for ActiveSupport::Concern gem 'activesupport', '>= 3.0.0', '< 4.0.0' From 821a62627ab0d940a766e2c125e2266d5860f7d4 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 28 May 2014 09:56:26 -0500 Subject: [PATCH 15/15] final spec cleanup --- Gemfile.lock | 8 ----- spec/lib/msf/core/auxiliary/report_spec.rb | 40 +++++++++------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8dffe95c19..5dbc63e2e2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,7 +35,6 @@ GEM arel (3.0.3) bcrypt (3.1.7) builder (3.0.4) - coderay (1.1.0) diff-lcs (1.2.5) erubis (2.7.0) factory_girl (4.4.0) @@ -54,7 +53,6 @@ GEM activerecord (>= 3.2.13, < 4.0.0) activesupport pg - method_source (0.8.2) mini_portile (0.5.3) msgpack (0.5.8) multi_json (1.0.4) @@ -64,10 +62,6 @@ GEM packetfu (1.1.9) pcaprub (0.11.3) pg (0.17.1) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) - slop (~> 3.4) rack (1.4.5) rack-cache (1.2) rack (>= 0.4) @@ -111,7 +105,6 @@ GEM multi_json (~> 1.0.3) simplecov-html (~> 0.5.3) simplecov-html (0.5.3) - slop (3.4.7) sprockets (2.2.2) hike (~> 1.2) multi_json (~> 1.0) @@ -143,7 +136,6 @@ DEPENDENCIES packetfu (= 1.1.9) pcaprub pg (>= 0.11) - pry railties rake (>= 10.0.0) redcarpet diff --git a/spec/lib/msf/core/auxiliary/report_spec.rb b/spec/lib/msf/core/auxiliary/report_spec.rb index 9016d39bb4..bcd0395191 100644 --- a/spec/lib/msf/core/auxiliary/report_spec.rb +++ b/spec/lib/msf/core/auxiliary/report_spec.rb @@ -32,9 +32,8 @@ describe Msf::Auxiliary::Report do end it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_origin_import).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_origin_import).to be_nil end context 'when called twice with the same options' do @@ -69,9 +68,8 @@ describe Msf::Auxiliary::Report do end it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_origin_manual).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_origin_manual).to be_nil end context 'when called twice with the same options' do @@ -209,9 +207,8 @@ describe Msf::Auxiliary::Report do end it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_origin_session).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_origin_session).to be_nil end context 'when called twice with the same options' do @@ -235,9 +232,8 @@ describe Msf::Auxiliary::Report do context '#create_credential_origin' do it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_origin).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_origin).to be_nil end it 'calls the correct method to create Origin::Import records' do @@ -271,9 +267,8 @@ describe Msf::Auxiliary::Report do workspace_id: framework.db.workspace.id, origin_type: :service } - my_module = test_object - expect(my_module).to receive(:create_credential_origin_service) - my_module.create_credential_origin(opts) + expect(test_object).to receive(:create_credential_origin_service) + test_object.create_credential_origin(opts) end it 'calls the correct method to create Origin::Session records' do @@ -315,9 +310,8 @@ describe Msf::Auxiliary::Report do end it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_realm).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_realm).to be_nil end context 'when called twice with the same options' do @@ -341,9 +335,8 @@ describe Msf::Auxiliary::Report do context '#create_credential_private' do it 'should return nil if there is no database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_private).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_private).to be_nil end context 'when missing an option' do @@ -406,9 +399,8 @@ describe Msf::Auxiliary::Report do end it 'returns nil if there is no active database connection' do - my_module = test_object - expect(my_module.framework.db).to receive(:active).and_return(false) - expect(my_module.create_credential_core).to be_nil + expect(test_object.framework.db).to receive(:active).and_return(false) + expect(test_object.create_credential_core).to be_nil end it 'creates a Metasploit::Credential::Core' do