Skip to content

Commit a92033a

Browse files
committed
Merge pull request #1 from jlee-r7/land-2897-meterpreter-interfaces
Refactor load_session_info
2 parents ac15179 + e9ccec4 commit a92033a

File tree

2 files changed

+63
-47
lines changed

2 files changed

+63
-47
lines changed

lib/msf/base/sessions/meterpreter.rb

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -303,57 +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-
# Look at each addr/netmask and see if it matches our gateway
324-
i.addrs.zip(i.netmasks).each do |a|
325-
bits = Rex::Socket.net2bitmask( a[1] )
326-
rang = Rex::Socket::RangeWalker.new( "#{a[0]}/#{bits}" ) rescue nil
327-
if rang and rang.include?( r.gateway )
328-
nhost = a[0]
329-
break
330-
end
331-
break if nhost
332-
end
333-
break if nhost
334-
end
335-
break if nhost
336-
end
306+
hobj = nil
337307

338-
# Find the first non-loopback address
339-
if not nhost
340-
iface = ifaces.select{|i| i.ip != "127.0.0.1" and i.ip != "::1" }
341-
if iface.length > 0
342-
nhost = iface.first.ip
343-
end
344-
end
345-
end
308+
nhost = find_internet_connected_address
346309

310+
original_session_host = self.session_host
347311
# If we found a better IP address for this session, change it up
348312
# only handle cases where the DB is not connected here
349-
if not (framework.db and framework.db.active)
313+
if !(framework.db && framework.db.active)
350314
self.session_host = nhost
351315
end
352316

353-
354317
# The rest of this requires a database, so bail if it's not
355318
# there
356-
return if not (framework.db and framework.db.active)
319+
return if !(framework.db && framework.db.active)
357320

358321
::ActiveRecord::Base.connection_pool.with_connection {
359322
wspace = framework.db.find_workspace(workspace)
@@ -389,18 +352,18 @@ def load_session_info()
389352
if nhost
390353
framework.db.report_note({
391354
:type => "host.nat.server",
392-
:host => shost,
355+
:host => original_session_host,
393356
:workspace => wspace,
394357
:data => { :info => "This device is acting as a NAT gateway for #{nhost}", :client => nhost },
395358
:update => :unique_data
396359
})
397-
framework.db.report_host(:host => shost, :purpose => 'firewall' )
360+
framework.db.report_host(:host => original_session_host, :purpose => 'firewall' )
398361

399362
framework.db.report_note({
400363
:type => "host.nat.client",
401364
:host => nhost,
402365
:workspace => wspace,
403-
: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 },
404367
:update => :unique_data
405368
})
406369
framework.db.report_host(:host => nhost, :purpose => 'client' )
@@ -475,6 +438,60 @@ def create(param)
475438

476439
attr_accessor :rstream # :nodoc:
477440

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+
478495
end
479496

480497
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)