Skip to content

Commit 10ac7a2

Browse files
committed
Land rapid7#2897 Sane address resolution [FixRM rapid7#7259]
2 parents f766a74 + a92033a commit 10ac7a2

File tree

2 files changed

+63
-42
lines changed

2 files changed

+63
-42
lines changed

lib/msf/base/sessions/meterpreter.rb

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -303,52 +303,20 @@ def load_session_info()
303303
safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_")
304304
self.info = safe_info
305305

306-
# Enumerate network interfaces to detect IP
307-
ifaces = self.net.config.get_interfaces().flatten rescue []
308-
routes = self.net.config.get_routes().flatten rescue []
309-
shost = self.session_host
310-
311-
# Try to match our visible IP to a real interface
312-
# TODO: Deal with IPv6 addresses
313-
found = !!(ifaces.find {|i| i.addrs.find {|a| a == shost } })
314-
nhost = nil
315-
hobj = nil
316-
317-
if Rex::Socket.is_ipv4?(shost) and not found
318-
319-
# Try to find an interface with a default route
320-
default_routes = routes.select{ |r| r.subnet == "0.0.0.0" || r.subnet == "::" }
321-
default_routes.each do |r|
322-
ifaces.each do |i|
323-
bits = Rex::Socket.net2bitmask( i.netmask ) rescue 32
324-
rang = Rex::Socket::RangeWalker.new( "#{i.ip}/#{bits}" ) rescue nil
325-
if rang and rang.include?( r.gateway )
326-
nhost = i.ip
327-
break
328-
end
329-
end
330-
break if nhost
331-
end
306+
hobj = nil
332307

333-
# Find the first non-loopback address
334-
if not nhost
335-
iface = ifaces.select{|i| i.ip != "127.0.0.1" and i.ip != "::1" }
336-
if iface.length > 0
337-
nhost = iface.first.ip
338-
end
339-
end
340-
end
308+
nhost = find_internet_connected_address
341309

310+
original_session_host = self.session_host
342311
# If we found a better IP address for this session, change it up
343312
# only handle cases where the DB is not connected here
344-
if not (framework.db and framework.db.active)
313+
if !(framework.db && framework.db.active)
345314
self.session_host = nhost
346315
end
347316

348-
349317
# The rest of this requires a database, so bail if it's not
350318
# there
351-
return if not (framework.db and framework.db.active)
319+
return if !(framework.db && framework.db.active)
352320

353321
::ActiveRecord::Base.connection_pool.with_connection {
354322
wspace = framework.db.find_workspace(workspace)
@@ -384,18 +352,18 @@ def load_session_info()
384352
if nhost
385353
framework.db.report_note({
386354
:type => "host.nat.server",
387-
:host => shost,
355+
:host => original_session_host,
388356
:workspace => wspace,
389357
:data => { :info => "This device is acting as a NAT gateway for #{nhost}", :client => nhost },
390358
:update => :unique_data
391359
})
392-
framework.db.report_host(:host => shost, :purpose => 'firewall' )
360+
framework.db.report_host(:host => original_session_host, :purpose => 'firewall' )
393361

394362
framework.db.report_note({
395363
:type => "host.nat.client",
396364
:host => nhost,
397365
:workspace => wspace,
398-
:data => { :info => "This device is traversing NAT gateway #{shost}", :server => shost },
366+
:data => { :info => "This device is traversing NAT gateway #{original_session_host}", :server => original_session_host },
399367
:update => :unique_data
400368
})
401369
framework.db.report_host(:host => nhost, :purpose => 'client' )
@@ -470,6 +438,60 @@ def create(param)
470438

471439
attr_accessor :rstream # :nodoc:
472440

441+
# Rummage through this host's routes and interfaces looking for an
442+
# address that it uses to talk to the internet.
443+
#
444+
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_interfaces
445+
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes
446+
# @return [String] The address from which this host reaches the
447+
# internet, as ASCII. e.g.: "192.168.100.156"
448+
def find_internet_connected_address
449+
450+
ifaces = self.net.config.get_interfaces().flatten rescue []
451+
routes = self.net.config.get_routes().flatten rescue []
452+
453+
# Try to match our visible IP to a real interface
454+
found = !!(ifaces.find { |i| i.addrs.find { |a| a == session_host } })
455+
nhost = nil
456+
457+
# If the host has no address that matches what we see, then one of
458+
# us is behind NAT so we have to look harder.
459+
if !found
460+
# Grab all routes to the internet
461+
default_routes = routes.select { |r| r.subnet == "0.0.0.0" || r.subnet == "::" }
462+
463+
default_routes.each do |route|
464+
# Now try to find an interface whose network includes this
465+
# Route's gateway, which means it's the one the host uses to get
466+
# to the interweb.
467+
ifaces.each do |i|
468+
# Try all the addresses this interface has configured
469+
addr_and_mask = i.addrs.zip(i.netmasks).find do |addr, netmask|
470+
bits = Rex::Socket.net2bitmask( netmask )
471+
range = Rex::Socket::RangeWalker.new("#{addr}/#{bits}") rescue nil
472+
473+
!!(range && range.valid? && range.include?(route.gateway))
474+
end
475+
if addr_and_mask
476+
nhost = addr_and_mask[0]
477+
break
478+
end
479+
end
480+
break if nhost
481+
end
482+
483+
if !nhost
484+
# Find the first non-loopback address
485+
non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" }
486+
if non_loopback
487+
nhost = non_loopback.ip
488+
end
489+
end
490+
end
491+
492+
nhost
493+
end
494+
473495
end
474496

475497
end

lib/rex/post/meterpreter/extensions/stdapi/net/config.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,9 @@ def each_interface(&block)
4949
get_interfaces().each(&block)
5050
end
5151

52-
#
5352
# Returns an array of network interfaces with each element.
5453
#
55-
# being an Interface
54+
# @return [Array<Interface>]
5655
def get_interfaces
5756
request = Packet.create_request('stdapi_net_config_get_interfaces')
5857
ifaces = []

0 commit comments

Comments
 (0)