Module: Msf::Auxiliary::UDPScanner

Includes:
Scanner
Defined in:
lib/msf/core/auxiliary/udp_scanner.rb

Overview

This module provides methods for scanning UDP services

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Scanner

#add_delay_jitter, #check, #has_check?, #has_fatal_errors?, #peer, #run, #scanner_handle_fatal_errors, #scanner_progress, #scanner_show_progress, #seppuko!

Instance Attribute Details

#resultsObject

A hash of results of a given batch run, keyed by host


14
15
16
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 14

def results
  @results
end

Instance Method Details

#build_probeObject

Builds and returns the probe to be sent


221
222
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 221

def build_probe
end

#cleanup_udp_socketsObject


66
67
68
69
70
71
72
73
74
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 66

def cleanup_udp_sockets
  @udp_sockets_mutex.synchronize do
    @udp_sockets.each do |key, sock|
      @udp_sockets.delete(key)
      remove_socket(sock)
      sock.close
    end
  end
end

#cportObject


208
209
210
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 208

def cport
  datastore['CPORT']
end

#initialize(info = {}) ⇒ Object

Initializes an instance of an auxiliary module that scans UDP


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 19

def initialize(info = {})
  super

  register_options(
  [
    Opt::RPORT,
    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)

  register_advanced_options(
  [
    Opt::CHOST,
    Opt::CPORT,
    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

#rportObject


212
213
214
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 212

def rport
  datastore['RPORT']
end

#run_batch(batch) ⇒ Object

Start scanning a batch of IP addresses


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 77

def run_batch(batch)
  @udp_sockets = {}
  @udp_sockets_mutex = Mutex.new

  @udp_send_count = 0
  @interval_mutex = Mutex.new

  # 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

#run_batch_sizeObject

Define our batch size


42
43
44
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 42

def run_batch_size
  datastore['BATCHSIZE'].to_i
end

#scan_host(ip) ⇒ Object

Called for each IP in the batch. This will send all necessary probes.


225
226
227
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 225

def scan_host(ip)
  scanner_send(build_probe, ip, rport)
end

#scanner_postscan(batch) ⇒ Object

Called after the scan block


242
243
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 242

def scanner_postscan(batch)
end

#scanner_prescan(batch) ⇒ Object

Called before the scan block


236
237
238
239
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 236

def scanner_prescan(batch)
  vprint_status("Sending probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
  @results = {}
end

#scanner_process(data, shost, _sport) ⇒ Object

Called for each response packet


230
231
232
233
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 230

def scanner_process(data, shost, _sport)
  @results[shost] ||= []
  @results[shost] << data
end

#scanner_recv(timeout = 0.1) ⇒ Object

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


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 169

def scanner_recv(timeout = 0.1)
  queue = []
  start = Time.now
  while Time.now - start < timeout do
    readable, _, _ = ::IO.select(@udp_sockets.values, nil, nil, timeout)
    if readable
      for sock in readable
        res = 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].sub(/^::ffff:/, '')

        # Ignore the response if we have a boundary
        next unless inside_workspace_boundary?(shost)

        queue << [res[0], shost, res[2]]

        if queue.length > datastore['ScannerRecvQueueLimit']
          break
        end
      end
    end
  end

  cleanup_udp_sockets

  queue.each do |q|
    scanner_process(*q)
  end

  queue.length
end

#scanner_send(data, ip, port) ⇒ Object

Send a packet to a given host and port


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 122

def scanner_send(data, ip, port)

  # flatten any bindata objects
  data = data.to_binary_s if data.respond_to?('to_binary_s')

  resend_count = 0

  begin
    addrinfo = Addrinfo.ip(ip)
    unless addrinfo.ipv4_multicast? || addrinfo.ipv6_multicast?
      sock = udp_socket(ip, port, bind_peer: true)
      sock.send(data, 0)
    else
      sock = udp_socket(ip, port, bind_peer: false)
      sock.sendto(data, ip, port, 0)
    end

  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)
    sleep(0.25)

    retry

  rescue ::Rex::ConnectionError, ::Errno::ECONNREFUSED
    # This fires for host unreachable, net unreachable, and broadcast sends
    # We can safely ignore all of these for UDP sends
  end

  @interval_mutex.synchronize do
    @udp_send_count += 1
    if @udp_send_count % datastore['ScannerRecvInterval'] == 0
      scanner_recv(0.1)
    end
  end

  true
end

#scanner_spoof_send(data, ip, port, srcip, num_packets = 1) ⇒ Object

Send a spoofed packet to a given host and port


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 104

def scanner_spoof_send(data, ip, port, srcip, num_packets=1)
  open_pcap
  p = PacketFu::UDPPacket.new
  p.ip_saddr = srcip
  p.ip_daddr = ip
  p.ip_ttl = 255
  p.udp_src = (rand((2**16)-1024)+1024).to_i
  p.udp_dst = port
  p.payload = data
  p.recalc
  print_status("Sending #{num_packets} packet(s) to #{ip} from #{srcip}")
  1.upto(num_packets) do |x|
    break unless capture_sendto(p, ip)
  end
  close_pcap
end

#udp_socket(ip, port, bind_peer: true) ⇒ Object


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/msf/core/auxiliary/udp_scanner.rb', line 46

def udp_socket(ip, port, bind_peer: true)
  key = "#{ip}:#{port}:#{bind_peer ? 'bound' : 'unbound'}"
  @udp_sockets_mutex.synchronize do
    unless @udp_sockets.key?(key)
      sock_info = {
        'LocalHost' => datastore['CHOST'] || nil,
        'LocalPort' => datastore['CPORT'] || 0,
        'Context'   => { 'Msf' => framework, 'MsfExploit' => self }
      }
      if bind_peer
        sock_info['PeerHost'] = ip
        sock_info['PeerPort'] = port
      end
      @udp_sockets[key] = Rex::Socket::Udp.create(sock_info)
      add_socket(@udp_sockets[key])
    end
    return @udp_sockets[key]
  end
end