From de45263fd9506427e110fa217ef87d44a146fc5e Mon Sep 17 00:00:00 2001 From: Meatballs1 Date: Sun, 22 Jul 2012 12:33:55 +0100 Subject: [PATCH] msftidy --- modules/auxiliary/scanner/smb/smb_login.rb | 537 +++++++++++---------- 1 file changed, 269 insertions(+), 268 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_login.rb b/modules/auxiliary/scanner/smb/smb_login.rb index 28da0497b3..5c9a76baaa 100644 --- a/modules/auxiliary/scanner/smb/smb_login.rb +++ b/modules/auxiliary/scanner/smb/smb_login.rb @@ -1,268 +1,269 @@ -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# web site for more information on licensing and terms of use. -# http://metasploit.com/ -## - - -require 'msf/core' - -class Metasploit3 < Msf::Auxiliary - - include Msf::Exploit::Remote::DCERPC - include Msf::Exploit::Remote::SMB - include Msf::Exploit::Remote::SMB::Authenticated - - include Msf::Auxiliary::Scanner - include Msf::Auxiliary::Report - include Msf::Auxiliary::AuthBrute - - attr_reader :accepts_bogus_domains - - def proto - 'smb' - end - def initialize - super( - 'Name' => 'SMB Login Check Scanner', - 'Description' => %q{ - This module will test a SMB login on a range of machines and - report successful logins. If you have loaded a database plugin - and connected to a database this module will record successful - logins and hosts so you can track your access. - }, - 'Author' => [ - 'tebo ', # Original - 'Ben Campbell ' # Refactoring - ], - 'References' => - [ - [ 'CVE', '1999-0506'], # Weak password - ], - 'License' => MSF_LICENSE - ) - deregister_options('RHOST','USERNAME','PASSWORD') - - @accepts_guest_logins = {} - @correct_credentials_status_codes = ["STATUS_INVALID_LOGON_HOURS", - "STATUS_ACCOUNT_RESTRICTION", - "STATUS_ACCOUNT_EXPIRED", - "STATUS_ACCOUNT_DISABLED", - "STATUS_PASSWORD_EXPIRED", - "STATUS_PASSWORD_MUST_CHANGE"] - - # These are normally advanced options, but for this module they have a - # more active role, so make them regular options. - register_options( - [ - OptString.new('SMBPass', [ false, "SMB Password" ]), - OptString.new('SMBUser', [ false, "SMB Username" ]), - OptString.new('SMBDomain', [ false, "SMB Domain", 'WORKGROUP']), - OptBool.new('PRESERVE_DOMAINS', [ false, "Respect a username that contains a domain name.", true]), - OptBool.new('RECORD_GUEST', [ false, "Record guest-privileged random logins to the database", false]), - ], self.class) - - end - - def run_host(ip) - print_brute(:level => :vstatus, :ip => ip, :msg => "Starting SMB login bruteforce") - - if accepts_bogus_logins? - print_error("#{smbhost} - This system accepts authentication with any credentials, brute force is ineffective.") - return - end - - unless datastore['RECORD_GUEST'] - if accepts_guest_logins? - print_status("#{ip} - This system allows guest sessions with any credentials, these instances will not be reported.") - end - end - - domain = datastore['SMBDomain'] || "" - - begin - each_user_pass do |user, pass| - result = try_user_pass(domain, user, pass) - end - rescue ::Rex::ConnectionError - nil - end - - end - - def check_login_status(domain, user, pass) - connect() - status_code = "" - begin - status_code = simple.login( datastore['SMBName'], - user, - pass, - domain, - datastore['SMB::VerifySignature'], - datastore['NTLM::UseNTLMv2'], - datastore['NTLM::UseNTLM2_session'], - datastore['NTLM::SendLM'], - datastore['NTLM::UseLMKey'], - datastore['NTLM::SendNTLM'], - datastore['SMB::Native_OS'], - datastore['SMB::Native_LM'], - {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} - ) - # This does not appear to be required to validate login details? simple.connect("\\\\#{datastore['RHOST']}\\IPC$") - rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e - status_code = e.get_error(e.error_code) - rescue ::Rex::Proto::SMB::Exceptions::LoginError => e - status_code = e.error_reason - ensure - disconnect() - end - - return status_code - end - - # Unsure how this result is different than bogus logins - def accepts_guest_logins? - guest = false - user = Rex::Text.rand_text_alpha(8) - pass = Rex::Text.rand_text_alpha(8) - - check_login_status(datastore['SMBDomain'], user, pass) - - unless(simple.client.auth_user) - guest = true - @accepts_guest_logins['rhost'] ||=[] unless @accepts_guest_logins.include?(rhost) #'rhost' should be rhost? - report_note( - :host => rhost, - :proto => 'tcp', - :sname => 'smb', - :port => datastore['RPORT'], - :type => 'smb.account.info', - :data => 'accepts guest login from any account', - :update => :unique_data - ) - end - - return guest - end - - def accepts_bogus_logins? - user = Rex::Text.rand_text_alpha(8) - pass = Rex::Text.rand_text_alpha(8) - check_login_status(datastore['SMBDomain'], user, pass) - return simple.client.auth_user ? true : false - end - - # This logic is not universal ie a local account will not care about workgroup - # but remote domain authentication will so check each instance - def accepts_bogus_domains?(user, pass, rhost) - domain = Rex::Text.rand_text_alpha(8) - status = check_login_status(domain, user, pass) - - bogus_domain = valid_credentials?(status) - if bogus_domain - vprint_status "Domain is ignored" - end - - return valid_credentials?(status) - end - - def valid_credentials?(status) - return (status == "STATUS_SUCCESS" || @correct_credentials_status_codes.include?(status)) - end - - def try_user_pass(domain, user, pass) - # Note that unless PRESERVE_DOMAINS is true, we're more - # than happy to pass illegal usernames that contain - # slashes. - if datastore["PRESERVE_DOMAINS"] - d,u = domain_username_split(user) - user = u - domain = d if d - end - - user = user.to_s.gsub(//i,"") - - status = check_login_status(domain, user, pass) - - output_message = "#{smbhost} - %s (#{smb_peer_os}) #{user} : #{pass} [#{status}]" - - case status - when 'STATUS_SUCCESS' - if(simple.client.auth_user) - print_good(output_message % "SUCCESSFUL LOGIN") - vprint_status("Auth-User: #{simple.client.auth_user}") - else - print_status(output_message % "GUEST LOGIN") - @accepts_guest_logins[rhost] = [user, pass] unless datastore['RECORD_GUEST'] - end - - report_creds(domain,user,pass,true) if @accepts_guest_logins.select{ |g_host, g_creds| g_host == rhost and g_creds == [user,pass] }.empty? - validuser_case_sensitive?(domain, user, pass) - when *@correct_credentials_status_codes - print_status(output_message % "FAILED LOGIN, VALID CREDENTIALS" ) - report_creds(domain,user,pass,false) - validuser_case_sensitive?(domain, user, pass) - when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED' - vprint_error(output_message % "FAILED LOGIN") - else - vprint_error(output_message % "FAILED LOGIN") - end - end - - - def validuser_case_sensitive?(domain, user, pass) - if user == user.downcase - user = user.upcase - else - user = user.downcase - end - - status = check_login_status(domain, user, pass) - case_insensitive = valid_credentials?(status) - if case_insensitive - vprint_status("Username is case insensitive") - end - - return case_insensitive - end - - def note_creds(domain,user,pass,reason) - report_note( - :host => rhost, - :proto => 'tcp', - :sname => 'smb', - :port => datastore['RPORT'], - :type => 'smb.account.info', - :data => {:user => user, :pass => pass, :status => reason}, - :update => :unique_data - ) - end - - def report_creds(domain,user,pass,active) - login_name = "" - - if accepts_bogus_domains?(user,pass,rhost) - login_name = user - else - login_name = "#{domain}\\#{user}" - end - - report_hash = { - :host => rhost, - :port => datastore['RPORT'], - :sname => 'smb', - :user => login_name, - :pass => pass, - :source_type => "user_supplied", - :active => active - } - - if pass =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/ - report_hash.merge!({:type => 'smb_hash'}) - else - report_hash.merge!({:type => 'password'}) - end - report_auth_info(report_hash) - end -end +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::DCERPC + include Msf::Exploit::Remote::SMB + include Msf::Exploit::Remote::SMB::Authenticated + + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + + attr_reader :accepts_bogus_domains + + def proto + 'smb' + end + def initialize + super( + 'Name' => 'SMB Login Check Scanner', + 'Description' => %q{ + This module will test a SMB login on a range of machines and + report successful logins. If you have loaded a database plugin + and connected to a database this module will record successful + logins and hosts so you can track your access. + }, + 'Author' => [ + 'tebo ', # Original + 'Ben Campbell ' # Refactoring + ], + 'References' => + [ + [ 'CVE', '1999-0506'], # Weak password + + ], + 'License' => MSF_LICENSE + ) + deregister_options('RHOST','USERNAME','PASSWORD') + + @accepts_guest_logins = {} + @correct_credentials_status_codes = ["STATUS_INVALID_LOGON_HOURS", + "STATUS_ACCOUNT_RESTRICTION", + "STATUS_ACCOUNT_EXPIRED", + "STATUS_ACCOUNT_DISABLED", + "STATUS_PASSWORD_EXPIRED", + "STATUS_PASSWORD_MUST_CHANGE"] + + # These are normally advanced options, but for this module they have a + # more active role, so make them regular options. + register_options( + [ + OptString.new('SMBPass', [ false, "SMB Password" ]), + OptString.new('SMBUser', [ false, "SMB Username" ]), + OptString.new('SMBDomain', [ false, "SMB Domain", 'WORKGROUP']), + OptBool.new('PRESERVE_DOMAINS', [ false, "Respect a username that contains a domain name.", true]), + OptBool.new('RECORD_GUEST', [ false, "Record guest-privileged random logins to the database", false]), + ], self.class) + + end + + def run_host(ip) + print_brute(:level => :vstatus, :ip => ip, :msg => "Starting SMB login bruteforce") + + if accepts_bogus_logins? + print_error("#{smbhost} - This system accepts authentication with any credentials, brute force is ineffective.") + return + end + + unless datastore['RECORD_GUEST'] + if accepts_guest_logins? + print_status("#{ip} - This system allows guest sessions with any credentials, these instances will not be reported.") + end + end + + domain = datastore['SMBDomain'] || "" + + begin + each_user_pass do |user, pass| + result = try_user_pass(domain, user, pass) + end + rescue ::Rex::ConnectionError + nil + end + + end + + def check_login_status(domain, user, pass) + connect() + status_code = "" + begin + status_code = simple.login( datastore['SMBName'], + user, + pass, + domain, + datastore['SMB::VerifySignature'], + datastore['NTLM::UseNTLMv2'], + datastore['NTLM::UseNTLM2_session'], + datastore['NTLM::SendLM'], + datastore['NTLM::UseLMKey'], + datastore['NTLM::SendNTLM'], + datastore['SMB::Native_OS'], + datastore['SMB::Native_LM'], + {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} + ) + # This does not appear to be required to validate login details? simple.connect("\\\\#{datastore['RHOST']}\\IPC$") + rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e + status_code = e.get_error(e.error_code) + rescue ::Rex::Proto::SMB::Exceptions::LoginError => e + status_code = e.error_reason + ensure + disconnect() + end + + return status_code + end + + # Unsure how this result is different than bogus logins + def accepts_guest_logins? + guest = false + user = Rex::Text.rand_text_alpha(8) + pass = Rex::Text.rand_text_alpha(8) + + check_login_status(datastore['SMBDomain'], user, pass) + + unless(simple.client.auth_user) + + guest = true + @accepts_guest_logins['rhost'] ||=[] unless @accepts_guest_logins.include?(rhost) #'rhost' should be rhost? + report_note( + :host => rhost, + :proto => 'tcp', + :sname => 'smb', + :port => datastore['RPORT'], + :type => 'smb.account.info', + :data => 'accepts guest login from any account', + :update => :unique_data + ) + end + + return guest + end + + def accepts_bogus_logins? + user = Rex::Text.rand_text_alpha(8) + pass = Rex::Text.rand_text_alpha(8) + check_login_status(datastore['SMBDomain'], user, pass) + return simple.client.auth_user ? true : false + end + + # This logic is not universal ie a local account will not care about workgroup + # but remote domain authentication will so check each instance + def accepts_bogus_domains?(user, pass, rhost) + domain = Rex::Text.rand_text_alpha(8) + status = check_login_status(domain, user, pass) + + bogus_domain = valid_credentials?(status) + if bogus_domain + vprint_status "Domain is ignored" + end + + return valid_credentials?(status) + end + + def valid_credentials?(status) + return (status == "STATUS_SUCCESS" || @correct_credentials_status_codes.include?(status)) + end + + def try_user_pass(domain, user, pass) + # Note that unless PRESERVE_DOMAINS is true, we're more + # than happy to pass illegal usernames that contain + # slashes. + if datastore["PRESERVE_DOMAINS"] + d,u = domain_username_split(user) + user = u + domain = d if d + end + + user = user.to_s.gsub(//i,"") + + status = check_login_status(domain, user, pass) + + output_message = "#{smbhost} - %s (#{smb_peer_os}) #{user} : #{pass} [#{status}]" + + case status + when 'STATUS_SUCCESS' + + if(simple.client.auth_user) + print_good(output_message % "SUCCESSFUL LOGIN") + vprint_status("Auth-User: #{simple.client.auth_user}") + else + print_status(output_message % "GUEST LOGIN") + @accepts_guest_logins[rhost] = [user, pass] unless datastore['RECORD_GUEST'] + end + + report_creds(domain,user,pass,true) if @accepts_guest_logins.select{ |g_host, g_creds| g_host == rhost and g_creds == [user,pass] }.empty? + validuser_case_sensitive?(domain, user, pass) + when *@correct_credentials_status_codes + print_status(output_message % "FAILED LOGIN, VALID CREDENTIALS" ) + report_creds(domain,user,pass,false) + validuser_case_sensitive?(domain, user, pass) + when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED' + vprint_error(output_message % "FAILED LOGIN") + else + vprint_error(output_message % "FAILED LOGIN") + end + end + + def validuser_case_sensitive?(domain, user, pass) + if user == user.downcase + user = user.upcase + else + user = user.downcase + end + + status = check_login_status(domain, user, pass) + case_insensitive = valid_credentials?(status) + if case_insensitive + vprint_status("Username is case insensitive") + end + + return case_insensitive + end + + def note_creds(domain,user,pass,reason) + report_note( + :host => rhost, + :proto => 'tcp', + :sname => 'smb', + :port => datastore['RPORT'], + :type => 'smb.account.info', + :data => {:user => user, :pass => pass, :status => reason}, + :update => :unique_data + ) + end + + def report_creds(domain,user,pass,active) + login_name = "" + + if accepts_bogus_domains?(user,pass,rhost) + login_name = user + else + login_name = "#{domain}\\#{user}" + end + + report_hash = { + :host => rhost, + :port => datastore['RPORT'], + :sname => 'smb', + :user => login_name, + :pass => pass, + :source_type => "user_supplied", + :active => active + } + + if pass =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/ + report_hash.merge!({:type => 'smb_hash'}) + else + report_hash.merge!({:type => 'password'}) + end + report_auth_info(report_hash) + end +end