diff --git a/modules/auxiliary/scanner/smb/smb_enumshares.rb b/modules/auxiliary/scanner/smb/smb_enumshares.rb index 086cc0512efcc..e2ace9ba18965 100644 --- a/modules/auxiliary/scanner/smb/smb_enumshares.rb +++ b/modules/auxiliary/scanner/smb/smb_enumshares.rb @@ -27,7 +27,7 @@ def initialize(info = {}) directories, files, time stamps, etc. By default, a RubySMB net_share_enum_all request is done in order to retrieve share information, - which uses SRVSVC. + which uses SRVSVC, otherwise will use Rex SMB. }, 'Author' => [ 'hdm', @@ -298,18 +298,22 @@ def run_host(ip) self.simple = session.simple_client enum_shares(session.address) else - [{ port: SMB1_PORT }, { port: SMB2_3_PORT } ].each do |info| - vprint_status("Connecting to the server...") + [ + # Force RubySMB library for SMB1 mode + # (Windows server 2019 has issues, but Samba 3.0.20/metasploitable2 is fine with SMB1 & RubySMB) + { port: SMB1_PORT, versions: [1], backend: :ruby_smb, label: 'SMB v1' }, + # @smcintyre-r7: The Rex client doesn't support SMB versions 2 and 3 + #{ port: SMB2_3_PORT, versions: [1, 2, 3], backend: :rex, label: 'SMB v1/2/3' }, + # Use Rex SMB library fallback for SMB1 mode + { port: SMB1_PORT, versions: [1], backend: :rex, label: 'SMB v1' }, + ].each do |info| + # Update line prefix, as port changes + remove_instance_variable(:@print_prefix) if instance_variable_defined?(:@print_prefix) # Assign @rport so that it is accessible via the rport method in this module, # as well as making it accessible to the module mixins @rport = info[:port] - if rport == SMB1_PORT - # force library in smb1 mode otherwise simple.client is a - # `Rex::Proto::SMB::Client` that does not supply `net_share_enum_all` - connect(versions: [1], backend: :ruby_smb) - else - connect(versions: [1, 2, 3]) - end + vprint_status("Connecting using #{info[:label]} via #{info[:backend]}") + connect(versions: info[:versions], backend: info[:backend]) smb_login shares = enum_shares(ip) next if shares.nil? || shares.empty? @@ -342,22 +346,31 @@ def enum_shares(ip) shares = [] begin - # Return all shares if `Shares` option has not been set - if datastore['Share'].nil? - shares = simple.client.net_share_enum_all(ip) - else + # RubySMB + if simple.client.respond_to?(:net_share_enum_all) + # Return all shares if `Shares` option has not been set + if datastore['Share'].nil? + shares = simple.client.net_share_enum_all(ip) # Return specific share if the `Share` option has been set - simple.client.net_share_enum_all(ip).each { |share| shares = [share] if share[:name] == datastore['Share'] } - # Return an error if `Share` option has been set but no matches were found - if shares.empty? - print_error("No shares match #{datastore['Share']}") + else + simple.client.net_share_enum_all(ip).each { |share| shares = [share] if share[:name] == datastore['Share'] } end + # Rex SMB: smb_netshareenumall tries SRVSVC then falls back to LANMAN + else + raw = smb_netshareenumall + shares = raw.map { |s| { name: s[0], type: s[1], comment: s[2] } } + shares = shares.select { |s| s[:name] == datastore['Share'] } if datastore['Share'] end + # Return an error if `Share` option has been set but no matches were found + print_error("No shares match #{datastore['Share']}") if shares.empty? rescue RubySMB::Error::UnexpectedStatusCode => e print_error("Error when trying to enumerate shares - #{e.status_code.name}") return rescue RubySMB::Error::InvalidPacket => e - print_error("Invalid packet received when trying to enumerate shares - #{e}") + vprint_error("Invalid packet received when trying to enumerate shares - #{e}") + return + rescue Rex::Proto::SMB::Exceptions::ErrorCode => e + print_error("Error when trying to enumerate shares - #{e.message}") return end @@ -383,7 +396,11 @@ def enum_shares(ip) ) if datastore['SpiderShares'] - get_files_info(ip, shares) + if simple.client.is_a?(RubySMB::Client) + get_files_info(ip, shares) + else + print_warning('This is not available for this server (unable to use RubySMB)') + end end end