Skip to content

Commit

Permalink
Improves UX for scanner/login modules
Browse files Browse the repository at this point in the history
  • Loading branch information
cgranleese-r7 committed May 31, 2024
1 parent 6301d84 commit 7eb4ca6
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 0 deletions.
129 changes: 129 additions & 0 deletions lib/msf/core/auxiliary/report_summary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- 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

# TODO: Fix class variables
# - Need to sync up with Alan on stuff
def run
@@report = {}
super
print_report_summary
end

def create_credential_login(credential_data)
credential = credential_data[:core].to_credential
@@report[rhost] = { successful_logins: [] }
@@report[rhost][:successful_logins] << credential
super
end

# I think this will work for our new modules, but I don't know if it'll work
# for other modules that might not use 'session_setup' convention;
# i.e. you might need to hijack start_session, or create_session, or some other
# lower level building block that you'll have access to
def start_session(obj, info, ds_merge, crlf = false, sock = nil, sess = nil)
result = super
@@report[rhost].merge!({ successful_sessions: [] })
@@report[rhost][:successful_sessions] << result
result
end

private

def print_report_summary
report = @@report

conditional_verbose_output(report.keys.count)
return report unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS)

logins = report.flat_map { |_k, v| v[:successful_logins] }.compact
sessions = report.flat_map { |_k, v| v[:successful_sessions] }.compact

# return results unless framework.features.enabled?(Msf::FeatureManager::MSSQL_SESSION_TYPE)

print_status("Bruteforce completed, #{logins.size} #{logins.size == 1 ? 'credential was' : 'credentials were'} successful.")
if datastore['CreateSession']
print_status("#{sessions.size} MySQL #{sessions.size == 1 ? 'session was' : 'sessions were'} opened successfully.")
else
print_status('You can open an MySQL session with these credentials and %grnCreateSession%clr set to true')
end

show_successful_logins(report)
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 show_successful_logins(report)
if datastore['ShowSuccessfulLogins'] == true
successful_logins_to_table(report)
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 [String] 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]
end
end

table = ::Rex::Text::Table.new(
'Header' => 'Successful logins',
'Indent' => 4,
'Columns' => field_headers,
'Rows' => markdown_fields.compact
)

print_line("\n" + table.to_s + "\n")
end
end
end
end
8 changes: 8 additions & 0 deletions lib/msf/core/feature_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -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: true,
developer_notes: 'Enabled in Metasploit 6.4.x'
}.freeze,
{
name: DNS,
description: 'When enabled allows configuration of DNS resolution behaviour in Metasploit',
Expand Down

0 comments on commit 7eb4ca6

Please sign in to comment.