Skip to content

Commit 99ab722

Browse files
author
HD Moore
committed
Dont forget our actual mixin
1 parent 910a91a commit 99ab722

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

lib/msf/core/auxiliary/udp_scanner.rb

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
5+
###
6+
#
7+
# This module provides methods for scanning UDP services
8+
#
9+
###
10+
module Auxiliary::UDPScanner
11+
12+
include Auxiliary::Scanner
13+
14+
#
15+
# Initializes an instance of an auxiliary module that scans UDP
16+
#
17+
18+
def initialize(info = {})
19+
super
20+
21+
register_options(
22+
[
23+
Opt::CHOST,
24+
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
25+
], self.class)
26+
27+
register_advanced_options(
28+
[
29+
OptInt.new('ScannerRecvInterval', [true, 'The maximum numbers of sends before entering the processing loop', 30]),
30+
OptInt.new('ScannerMaxResends', [true, 'The maximum times to resend a packet when out of buffers', 10]),
31+
OptInt.new('ScannerRecvQueueLimit', [true, 'The maximum queue size before breaking out of the processing loop', 100]),
32+
OptInt.new('ScannerRecvWindow', [true, 'The number of seconds to wait post-scan to catch leftover replies', 15]),
33+
34+
], self.class)
35+
end
36+
37+
38+
# Define our batch size
39+
def run_batch_size
40+
datastore['BATCHSIZE'].to_i
41+
end
42+
43+
# Start scanning a batch of IP addresses
44+
def run_batch(batch)
45+
@udp_sock = Rex::Socket::Udp.create({
46+
'LocalHost' => datastore['CHOST'] || nil,
47+
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
48+
})
49+
add_socket(@udp_sock)
50+
51+
@udp_send_count = 0
52+
53+
# Provide a hook for pre-scanning setup
54+
scanner_prescan(batch)
55+
56+
# Call the including module once per IP
57+
batch.each do |ip|
58+
scan_host(ip)
59+
end
60+
61+
# Catch any stragglers
62+
stime = Time.now.to_f
63+
64+
while Time.now.to_f < ( stime + datastore['ScannerRecvWindow'] )
65+
scanner_recv(1.0)
66+
end
67+
68+
# Provide a hook for post-scanning processing
69+
scanner_postscan(batch)
70+
end
71+
72+
# Send a packet to a given host and port
73+
def scanner_send(data, ip, port)
74+
75+
resend_count = 0
76+
begin
77+
78+
@udp_sock.sendto(data, ip, port, 0)
79+
80+
rescue ::Errno::ENOBUFS
81+
resend_count += 1
82+
if resend_count > datastore['ScannerMaxResends']
83+
vprint_error("#{ip}:#{port} Max resend count hit sending #{data.length}")
84+
return false
85+
end
86+
87+
scanner_recv(0.1)
88+
89+
::IO.select(nil, nil, nil, 0.25)
90+
91+
retry
92+
93+
rescue ::Rex::ConnectionError
94+
# This fires for host unreachable, net unreachable, and broadcast sends
95+
# We can safely ignore all of these for UDP sends
96+
end
97+
98+
@udp_send_count += 1
99+
100+
if @udp_send_count % datastore['ScannerRecvInterval'] == 0
101+
scanner_recv(0.1)
102+
end
103+
104+
true
105+
end
106+
107+
# Process incoming packets and dispatch to the module
108+
# Ensure a response flood doesn't trap us in a loop
109+
# Ignore packets outside of our project's scope
110+
def scanner_recv(timeout=0.1)
111+
queue = []
112+
while (res = @udp_sock.recvfrom(65535, timeout))
113+
114+
# Ignore invalid responses
115+
break if not res[1]
116+
117+
# Ignore empty responses
118+
next if not (res[0] and res[0].length > 0)
119+
120+
# Trim the IPv6-compat prefix off if needed
121+
shost = res[1].gsub(/^::ffff:/, '')
122+
123+
# Store the source port
124+
sport = res[2]
125+
126+
# Ignore the response if we have a boundary
127+
next unless inside_workspace_boundary?(shost)
128+
129+
queue << [res[0], shost, sport]
130+
131+
if queue.length > datastore['ScannerRecvQueueLimit']
132+
break
133+
end
134+
end
135+
136+
queue.each do |q|
137+
scanner_process(*q)
138+
end
139+
140+
queue.length
141+
end
142+
143+
#
144+
# The including module override these methods
145+
#
146+
147+
# Called for each IP in the batch
148+
def scan_host(ip)
149+
end
150+
151+
# Called for each response packet
152+
def scanner_process(data, shost, sport)
153+
end
154+
155+
# Called before the scan block
156+
def scanner_prescan(batch)
157+
end
158+
159+
# Called after the scan block
160+
def scanner_postscan(batch)
161+
end
162+
163+
end
164+
end

0 commit comments

Comments
 (0)