Skip to content

Commit 6b4eb9a

Browse files
author
HD Moore
committed
Differentiate failed binds from connects, closes rapid7#4169
This change adds two new Rex exceptions and changes the local comm to raise the right one depending on the circumstances. The problem with the existing model is that failed binds and failed connections both raised the same exception. This change is backwards compatible with modules that rescue Rex::AddressInUse in additi on to Rex::ConnectionError. There were two corner cases that rescued Rex::AddressInUse specifically: 1. The 'r'-services mixin and modules caught the old exception when handling bind errors. These have been updated to use BindFailed 2. The meterpreter client had a catch for the old exception when the socket reports a bad destination (usually a network connection dropped). This has been updat ed to use InvalidDestination as that was the intention prior to this change. Since AddressInUse was part of ConnectionError, modules and mixins which caught both in the same rescue have been updated to just catch ConnectionError.
1 parent dbd5937 commit 6b4eb9a

23 files changed

+105
-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: 43 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 @scan_errors.length > 0
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 @scan_errors.length > 0
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 @scan_errors.length > 0
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_report_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 @scan_errors.length > 0
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 @scan_errors.length > 0
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_report_fatal_errors
221245
return
222246
end
223247

@@ -240,6 +264,20 @@ def seppuko!
240264
end
241265
end
242266

267+
def scanner_report_fatal_errors
268+
return unless @scan_errors && @scan_errors.length > 0
269+
return unless @tl
270+
271+
# First kill any running threads
272+
@tl.each {|t| t.kill if t.alive? }
273+
274+
# Show the unique errors triggered by the scan
275+
@scan_errors.uniq.each do |emsg|
276+
print_error("Fatal: #{emsg}")
277+
end
278+
print_status("Scan terminated due to one or more fatal errors")
279+
end
280+
243281
def scanner_progress
244282
return 0 unless @range_done and @range_count
245283
pct = (@range_done / @range_count.to_f) * 100

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)