142 lines
5.4 KiB
Ruby
142 lines
5.4 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'rex'
|
|
|
|
class MetasploitModule < Msf::Post
|
|
include Msf::Post::Windows::Registry
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Windows Antivirus Exclusions Enumeration',
|
|
'Description' => %q(
|
|
This module will enumerate the file, directory, process and
|
|
extension-based exclusions from supported AV products, which
|
|
currently includes Microsoft Defender, Microsoft Security
|
|
Essentials/Antimalware, and Symantec Endpoint Protection.
|
|
),
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'Andrew Smith', # original metasploit module
|
|
'Jon Hart <jon_hart[at]rapid7.com>' # improved metasploit module
|
|
],
|
|
'Platform' => [ 'win' ],
|
|
# XXX: this will work with 'shell' when the sysinfo parts are removed
|
|
# and https://github.com/rapid7/metasploit-framework/issues/6328 and
|
|
# perhaps https://github.com/rapid7/metasploit-framework/issues/6316
|
|
# are fixed
|
|
'SessionTypes' => [ 'meterpreter' ]
|
|
)
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('DEFENDER', [true, 'Enumerate exclusions for Microsoft Defender', true]),
|
|
OptBool.new('ESSENTIALS', [true, 'Enumerate exclusions for Microsoft Security Essentials/Antimalware', true]),
|
|
OptBool.new('SEP', [true, 'Enumerate exclusions for Symantec Endpoint Protection (SEP)', true])
|
|
]
|
|
)
|
|
end
|
|
|
|
DEFENDER = 'Windows Defender'
|
|
DEFENDER_BASE_KEY = 'HKLM\\SOFTWARE\\Microsoft\\Windows Defender'
|
|
ESSENTIALS = 'Microsoft Security Essentials / Antimalware'
|
|
ESSENTIALS_BASE_KEY = 'HKLM\\SOFTWARE\\Microsoft\\Microsoft Antimalware'
|
|
SEP = 'Symantec Endpoint Protection (SEP)'
|
|
SEP_BASE_KEY = 'HKLM\\SOFTWARE\\Symantec\\Symantec Endpoint Protection'
|
|
|
|
def av_installed?(base_key, product)
|
|
if registry_key_exist?(base_key)
|
|
print_good("Found #{product}")
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def excluded_sep
|
|
base_exclusion_key = "#{SEP_BASE_KEY}\\Exclusions\\ScanningEngines\\Directory"
|
|
admin_exclusion_key = "#{base_exclusion_key}\\Admin"
|
|
client_exclusion_key = "#{base_exclusion_key}\\Client"
|
|
|
|
admin_paths = []
|
|
if (admin_exclusion_keys = registry_enumkeys(admin_exclusion_key, @registry_view))
|
|
admin_exclusion_keys.map do |key|
|
|
admin_paths << registry_getvaldata("#{admin_exclusion_key}\\#{key}", 'DirectoryName', @registry_view)
|
|
end
|
|
print_exclusions_table(SEP, 'admin path', admin_paths)
|
|
end
|
|
client_paths = []
|
|
if (client_exclusion_keys = registry_enumkeys(client_exclusion_key, @registry_view))
|
|
client_exclusion_keys.map do |key|
|
|
client_paths << registry_getvaldata("#{client_exclusion_key}\\#{key}", 'DirectoryName', @registry_view)
|
|
end
|
|
end
|
|
print_exclusions_table(SEP, 'client path', client_paths)
|
|
end
|
|
|
|
def excluded_defender
|
|
print_exclusions_table(DEFENDER, 'extension', registry_enumvals("#{DEFENDER_BASE_KEY}\\Exclusions\\Extensions", @registry_view))
|
|
print_exclusions_table(DEFENDER, 'path', registry_enumvals("#{DEFENDER_BASE_KEY}\\Exclusions\\Paths", @registry_view))
|
|
print_exclusions_table(DEFENDER, 'process', registry_enumvals("#{DEFENDER_BASE_KEY}\\Exclusions\\Processes", @registry_view))
|
|
end
|
|
|
|
def excluded_mssec
|
|
print_exclusions_table(ESSENTIALS, 'extension', registry_enumvals("#{ESSENTIALS_BASE_KEY}\\Exclusions\\Extensions", @registry_view))
|
|
print_exclusions_table(ESSENTIALS, 'path', registry_enumvals("#{ESSENTIALS_BASE_KEY}\\Exclusions\\Paths", @registry_view))
|
|
print_exclusions_table(ESSENTIALS, 'process', registry_enumvals("#{ESSENTIALS_BASE_KEY}\\Exclusions\\Processes", @registry_view))
|
|
end
|
|
|
|
def print_exclusions_table(product, exclusion_type, exclusions)
|
|
exclusions ||= []
|
|
exclusions = exclusions.compact.reject(&:blank?)
|
|
if exclusions.empty?
|
|
print_status("No #{exclusion_type} exclusions for #{product}")
|
|
return
|
|
end
|
|
table = Rex::Ui::Text::Table.new(
|
|
'Header' => "#{product} excluded #{exclusion_type.pluralize}",
|
|
'Indent' => 1,
|
|
'Columns' => [ exclusion_type.capitalize ]
|
|
)
|
|
exclusions.map { |exclusion| table << [exclusion] }
|
|
print_line(table.to_s)
|
|
end
|
|
|
|
def setup
|
|
# all of these target applications seemingly store their registry
|
|
# keys/values at the same architecture of the host, so if we happen to be
|
|
# in a 32-bit process on a 64-bit machine, ensure that we read from the
|
|
# 64-bit keys/values, and otherwise use the native keys/values
|
|
@registry_view = sysinfo['Architecture'] =~ /WOW64/ ? REGISTRY_VIEW_64_BIT : REGISTRY_VIEW_NATIVE
|
|
unless datastore['DEFENDER'] || datastore['ESSENTIALS'] || datastore['SEP']
|
|
fail_with(Failure::BadConfig, 'Must set one or more of DEFENDER, ESSENTIALS or SEP to true')
|
|
end
|
|
end
|
|
|
|
def run
|
|
print_status("Enumerating Excluded Paths for AV on #{sysinfo['Computer']}")
|
|
|
|
found = false
|
|
if datastore['DEFENDER'] && av_installed?(DEFENDER_BASE_KEY, DEFENDER)
|
|
found = true
|
|
excluded_defender
|
|
end
|
|
if datastore['ESSENTIALS'] && av_installed?(ESSENTIALS_BASE_KEY, ESSENTIALS)
|
|
found = true
|
|
excluded_mssec
|
|
end
|
|
if datastore['SEP'] && av_installed?(SEP_BASE_KEY, SEP)
|
|
found = true
|
|
excluded_sep
|
|
end
|
|
|
|
print_error "No supported AV identified" unless found
|
|
end
|
|
end
|