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 fbe5f90
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 21 deletions.
138 changes: 138 additions & 0 deletions lib/msf/core/auxiliary/report_summary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -*- 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
return unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS)
return unless datastore['ShowSuccessfulLogins'] == true

print_report_summary
end

def create_credential_login(credential_data)
return unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS)
return unless datastore['ShowSuccessfulLogins'] == true

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)
return unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS)
return unless datastore['ShowSuccessfulLogins'] == true

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)

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

# TODO - This was previously within the run command that was initially added as a workaround
# 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} #{sessions.size == 1 ? 'session was' : 'sessions were'} opened successfully.")
else
print_status('You can open a 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 && !report.empty?
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: false,
developer_notes: 'To be enabled after appropriate testing'
}.freeze,
{
name: DNS,
description: 'When enabled allows configuration of DNS resolution behaviour in Metasploit',
Expand Down
23 changes: 2 additions & 21 deletions modules/auxiliary/scanner/mysql/mysql_login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -60,21 +61,6 @@ def target
[rhost,rport].join(":")
end

def run
results = super
logins = results.flat_map { |_k, v| v[:successful_logins] }
sessions = results.flat_map { |_k, v| v[:successful_sessions] }
print_status("Bruteforce completed, #{logins.size} #{logins.size == 1 ? 'credential was' : 'credentials were'} successful.")
return results unless framework.features.enabled?(Msf::FeatureManager::MYSQL_SESSION_TYPE)

if create_session?
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
results
end

def run_host(ip)
begin
if mysql_version_check("4.1.1") # Pushing down to 4.1.1.
Expand Down Expand Up @@ -102,9 +88,6 @@ def run_host(ip)
local_host: datastore['CHOST']
)
)

successful_logins = []
successful_sessions = []
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
Expand All @@ -117,11 +100,10 @@ def run_host(ip)
create_credential_login(credential_data)

print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}'"
successful_logins << result

if create_session?
begin
successful_sessions << session_setup(result)
session_setup(result)
rescue ::StandardError => e
elog('Failed to setup the session', error: e)
print_brute level: :error, ip: ip, msg: "Failed to setup the session - #{e.class} #{e.message}"
Expand All @@ -140,7 +122,6 @@ def run_host(ip)
rescue ::Rex::ConnectionError, ::EOFError => e
vprint_error "#{target} - Unable to connect: #{e.to_s}"
end
{ successful_logins: successful_logins, successful_sessions: successful_sessions }
end

# Tmtm's rbmysql is only good for recent versions of mysql, according
Expand Down
1 change: 1 addition & 0 deletions modules/auxiliary/scanner/rservices/rlogin_login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions modules/auxiliary/scanner/ssh/ssh_login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit fbe5f90

Please sign in to comment.