Skip to content

Commit 57aef9a

Browse files
committed
Land rapid7#4177, @hmoore-r7's fix for rapid7#4169
2 parents 386a191 + 2abc636 commit 57aef9a

23 files changed

+112
-35
lines changed

lib/metasploit/framework/login_scanner/ftp.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def attempt_login(credential)
4141

4242
begin
4343
success = connect_login(credential.public, credential.private)
44-
rescue ::EOFError, Errno::ECONNRESET, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
44+
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
4545
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
4646
success = false
4747
end

lib/metasploit/framework/login_scanner/telnet.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def attempt_login(credential)
9292
end
9393

9494
end
95-
rescue ::EOFError, Errno::ECONNRESET, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
95+
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
9696
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
9797
end
9898

lib/msf/core/auxiliary/rservices.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def connect_from_privileged_port(start_port = 1023)
3535
begin
3636
sd = connect(true, { 'CPORT' => cport })
3737

38-
rescue Rex::AddressInUse
38+
rescue Rex::BindFailed
3939
# Ignore and try again
4040
#vprint_error("Unable to connect: #{$!}")
4141

lib/msf/core/auxiliary/scanner.rb

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def run
5757

5858
threads_max = datastore['THREADS'].to_i
5959
@tl = []
60+
@scan_errors = []
6061

6162
#
6263
# Sanity check threading given different conditions
@@ -87,17 +88,22 @@ def run
8788
begin
8889

8990
if (self.respond_to?('run_range'))
90-
# No automated progress reporting for run_range
91+
# No automated progress reporting or error handling for run_range
9192
return run_range(datastore['RHOSTS'])
9293
end
9394

9495
if (self.respond_to?('run_host'))
9596

96-
@tl = []
97-
9897
loop do
98+
# Stop scanning if we hit a fatal error
99+
break if has_fatal_errors?
100+
99101
# Spawn threads for each host
100102
while (@tl.length < threads_max)
103+
104+
# Stop scanning if we hit a fatal error
105+
break if has_fatal_errors?
106+
101107
ip = ar.next_ip
102108
break if not ip
103109

@@ -108,6 +114,10 @@ def run
108114

109115
begin
110116
nmod.run_host(targ)
117+
rescue ::Rex::BindFailed
118+
if datastore['CHOST']
119+
@scan_errors << "The source IP (CHOST) value of #{datastore['CHOST']} was not usable"
120+
end
111121
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError
112122
rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError
113123
raise $!
@@ -120,6 +130,9 @@ def run
120130
end
121131
end
122132

133+
# Stop scanning if we hit a fatal error
134+
break if has_fatal_errors?
135+
123136
# Exit once we run out of hosts
124137
if(@tl.length == 0)
125138
break
@@ -139,6 +152,7 @@ def run
139152
scanner_show_progress() if @show_progress
140153
end
141154

155+
scanner_handle_fatal_errors
142156
return
143157
end
144158

@@ -153,10 +167,12 @@ def run
153167

154168
ar = Rex::Socket::RangeWalker.new(datastore['RHOSTS'])
155169

156-
@tl = []
157-
158170
while(true)
159171
nohosts = false
172+
173+
# Stop scanning if we hit a fatal error
174+
break if has_fatal_errors?
175+
160176
while (@tl.length < threads_max)
161177

162178
batch = []
@@ -178,6 +194,10 @@ def run
178194
mybatch = bat.dup
179195
begin
180196
nmod.run_batch(mybatch)
197+
rescue ::Rex::BindFailed
198+
if datastore['CHOST']
199+
@scan_errors << "The source IP (CHOST) value of #{datastore['CHOST']} was not usable"
200+
end
181201
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
182202
rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError
183203
raise $!
@@ -197,6 +217,9 @@ def run
197217
end
198218
end
199219

220+
# Stop scanning if we hit a fatal error
221+
break if has_fatal_errors?
222+
200223
# Exit if there are no more pending threads
201224
if (@tl.length == 0)
202225
break
@@ -218,6 +241,7 @@ def run
218241
scanner_show_progress() if @show_progress
219242
end
220243

244+
scanner_handle_fatal_errors
221245
return
222246
end
223247

@@ -240,12 +264,33 @@ def seppuko!
240264
end
241265
end
242266

267+
def has_fatal_errors?
268+
@scan_errors && !@scan_errors.empty?
269+
end
270+
271+
def scanner_handle_fatal_errors
272+
return unless has_fatal_errors?
273+
return unless @tl
274+
275+
# First kill any running threads
276+
@tl.each {|t| t.kill if t.alive? }
277+
278+
# Show the unique errors triggered by the scan
279+
uniq_errors = @scan_errors.uniq
280+
uniq_errors.each do |emsg|
281+
print_error("Fatal: #{emsg}")
282+
end
283+
print_error("Scan terminated due to #{uniq_errors.size} fatal error(s)")
284+
end
285+
243286
def scanner_progress
244287
return 0 unless @range_done and @range_count
245288
pct = (@range_done / @range_count.to_f) * 100
246289
end
247290

248291
def scanner_show_progress
292+
# it should already be in the process of shutting down if there are fatal errors
293+
return if has_fatal_errors?
249294
pct = scanner_progress
250295
if pct >= (@range_percent + @show_percent)
251296
@range_percent = @range_percent + @show_percent

lib/rex/exceptions.rb

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,25 +213,57 @@ def to_s
213213
end
214214
end
215215

216+
###
217+
#
218+
# This connection error is raised when an attempt is made to connect
219+
# to a broadcast or network address.
220+
#
221+
###
222+
class InvalidDestination < ConnectionError
223+
include SocketError
224+
include HostCommunicationError
225+
226+
def to_s
227+
"The destination is invalid: #{addr_to_s}."
228+
end
229+
end
216230

217231
###
218232
#
219233
# This exception is raised when an attempt to use an address or port that is
220-
# already in use occurs, such as binding to a host on a given port that is
221-
# already in use. Note that Windows raises this in some cases when attempting
222-
# to connect to addresses that it can't handle, e.g. "0.0.0.0". Thus, this is
223-
# a ConnectionError.
234+
# already in use or onot available occurs. such as binding to a host on a
235+
# given port that is already in use, or when a bind address is specified that
236+
# is not available to the host.
224237
#
225238
###
239+
class BindFailed < ::ArgumentError
240+
include SocketError
241+
include HostCommunicationError
242+
243+
def to_s
244+
"The address is already in use or unavailable: #{addr_to_s}."
245+
end
246+
end
247+
248+
##
249+
#
250+
# This exception is listed for backwards compatibility. We had been
251+
# using AddressInUse as the exception for both bind errors and connection
252+
# errors triggered by connection attempts to broadcast and network addresses.
253+
# The two classes above have split this into their respective sources, but
254+
# callers may still expect the old behavior.
255+
#
256+
##
226257
class AddressInUse < ConnectionError
227258
include SocketError
228259
include HostCommunicationError
229260

230261
def to_s
231-
"The address is already in use #{addr_to_s}."
262+
"The address is already in use or unavailable: #{addr_to_s}."
232263
end
233264
end
234265

266+
235267
###
236268
#
237269
# This exception is raised when an unsupported internet protocol is specified.

lib/rex/post/meterpreter/ui/console.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def run_command(dispatcher, method, arguments)
106106
log_error("Operation timed out.")
107107
rescue RequestError => info
108108
log_error(info.to_s)
109-
rescue Rex::AddressInUse => e
109+
rescue Rex::InvalidDestination => e
110110
log_error(e.message)
111111
rescue ::Errno::EPIPE, ::OpenSSL::SSL::SSLError, ::IOError
112112
self.client.kill

lib/rex/socket/comm/local.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def self.create_by_type(param, type, proto = 0)
195195

196196
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
197197
sock.close
198-
raise Rex::AddressInUse.new(param.localhost, param.localport), caller
198+
raise Rex::BindFailed.new(param.localhost, param.localport), caller
199199
end
200200
end
201201

@@ -295,7 +295,7 @@ def self.create_by_type(param, type, proto = 0)
295295

296296
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
297297
sock.close
298-
raise Rex::AddressInUse.new(ip, port), caller
298+
raise Rex::InvalidDestination.new(ip, port), caller
299299

300300
rescue Errno::ETIMEDOUT
301301
sock.close

modules/auxiliary/gather/xerox_pwd_extract.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def write
9696
begin
9797
connect(true, 'RPORT' => jport)
9898
sock.put(create_print_job)
99-
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::AddressInUse
99+
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout
100100
print_error("#{rhost}:#{jport} - Error connecting to #{rhost}")
101101
ensure
102102
disconnect
@@ -113,7 +113,7 @@ def retrieve
113113
res = sock.get_once || ''
114114
passwd = res.match(/\r\n\s(.+?)\n/)
115115
return passwd ? passwd[1] : ''
116-
rescue ::EOFError, ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::AddressInUse, EOFError
116+
rescue ::EOFError, ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, ::EOFError
117117
print_error("#{rhost}:#{jport} - Error getting password from #{rhost}")
118118
return
119119
ensure
@@ -150,7 +150,7 @@ def remove
150150
begin
151151
connect(true, 'RPORT' => jport)
152152
sock.put(remove_print_job)
153-
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::AddressInUse
153+
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout
154154
print_error("#{rhost}:#{jport} - Error removing print job from #{rhost}")
155155
ensure
156156
disconnect

modules/auxiliary/scanner/rservices/rexec_login.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def listen_on_port(stderr_port)
151151
begin
152152
sd = Rex::Socket.create_tcp_server('LocalPort' => stderr_port)
153153

154-
rescue Rex::AddressInUse
154+
rescue Rex::BindFailed
155155
# Ignore and try again
156156

157157
end

modules/auxiliary/scanner/rservices/rsh_login.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def listen_on_privileged_port
201201
begin
202202
sd = Rex::Socket.create_tcp_server('LocalPort' => lport)
203203

204-
rescue Rex::AddressInUse
204+
rescue Rex::BindFailed
205205
# Ignore and try again
206206

207207
end

0 commit comments

Comments
 (0)