Add the UDP scanner template, lands #4113.
There is some additional work to do regarding CHOST/CPORT, but this is not tied to the udp template changes.bug/bundler_fix
commit
96ba6da697
11
.rubocop.yml
11
.rubocop.yml
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
# inherit_from: .rubocop_todo.yml
|
# inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
Style/ClassLength:
|
Metrics/ClassLength:
|
||||||
Description: 'Most Metasploit modules are quite large. This is ok.'
|
Description: 'Most Metasploit modules are quite large. This is ok.'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -25,14 +25,14 @@ Style/Encoding:
|
||||||
Description: 'We prefer binary to UTF-8.'
|
Description: 'We prefer binary to UTF-8.'
|
||||||
EnforcedStyle: 'when_needed'
|
EnforcedStyle: 'when_needed'
|
||||||
|
|
||||||
Style/LineLength:
|
Metrics/LineLength:
|
||||||
Description: >-
|
Description: >-
|
||||||
Metasploit modules often pattern match against very
|
Metasploit modules often pattern match against very
|
||||||
long strings when identifying targets.
|
long strings when identifying targets.
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Max: 180
|
Max: 180
|
||||||
|
|
||||||
Style/MethodLength:
|
Metrics/MethodLength:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Description: >-
|
Description: >-
|
||||||
While the style guide suggests 10 lines, exploit definitions
|
While the style guide suggests 10 lines, exploit definitions
|
||||||
|
@ -44,6 +44,11 @@ Style/MethodLength:
|
||||||
Style/Encoding:
|
Style/Encoding:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# %q() is super useful for long strings split over multiple lines and
|
||||||
|
# is very common in module constructors for things like descriptions
|
||||||
|
Style/UnneededPercentQ:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Style/NumericLiterals:
|
Style/NumericLiterals:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
Description: 'This often hurts readability for exploit-ish code.'
|
Description: 'This often hurts readability for exploit-ish code.'
|
||||||
|
|
|
@ -241,10 +241,10 @@ end
|
||||||
|
|
||||||
def scanner_show_progress
|
def scanner_show_progress
|
||||||
pct = scanner_progress
|
pct = scanner_progress
|
||||||
if(pct >= (@range_percent + @show_percent))
|
if pct >= (@range_percent + @show_percent)
|
||||||
@range_percent = @range_percent + @show_percent
|
@range_percent = @range_percent + @show_percent
|
||||||
tdlen = @range_count.to_s.length
|
tdlen = @range_count.to_s.length
|
||||||
print_status("Scanned #{"%.#{tdlen}d" % @range_done} of #{@range_count} hosts (#{"%.3d" % pct.to_i}% complete)")
|
print_status(sprintf("Scanned %#{tdlen}d of %d hosts (%d%% complete)", @range_done, @range_count, pct))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,33 +8,36 @@ module Msf
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
module Auxiliary::UDPScanner
|
module Auxiliary::UDPScanner
|
||||||
|
|
||||||
include Auxiliary::Scanner
|
include Auxiliary::Scanner
|
||||||
|
|
||||||
|
# A hash of results of a given batch run, keyed by host
|
||||||
|
attr_accessor :results
|
||||||
|
|
||||||
#
|
#
|
||||||
# Initializes an instance of an auxiliary module that scans UDP
|
# Initializes an instance of an auxiliary module that scans UDP
|
||||||
#
|
#
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::CHOST,
|
Opt::RPORT,
|
||||||
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
||||||
|
OptInt.new('THREADS', [true, "The number of concurrent threads", 10])
|
||||||
], self.class)
|
], self.class)
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
|
Opt::CHOST,
|
||||||
|
Opt::CPORT,
|
||||||
OptInt.new('ScannerRecvInterval', [true, 'The maximum numbers of sends before entering the processing loop', 30]),
|
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('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('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]),
|
OptInt.new('ScannerRecvWindow', [true, 'The number of seconds to wait post-scan to catch leftover replies', 15])
|
||||||
|
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Define our batch size
|
# Define our batch size
|
||||||
def run_batch_size
|
def run_batch_size
|
||||||
datastore['BATCHSIZE'].to_i
|
datastore['BATCHSIZE'].to_i
|
||||||
|
@ -44,6 +47,7 @@ module Auxiliary::UDPScanner
|
||||||
def run_batch(batch)
|
def run_batch(batch)
|
||||||
@udp_sock = Rex::Socket::Udp.create({
|
@udp_sock = Rex::Socket::Udp.create({
|
||||||
'LocalHost' => datastore['CHOST'] || nil,
|
'LocalHost' => datastore['CHOST'] || nil,
|
||||||
|
'LocalPort' => datastore['CPORT'] || 0,
|
||||||
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
||||||
})
|
})
|
||||||
add_socket(@udp_sock)
|
add_socket(@udp_sock)
|
||||||
|
@ -155,12 +159,25 @@ module Auxiliary::UDPScanner
|
||||||
queue.length
|
queue.length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cport
|
||||||
|
datastore['CPORT']
|
||||||
|
end
|
||||||
|
|
||||||
|
def rport
|
||||||
|
datastore['RPORT']
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# The including module override these methods
|
# The including module may override some of these methods
|
||||||
#
|
#
|
||||||
|
|
||||||
# Called for each IP in the batch
|
# Builds and returns the probe to be sent
|
||||||
|
def build_probe
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called for each IP in the batch. This will send all necessary probes.
|
||||||
def scan_host(ip)
|
def scan_host(ip)
|
||||||
|
scanner_send(build_probe, ip, rport)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called for each response packet
|
# Called for each response packet
|
||||||
|
@ -169,11 +186,12 @@ module Auxiliary::UDPScanner
|
||||||
|
|
||||||
# Called before the scan block
|
# Called before the scan block
|
||||||
def scanner_prescan(batch)
|
def scanner_prescan(batch)
|
||||||
|
vprint_status("Sending probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
|
||||||
|
@results = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Called after the scan block
|
# Called after the scan block
|
||||||
def scanner_postscan(batch)
|
def scanner_postscan(batch)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Auxiliary
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::Auxiliary::UDPScanner
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(
|
||||||
|
update_info(
|
||||||
|
info,
|
||||||
|
# TODO: fill in all of this
|
||||||
|
'Name' => 'UDP Scanner Example',
|
||||||
|
'Description' => %q(
|
||||||
|
This module is an example of how to send probes to UDP services
|
||||||
|
en-masse, analyze any responses, and then report on any discovered
|
||||||
|
hosts, services, vulnerabilities or otherwise noteworthy things.
|
||||||
|
Simply address any of the TODOs.
|
||||||
|
),
|
||||||
|
'Author' => 'Joe Contributor <joe_contributor[at]example.com>',
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://example.com/~jcontributor']
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Mar 15 2014',
|
||||||
|
'License' => MSF_LICENSE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
# TODO: change to the port you need to scan
|
||||||
|
Opt::RPORT(12345)
|
||||||
|
], self.class)
|
||||||
|
|
||||||
|
# TODO: add any advanced, special options here, otherwise remove
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptBool.new('SPECIAL', [true, 'Try this special thing', false])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
super
|
||||||
|
# TODO: do any sort of preliminary sanity checking, like perhaps validating some options
|
||||||
|
# in the datastore, etc.
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: construct the appropriate probe here.
|
||||||
|
def build_probe
|
||||||
|
@probe ||= 'abracadabra!'
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: this is called before the scan block for each batch of hosts. Do any
|
||||||
|
# per-batch setup here, otherwise remove it.
|
||||||
|
def scanner_prescan(batch)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: this is called for each IP in the batch. This will send all of the
|
||||||
|
# necessary probes. If something different must be done for each IP, do it
|
||||||
|
# here, otherwise remove it.
|
||||||
|
def scan_host(ip)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called for each response packet
|
||||||
|
def scanner_process(response, src_host, _src_port)
|
||||||
|
# TODO: inspect each response, perhaps confirming that it is a valid
|
||||||
|
# response for the service/protocol in question and/or analyzing it more
|
||||||
|
# closely. In this case, we simply check to see that it is of reasonable
|
||||||
|
# size and storing a result for this host iff so. Note that src_port may
|
||||||
|
# not actually be the same as the original RPORT for some services if they
|
||||||
|
# respond back from different ports
|
||||||
|
return unless response.size >= 42
|
||||||
|
@results[src_host] ||= []
|
||||||
|
|
||||||
|
# TODO: store something about this response, perhaps the response itself,
|
||||||
|
# some metadata obtained by analyzing it, the proof that it is vulnerable
|
||||||
|
# to something, etc. In this example, we simply look for any response
|
||||||
|
# with a sequence of 5 useful ASCII characters and, iff found, we store
|
||||||
|
# that sequence
|
||||||
|
/(?<relevant>[\x20-\x7E]{5})/ =~ response && @results[src_host] << relevant
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called after the scan block
|
||||||
|
def scanner_postscan(_batch)
|
||||||
|
@results.each_pair do |host, relevant_responses|
|
||||||
|
peer = "#{host}:#{rport}"
|
||||||
|
|
||||||
|
# report on the host
|
||||||
|
report_host(host: host)
|
||||||
|
|
||||||
|
# report on the service, since it responded
|
||||||
|
report_service(
|
||||||
|
host: host,
|
||||||
|
proto: 'udp',
|
||||||
|
port: rport,
|
||||||
|
name: 'example',
|
||||||
|
# show at most 4 relevant responses
|
||||||
|
info: relevant_responses[0, 4].join(',')
|
||||||
|
)
|
||||||
|
|
||||||
|
if relevant_responses.empty?
|
||||||
|
vprint_status("#{peer} Not vulnerable to something")
|
||||||
|
else
|
||||||
|
print_good("#{peer} Vulnerable to something!")
|
||||||
|
report_vuln(
|
||||||
|
host: host,
|
||||||
|
port: rport,
|
||||||
|
proto: 'udp',
|
||||||
|
name: 'something!',
|
||||||
|
info: "Got #{relevant_responses.size} response(s)",
|
||||||
|
refs: references
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue