Dont forget our actual mixin
parent
910a91a0f6
commit
99ab722aca
|
@ -0,0 +1,164 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# This module provides methods for scanning UDP services
|
||||||
|
#
|
||||||
|
###
|
||||||
|
module Auxiliary::UDPScanner
|
||||||
|
|
||||||
|
include Auxiliary::Scanner
|
||||||
|
|
||||||
|
#
|
||||||
|
# Initializes an instance of an auxiliary module that scans UDP
|
||||||
|
#
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::CHOST,
|
||||||
|
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
||||||
|
], self.class)
|
||||||
|
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptInt.new('ScannerRecvInterval', [true, 'The maximum numbers of sends before entering the processing loop', 30]),
|
||||||
|
OptInt.new('ScannerMaxResends', [true, 'The maximum times to resend a packet when out of buffers', 10]),
|
||||||
|
OptInt.new('ScannerRecvQueueLimit', [true, 'The maximum queue size before breaking out of the processing loop', 100]),
|
||||||
|
OptInt.new('ScannerRecvWindow', [true, 'The number of seconds to wait post-scan to catch leftover replies', 15]),
|
||||||
|
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Define our batch size
|
||||||
|
def run_batch_size
|
||||||
|
datastore['BATCHSIZE'].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start scanning a batch of IP addresses
|
||||||
|
def run_batch(batch)
|
||||||
|
@udp_sock = Rex::Socket::Udp.create({
|
||||||
|
'LocalHost' => datastore['CHOST'] || nil,
|
||||||
|
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
||||||
|
})
|
||||||
|
add_socket(@udp_sock)
|
||||||
|
|
||||||
|
@udp_send_count = 0
|
||||||
|
|
||||||
|
# Provide a hook for pre-scanning setup
|
||||||
|
scanner_prescan(batch)
|
||||||
|
|
||||||
|
# Call the including module once per IP
|
||||||
|
batch.each do |ip|
|
||||||
|
scan_host(ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Catch any stragglers
|
||||||
|
stime = Time.now.to_f
|
||||||
|
|
||||||
|
while Time.now.to_f < ( stime + datastore['ScannerRecvWindow'] )
|
||||||
|
scanner_recv(1.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provide a hook for post-scanning processing
|
||||||
|
scanner_postscan(batch)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Send a packet to a given host and port
|
||||||
|
def scanner_send(data, ip, port)
|
||||||
|
|
||||||
|
resend_count = 0
|
||||||
|
begin
|
||||||
|
|
||||||
|
@udp_sock.sendto(data, ip, port, 0)
|
||||||
|
|
||||||
|
rescue ::Errno::ENOBUFS
|
||||||
|
resend_count += 1
|
||||||
|
if resend_count > datastore['ScannerMaxResends']
|
||||||
|
vprint_error("#{ip}:#{port} Max resend count hit sending #{data.length}")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
scanner_recv(0.1)
|
||||||
|
|
||||||
|
::IO.select(nil, nil, nil, 0.25)
|
||||||
|
|
||||||
|
retry
|
||||||
|
|
||||||
|
rescue ::Rex::ConnectionError
|
||||||
|
# This fires for host unreachable, net unreachable, and broadcast sends
|
||||||
|
# We can safely ignore all of these for UDP sends
|
||||||
|
end
|
||||||
|
|
||||||
|
@udp_send_count += 1
|
||||||
|
|
||||||
|
if @udp_send_count % datastore['ScannerRecvInterval'] == 0
|
||||||
|
scanner_recv(0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process incoming packets and dispatch to the module
|
||||||
|
# Ensure a response flood doesn't trap us in a loop
|
||||||
|
# Ignore packets outside of our project's scope
|
||||||
|
def scanner_recv(timeout=0.1)
|
||||||
|
queue = []
|
||||||
|
while (res = @udp_sock.recvfrom(65535, timeout))
|
||||||
|
|
||||||
|
# Ignore invalid responses
|
||||||
|
break if not res[1]
|
||||||
|
|
||||||
|
# Ignore empty responses
|
||||||
|
next if not (res[0] and res[0].length > 0)
|
||||||
|
|
||||||
|
# Trim the IPv6-compat prefix off if needed
|
||||||
|
shost = res[1].gsub(/^::ffff:/, '')
|
||||||
|
|
||||||
|
# Store the source port
|
||||||
|
sport = res[2]
|
||||||
|
|
||||||
|
# Ignore the response if we have a boundary
|
||||||
|
next unless inside_workspace_boundary?(shost)
|
||||||
|
|
||||||
|
queue << [res[0], shost, sport]
|
||||||
|
|
||||||
|
if queue.length > datastore['ScannerRecvQueueLimit']
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.each do |q|
|
||||||
|
scanner_process(*q)
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.length
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# The including module override these methods
|
||||||
|
#
|
||||||
|
|
||||||
|
# Called for each IP in the batch
|
||||||
|
def scan_host(ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called for each response packet
|
||||||
|
def scanner_process(data, shost, sport)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called before the scan block
|
||||||
|
def scanner_prescan(batch)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called after the scan block
|
||||||
|
def scanner_postscan(batch)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue