diff --git a/lib/metasploit/framework/login_scanner/snmp.rb b/lib/metasploit/framework/login_scanner/snmp.rb index 8a1397c6152e..2f32c4e7c809 100644 --- a/lib/metasploit/framework/login_scanner/snmp.rb +++ b/lib/metasploit/framework/login_scanner/snmp.rb @@ -14,6 +14,7 @@ class SNMP DEFAULT_TIMEOUT = 2 DEFAULT_PORT = 161 + DEFAULT_PROTOCOL = 'udp'.freeze DEFAULT_VERSION = '1'.freeze DEFAULT_QUEUE_SIZE = 100 LIKELY_PORTS = [ 161, 162 ].freeze @@ -27,6 +28,10 @@ class SNMP # @return [String] attr_accessor :version + # The SNMP protocol to use + # @return [String] + attr_accessor :protocol + # The number of logins to try in each batch # @return [Integer] attr_accessor :queue_size @@ -37,6 +42,12 @@ class SNMP in: ['1', '2c', 'all'] } + validates :protocol, + presence: true, + inclusion: { + in: ['udp', 'tcp'] + } + validates :queue_size, presence: true, numericality: { @@ -191,10 +202,26 @@ def process_logins(opts = {}) process_responses(1.0) end + def recv_wrapper(sock, max_size, timeout) + res = nil + if protocol == 'udp' + res = sock.recvfrom(max_size, timeout) + elsif protocol == 'tcp' + ready = ::IO.select([sock], nil, nil, timeout) + if ready + res = sock.recv_nonblock(max_size) + # Put into an array to mimic recvfrom + res = [res, host, port] + end + end + + res + end + # Process any responses on the UDP socket and queue the results def process_responses(timeout = 1.0) queue = [] - while (res = sock.recvfrom(65535, timeout)) + while (res = recv_wrapper(sock, 65535, timeout)) # Ignore invalid responses break if !(res[1]) @@ -212,7 +239,7 @@ def process_responses(timeout = 1.0) community: response[:community], host: host, port: port, - protocol: 'udp', + protocol: protocol, service_name: 'snmp', proof: response[:proof], status: Metasploit::Model::Login::Status::SUCCESSFUL, @@ -237,12 +264,22 @@ def send_snmp_write_request(version, community, data) ) end + def send_wrapper(sock, pkt, host, port, flags) + if protocol == 'tcp' + return sock.send(pkt, flags) + end + + if protocol == 'udp' + return sock.sendto(pkt, host, port, 0) + end + end + # Send a SNMP request on the existing socket def send_snmp_request(pkt) resend_count = 0 begin - sock.sendto(pkt, host, port, 0) + send_wrapper(sock, pkt, host, port, 0) rescue ::Errno::ENOBUFS resend_count += 1 if resend_count > MAX_RESEND_COUNT @@ -347,10 +384,15 @@ def parse_snmp_response(pkt) # Create a new socket for this scanner def configure_socket shutdown_socket if sock - self.sock = ::Rex::Socket::Udp.create( + + self.sock = ::Rex::Socket.create({ + 'PeerHost' => host, + 'PeerPort' => port, + 'Proto' => protocol, + 'Timeout' => connection_timeout, 'Context' => { 'Msf' => framework, 'MsfExploit' => framework_module } - ) + }) end # Close any open socket if it exists @@ -362,6 +404,7 @@ def shutdown_socket # Sets the SNMP parameters if not specified def set_sane_defaults self.connection_timeout = DEFAULT_TIMEOUT if connection_timeout.nil? + self.protocol = DEFAULT_PROTOCOL if protocol.nil? self.port = DEFAULT_PORT if port.nil? self.version = DEFAULT_VERSION if version.nil? self.queue_size = DEFAULT_QUEUE_SIZE if queue_size.nil? diff --git a/modules/auxiliary/scanner/snmp/snmp_login.rb b/modules/auxiliary/scanner/snmp/snmp_login.rb index 89dc055f4422..b8d281f57616 100644 --- a/modules/auxiliary/scanner/snmp/snmp_login.rb +++ b/modules/auxiliary/scanner/snmp/snmp_login.rb @@ -30,6 +30,7 @@ def initialize 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", @@ -51,6 +52,7 @@ def run_host(ip) scanner = Metasploit::Framework::LoginScanner::SNMP.new( host: ip, port: rport, + protocol: datastore['PROTOCOL'], cred_details: collection, stop_on_success: datastore['STOP_ON_SUCCESS'], bruteforce_speed: datastore['BRUTEFORCE_SPEED'], @@ -91,6 +93,10 @@ def rport datastore['RPORT'] end + def protocol + datastore['PROTOCOL'] + end +