diff --git a/lib/msf/core/auxiliary/report_summary.rb b/lib/msf/core/auxiliary/report_summary.rb new file mode 100644 index 000000000000..e818fd3e3785 --- /dev/null +++ b/lib/msf/core/auxiliary/report_summary.rb @@ -0,0 +1,147 @@ +# -*- coding: binary -*- + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# https://metasploit.com/framework/ +## + +module Msf + class Auxiliary + ### + # + # This module provides a means to report module summaries + # + ### + module ReportSummary + def initialize(info = {}) + super(info) + + if framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) + register_options( + [ + OptBool.new('ShowSuccessfulLogins', [false, 'Outputs a table of successful logins', true]), + ] + ) + end + end + + def run + return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] + + @report = {} + @report.extend(::Rex::Ref) + rhost_walker = Msf::RhostsWalker.new(datastore['RHOSTS'], datastore).to_enum + conditional_verbose_output(rhost_walker.count) + result = super + print_report_summary + result + end + + # Creates a credential and adds to to the DB if one is present + # + # @param [Hash] credential_data + # @return [Metasploit::Credential::Login] + def create_credential_login(credential_data) + return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] + + credential = { + public: credential_data[:username], + private_data: credential_data[:private_data] + } + @report[rhost] = { successful_logins: [] } + @report[rhost][:successful_logins] << credential + super + end + + # Framework is notified that we have a new session opened + # + # @param [MetasploitModule] obj + # @param [Object] info + # @param [Hash] ds_merge + # @param [FalseClass] crlf + # @param [Socket] sock + # @param [Msf::Sessions::] sess + # @return [Msf::Sessions::] + def start_session(obj, info, ds_merge, crlf = false, sock = nil, sess = nil) + return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] + + result = super + @report[rhost].merge!({ successful_sessions: [] }) + @report[rhost][:successful_sessions] << result + result + end + + private + + # Prints a summary of successful logins + # Returns a ::Rex::Text::Table with the following data: host, public and private credentials for each + # successful login per host + # + # @return [Hash] Rhost keys mapped to successful logins and sessions for each host + def print_report_summary + report = @report + + logins = report.flat_map { |_k, v| v[:successful_logins] }.compact + sessions = report.flat_map { |_k, v| v[:successful_sessions] }.compact + + print_status("Scan completed, #{logins.size} #{logins.size == 1 ? 'credential was' : 'credentials were'} successful.") + print_successful_logins(report) + + if datastore['CreateSession'] + print_status("#{sessions.size} #{sessions.size == 1 ? 'session was' : 'sessions were'} opened successfully.") + end + + report + end + + # Logic to detect if the ShowSuccessLogins datastore option has been set + # + # @param [Hash] report Host mapped to successful logins and sessions + # @return [String] Rex::Text::Table containing successful logins + def print_successful_logins(report) + if datastore['ShowSuccessfulLogins'] == true && !report.empty? + table = successful_logins_to_table(report) + print_line("\n" + table.to_s + "\n") + end + end + + # The idea here is to add a hybrid approach for scanner modules + # If only one host is scanned a more verbose output is useful to the user + # If scanning multiple hosts we would want more lightweight information + # + # @param [Object] host_count The number of hosts + def conditional_verbose_output(host_count) + if host_count == 1 + datastore['Verbose'] = true + end + end + + # Takes the login/session results and converts them into a Rex::Text::Table format + # + # @param report [Hash{String => [Metasploit::Framework::LoginScanner::Result, Msf::Sessions]}] + # @return [Rex::Text::WrappedTable] Rex::Text::Table containing successful logins + def successful_logins_to_table(report) + field_headers = %w[Host Public Private] + + markdown_fields = report.flat_map do |host, result| + if result[:successful_logins].nil? + next + end + + result[:successful_logins].map do |credential| + [host, credential[:public], credential[:private_data]] + end + end + + ::Rex::Text::Table.new( + 'Header' => 'Successful logins', + 'Indent' => 4, + 'Columns' => field_headers, + 'Rows' => markdown_fields.compact + ) + end + end + end +end diff --git a/lib/msf/core/feature_manager.rb b/lib/msf/core/feature_manager.rb index 4c67e01fa423..3ab3865ae771 100644 --- a/lib/msf/core/feature_manager.rb +++ b/lib/msf/core/feature_manager.rb @@ -27,6 +27,7 @@ class FeatureManager MYSQL_SESSION_TYPE = 'mysql_session_type' MSSQL_SESSION_TYPE = 'mssql_session_type' LDAP_SESSION_TYPE = 'ldap_session_type' + SHOW_SUCCESSFUL_LOGINS = 'show_successful_logins' DEFAULTS = [ { @@ -103,6 +104,13 @@ class FeatureManager default_value: false, developer_notes: 'To be enabled by default after appropriate testing' }.freeze, + { + name: SHOW_SUCCESSFUL_LOGINS, + description: 'When enabled scanners/login modules will return a table off successful logins once the module completes', + requires_restart: false, + default_value: false, + developer_notes: 'To be enabled after appropriate testing' + }.freeze, { name: DNS, description: 'When enabled allows configuration of DNS resolution behaviour in Metasploit', diff --git a/modules/auxiliary/cloud/aws/enum_ssm.rb b/modules/auxiliary/cloud/aws/enum_ssm.rb index e4be5d3cbe7f..e1667d76805a 100644 --- a/modules/auxiliary/cloud/aws/enum_ssm.rb +++ b/modules/auxiliary/cloud/aws/enum_ssm.rb @@ -11,6 +11,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super( diff --git a/modules/auxiliary/scanner/ldap/ldap_login.rb b/modules/auxiliary/scanner/ldap/ldap_login.rb index e9a7707273ee..45fdf3adb4a3 100644 --- a/modules/auxiliary/scanner/ldap/ldap_login.rb +++ b/modules/auxiliary/scanner/ldap/ldap_login.rb @@ -13,6 +13,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::LDAP include Msf::Sessions::CreateSessionOptions include Msf::Auxiliary::CommandShell + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super( diff --git a/modules/auxiliary/scanner/mssql/mssql_login.rb b/modules/auxiliary/scanner/mssql/mssql_login.rb index 859b78d9644d..08381fd7a84e 100644 --- a/modules/auxiliary/scanner/mssql/mssql_login.rb +++ b/modules/auxiliary/scanner/mssql/mssql_login.rb @@ -15,6 +15,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::CommandShell include Msf::Auxiliary::Scanner include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/mysql/mysql_login.rb b/modules/auxiliary/scanner/mysql/mysql_login.rb index cec15d727f4b..e7172d92f3ef 100644 --- a/modules/auxiliary/scanner/mysql/mysql_login.rb +++ b/modules/auxiliary/scanner/mysql/mysql_login.rb @@ -13,6 +13,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Sessions::CreateSessionOptions include Msf::Auxiliary::CommandShell + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super(update_info(info, diff --git a/modules/auxiliary/scanner/postgres/postgres_login.rb b/modules/auxiliary/scanner/postgres/postgres_login.rb index 2ba18b8f0ff3..8d1631ed90af 100644 --- a/modules/auxiliary/scanner/postgres/postgres_login.rb +++ b/modules/auxiliary/scanner/postgres/postgres_login.rb @@ -14,6 +14,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary # Creates an instance of this module. def initialize(info = {}) diff --git a/modules/auxiliary/scanner/rservices/rexec_login.rb b/modules/auxiliary/scanner/rservices/rexec_login.rb index cb43217416e2..f12a7cd2fc6d 100644 --- a/modules/auxiliary/scanner/rservices/rexec_login.rb +++ b/modules/auxiliary/scanner/rservices/rexec_login.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/rservices/rlogin_login.rb b/modules/auxiliary/scanner/rservices/rlogin_login.rb index ca5356cd2dcf..ecfe7c2df07f 100644 --- a/modules/auxiliary/scanner/rservices/rlogin_login.rb +++ b/modules/auxiliary/scanner/rservices/rlogin_login.rb @@ -12,6 +12,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Login include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/rservices/rsh_login.rb b/modules/auxiliary/scanner/rservices/rsh_login.rb index 065e657ba448..ad2e446ac3b8 100644 --- a/modules/auxiliary/scanner/rservices/rsh_login.rb +++ b/modules/auxiliary/scanner/rservices/rsh_login.rb @@ -11,6 +11,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/smb/smb_login.rb b/modules/auxiliary/scanner/smb/smb_login.rb index 57056f9153c5..0519b29657f9 100644 --- a/modules/auxiliary/scanner/smb/smb_login.rb +++ b/modules/auxiliary/scanner/smb/smb_login.rb @@ -16,6 +16,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary Aliases = [ 'auxiliary/scanner/smb/login' diff --git a/modules/auxiliary/scanner/ssh/eaton_xpert_backdoor.rb b/modules/auxiliary/scanner/ssh/eaton_xpert_backdoor.rb index aa55571dcfbf..31832116bdc0 100644 --- a/modules/auxiliary/scanner/ssh/eaton_xpert_backdoor.rb +++ b/modules/auxiliary/scanner/ssh/eaton_xpert_backdoor.rb @@ -13,6 +13,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::CommandShell include Msf::Auxiliary::Report include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super(update_info(info, diff --git a/modules/auxiliary/scanner/ssh/fortinet_backdoor.rb b/modules/auxiliary/scanner/ssh/fortinet_backdoor.rb index 97260eaf0377..7f874e63e9dd 100644 --- a/modules/auxiliary/scanner/ssh/fortinet_backdoor.rb +++ b/modules/auxiliary/scanner/ssh/fortinet_backdoor.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions include Msf::Auxiliary::Report + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super(update_info(info, diff --git a/modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb b/modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb index a27bae504442..b5341b2b9585 100644 --- a/modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb +++ b/modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::CommandShell include Msf::Auxiliary::Report include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize(info = {}) super(update_info(info, diff --git a/modules/auxiliary/scanner/ssh/ssh_login.rb b/modules/auxiliary/scanner/ssh/ssh_login.rb index 1cab12905b80..31cd0bcdafe1 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login.rb @@ -15,6 +15,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Exploit::Remote::SSH::Options include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index ff3ce668db6d..03507aae7adc 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -16,6 +16,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Exploit::Remote::SSH::Options include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary attr_accessor :ssh_socket, :good_key diff --git a/modules/auxiliary/scanner/telnet/brocade_enable_login.rb b/modules/auxiliary/scanner/telnet/brocade_enable_login.rb index 197c68633fd2..22b65e497dd1 100644 --- a/modules/auxiliary/scanner/telnet/brocade_enable_login.rb +++ b/modules/auxiliary/scanner/telnet/brocade_enable_login.rb @@ -13,6 +13,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/telnet/telnet_login.rb b/modules/auxiliary/scanner/telnet/telnet_login.rb index 78447276b33b..ad6f5e60a68a 100644 --- a/modules/auxiliary/scanner/telnet/telnet_login.rb +++ b/modules/auxiliary/scanner/telnet/telnet_login.rb @@ -13,6 +13,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Auxiliary::CommandShell include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super( diff --git a/modules/auxiliary/scanner/winrm/winrm_login.rb b/modules/auxiliary/scanner/winrm/winrm_login.rb index 2915ca92255a..5bf1860c0729 100644 --- a/modules/auxiliary/scanner/winrm/winrm_login.rb +++ b/modules/auxiliary/scanner/winrm/winrm_login.rb @@ -16,6 +16,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Scanner include Msf::Exploit::Remote::Kerberos::Ticket::Storage include Msf::Sessions::CreateSessionOptions + include Msf::Auxiliary::ReportSummary def initialize super(