Land #8221, move autoroute

bug/bundler_fix
James Lee 2017-05-17 15:17:45 -05:00
commit b78749bc1b
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
4 changed files with 222 additions and 104 deletions

View File

@ -18,6 +18,8 @@ Netmask Examples `set NETMASK 255.255.255.0` or `set NETMASK /24`
### delete
This CMD option is used to remove a route from Metasploit's routing table. The IPv4 subnet and netmask (IPv4 or CIDR) of the route to be removed are required. The session number of the Meterpreter session to run the module on is also required. Use `route print` or the print CMD option to display the current Metasploit routing table.
To remove all routes associated with the specified session, use CMD delete and leave the subnet option blank.
### print
This CMD option is used to display Metasploit's routing table. This option has the same functionality as the `route print` command.
@ -84,13 +86,13 @@ You need a Windows Meterpreter session on a host that has a different public IP
First set up a default route for the Meterpreter session.
```
meterpreter > run post/windows/manage/autoroute CMD=default
meterpreter > run post/multi/manage/autoroute CMD=default
```
or
```
msf > use post/windows/manage/autoroute
msf > use post/multi/manage/autoroute
msf post(autoroute) > set SESSION session-id
msf post(autoroute) > set CMD default
msf post(autoroute) > exploit

View File

@ -56,7 +56,7 @@ module Scriptable
#
def legacy_script_to_post_module(script_name)
{
'autoroute' => 'post/windows/manage/autoroute',
'autoroute' => 'post/multi/manage/autoroute',
'checkvm' => 'post/windows/gather/checkvm',
'duplicate' => 'post/windows/manage/multi_meterpreter_inject',
'enum_chrome' => 'post/windows/gather/enum_chrome',
@ -68,7 +68,7 @@ module Scriptable
'file_collector' => 'post/windows/gather/enum_files',
'get_application_list' => 'post/windows/gather/enum_applications',
'get_filezilla_creds' => 'post/windows/gather/credentials/filezilla_server',
'get_local_subnets' => 'post/windows/manage/autoroute',
'get_local_subnets' => 'post/multi/manage/autoroute',
'get_valid_community' => 'post/windows/gather/enum_snmp',
'getcountermeasure' => 'post/windows/manage/killav',
'getgui' => 'post/windows/manage/enable_rdp',

View File

@ -6,10 +6,9 @@
class MetasploitModule < Msf::Post
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Manage Network Route via Meterpreter Session',
'Name' => 'Multi Manage Network Route via Meterpreter Session',
'Description' => %q{This module manages session routing via an existing
Meterpreter session. It enables other modules to 'pivot' through a
compromised host when connecting to the named NETWORK and SUBMASK.
@ -22,9 +21,8 @@ class MetasploitModule < Msf::Post
'Author' =>
[
'todb',
'Josh Hale <jhale85446[at]gmail.com>'
'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>'
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter']
))
@ -36,6 +34,8 @@ class MetasploitModule < Msf::Post
])
end
# Get the CMD string vs ACTION
#
# Backwards compatability: This was changed because the option name of "ACTION"
# is special for some things, and indicates the :action attribute, not a datastore option.
# However, this is a semi-popular module, though, so I'd prefer not to break people's
@ -43,6 +43,8 @@ class MetasploitModule < Msf::Post
#
# TODO: The better solution is to use 'Action' and 'DefaultAction' info elements,
# but there are some squirelly problems right now with rendering these for post modules.
#
# @return [string class] cmd string
def route_cmd
if datastore['ACTION'].to_s.empty?
datastore['CMD'].to_s.downcase.to_sym
@ -53,7 +55,11 @@ class MetasploitModule < Msf::Post
end
# Run Method for when run command is issued
#
# @return [void] A useful return value is not expected here
def run
return unless session_good?
print_status("Running module against #{sysinfo['Computer']}")
case route_cmd()
@ -62,7 +68,7 @@ class MetasploitModule < Msf::Post
when :add
if validate_cmd(datastore['SUBNET'],netmask)
print_status("Adding a route to %s/%s..." % [datastore['SUBNET'],netmask])
add_route(:subnet => datastore['SUBNET'], :netmask => netmask)
add_route(datastore['SUBNET'], netmask)
end
when :autoadd
autoadd_routes
@ -71,63 +77,111 @@ class MetasploitModule < Msf::Post
when :delete
if datastore['SUBNET']
print_status("Deleting route to %s/%s..." % [datastore['SUBNET'],netmask])
delete_route(:subnet => datastore['SUBNET'], :netmask => netmask)
delete_route(datastore['SUBNET'], netmask)
else
delete_all_routes()
end
end
end
# Delete all routes from framework routing table.
#
# @return [void] A useful return value is not expected here
def delete_all_routes
if Rex::Socket::SwitchBoard.routes.size > 0
routes = []
Rex::Socket::SwitchBoard.each do |route|
routes << {:subnet => route.subnet, :netmask => route.netmask}
print_status("Deleting all routes associated with session: #{session.sid.to_s}.")
while true
count = 0
Rex::Socket::SwitchBoard.each do |route|
if route.comm == session
print_status("Deleting: #{route.subnet}/#{route.netmask}")
delete_route(route.subnet, route.netmask)
end
end
Rex::Socket::SwitchBoard.each do |route|
count = count + 1 if route.comm == session
end
break if count == 0
end
routes.each {|route_opts| delete_route(route_opts)}
print_status "Deleted all routes"
print_status("Deleted all routes")
else
print_status "No routes have been added yet"
print_status("No routes associated with this session to delete.")
end
end
# Print all of the active routes defined on the framework
#
# Identical functionality to command_dispatcher/core.rb, and
# nearly identical code
#
# @return [void] A useful return value is not expected here
def print_routes
if Rex::Socket::SwitchBoard.routes.size > 0
tbl = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "Active Routing Table",
'Prefix' => "\n",
'Postfix' => "\n",
'Columns' => [
# IPv4 Table
tbl_ipv4 = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "IPv4 Active Routing Table",
'Prefix' => "\n",
'Postfix' => "\n",
'Columns' =>
[
'Subnet',
'Netmask',
'Gateway',
],
'ColProps' => {
'ColProps' =>
{
'Subnet' => { 'MaxWidth' => 17 },
'Netmask' => { 'MaxWidth' => 17 },
})
ret = []
Rex::Socket::SwitchBoard.each { |route|
if (route.comm.kind_of?(Msf::Session))
gw = "Session #{route.comm.sid}"
else
gw = route.comm.name.split(/::/)[-1]
end
tbl << [ route.subnet, route.netmask, gw ]
}
print_line tbl.to_s
else
print_status "No routes have been added yet"
# IPv6 Table
tbl_ipv6 = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "IPv6 Active Routing Table",
'Prefix' => "\n",
'Postfix' => "\n",
'Columns' =>
[
'Subnet',
'Netmask',
'Gateway',
],
'ColProps' =>
{
'Subnet' => { 'MaxWidth' => 17 },
'Netmask' => { 'MaxWidth' => 17 },
})
# Populate Route Tables
Rex::Socket::SwitchBoard.each { |route|
if (route.comm.kind_of?(Msf::Session))
gw = "Session #{route.comm.sid}"
else
gw = route.comm.name.split(/::/)[-1]
end
tbl_ipv4 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv4?(route.netmask)
tbl_ipv6 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv6?(route.netmask)
}
# Print Route Tables
print(tbl_ipv4.to_s) if tbl_ipv4.rows.length > 0
print(tbl_ipv6.to_s) if tbl_ipv6.rows.length > 0
if (tbl_ipv4.rows.length + tbl_ipv6.rows.length) < 1
print_status("There are currently no routes defined.")
elsif (tbl_ipv4.rows.length < 1) && (tbl_ipv6.rows.length > 0)
print_status("There are currently no IPv4 routes defined.")
elsif (tbl_ipv4.rows.length > 0) && (tbl_ipv6.rows.length < 1)
print_status("There are currently no IPv6 routes defined.")
end
end
# Validation check on an IPv4 address
#
# Yet another IP validator. I'm sure there's some Rex
# function that can just do this.
#
# @return [string class] IPv4 subnet
def check_ip(ip=nil)
return false if(ip.nil? || ip.strip.empty?)
begin
@ -138,11 +192,17 @@ class MetasploitModule < Msf::Post
end
end
# Converts a CIDR value to a netmask
#
# @return [string class] IPv4 netmask
def cidr_to_netmask(cidr)
int = cidr.gsub(/\x2f/,"").to_i
Rex::Socket.addr_ctoa(int)
end
# Validates the user input 'NETMASK'
#
# @return [string class] IPv4 netmask
def netmask
case datastore['NETMASK']
when /^\x2f[0-9]{1,2}/
@ -154,16 +214,52 @@ class MetasploitModule < Msf::Post
end
end
# Adds a route to the framework instance
def add_route(opts={})
subnet = opts[:subnet]
Rex::Socket::SwitchBoard.add_route(subnet, netmask, session)
# This function adds a route to the framework routing table
#
# @subnet [string class] subnet to add
# @netmask [string class] netmask
# @origin [string class] where route is coming from. Nill for none.
#
# @return [true] If added
# @return [false] If not
def add_route(subnet, netmask, origin=nil)
if origin
origin = " from #{origin}"
else
origin = ""
end
# Removes a route to the framework instance
def delete_route(opts={})
subnet = opts[:subnet]
Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session)
begin
if Rex::Socket::SwitchBoard.add_route(subnet, netmask, session)
print_good("Route added to subnet #{subnet}/#{netmask}#{origin}.")
return true
else
print_error("Could not add route to subnet #{subnet}/#{netmask}#{origin}.")
return false
end
rescue ::Rex::Post::Meterpreter::RequestError => re
print_error("Could not add route to subnet #{subnet}/#{netmask}#{origin}.")
print_error("#{re.class} #{re.message}\n#{re.backtrace * "\n"}")
return false
end
end
# This function removes a route to the framework routing table
#
# @subnet [string class] subnet to add
# @netmask [string class] netmask
# @origin [string class] where route is coming from.
#
# @return [true] If removed
# @return [false] If not
def delete_route(subnet, netmask)
begin
Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session)
rescue ::Rex::Post::Meterpreter::RequestError => re
print_error("Could not remove route to subnet #{subnet}/#{netmask}")
print_error("#{re.class} #{re.message}\n#{re.backtrace * "\n"}")
return false
end
end
# This function will exclude loopback, multicast, and default routes
@ -190,26 +286,23 @@ class MetasploitModule < Msf::Post
#
# @return [void] A useful return value is not expected here
def autoadd_routes
switch_board = Rex::Socket::SwitchBoard.instance
return unless route_compatible?
print_status("Searching for subnets to autoroute.")
found = false
session.net.config.each_route do | route |
next unless is_routable?(route.subnet, route.netmask)
begin
session.net.config.each_route do | route |
next unless (Rex::Socket.is_ipv4?(route.subnet) && Rex::Socket.is_ipv4?(route.netmask)) # Pick out the IPv4 addresses
subnet = get_subnet(route.subnet, route.netmask) # Make sure that the subnet is actually a subnet and not an IP address. Android phones like to send over their IP.
next unless is_routable?(subnet, route.netmask)
if !switch_board.route_exists?(route.subnet, route.netmask)
begin
if Rex::Socket::SwitchBoard.add_route(route.subnet, route.netmask, session)
print_good("Route added to subnet #{route.subnet}/#{route.netmask} from host's routing table.")
found = true
else
print_error("Could not add route to subnet #{route.subnet}/#{route.netmask} from host's routing table.")
end
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("Could not add route to subnet #{route.subnet}/(#{route.netmask}) from host's routing table.")
print_error(error.to_s)
if !Rex::Socket::SwitchBoard.route_exists?(subnet, route.netmask)
found = true if add_route(subnet, route.netmask, "host's routing table")
end
end
rescue ::Rex::Post::Meterpreter::RequestError => re
print_status("Unable to get routes from session, trying interface list.")
end
if !autoadd_interface_routes && !found # Check interface list for more possible routes
@ -223,37 +316,32 @@ class MetasploitModule < Msf::Post
# @return [true] A route from the interface list was added
# @return [false] No additional routes were added
def autoadd_interface_routes
switch_board = Rex::Socket::SwitchBoard.instance
return unless interface_compatible?
found = false
session.net.config.each_interface do | interface | # Step through each of the network interfaces
begin
session.net.config.each_interface do | interface | # Step through each of the network interfaces
(0..(interface.addrs.size - 1)).each do | index | # Step through the addresses for the interface
(0..(interface.addrs.size - 1)).each do | index | # Step through the addresses for the interface
ip_addr = interface.addrs[index]
netmask = interface.netmasks[index]
ip_addr = interface.addrs[index]
netmask = interface.netmasks[index]
next unless ip_addr =~ /\./ # Pick out the IPv4 addresses
next unless is_routable?(ip_addr, netmask)
next unless (Rex::Socket.is_ipv4?(ip_addr) && Rex::Socket.is_ipv4?(netmask)) # Pick out the IPv4 addresses
next unless is_routable?(ip_addr, netmask)
subnet = get_subnet(ip_addr, netmask)
subnet = get_subnet(ip_addr, netmask)
if subnet
if !switch_board.route_exists?(subnet, netmask)
begin
if Rex::Socket::SwitchBoard.add_route(subnet, netmask, session)
print_good("Route added to subnet #{subnet}/#{netmask} from #{interface.mac_name}.")
found = true
else
print_error("Could not add route to subnet #{subnet}/#{netmask} from #{interface.mac_name}")
end
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("Could not add route to subnet #{subnet}/(#{netmask}) from #{interface.mac_name}")
print_error(error.to_s)
if subnet
if !Rex::Socket::SwitchBoard.route_exists?(subnet, netmask)
found = true if add_route(subnet, netmask, interface.mac_name)
end
end
end
end
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("Unable to get interface information from session.")
end
return found
end
@ -321,20 +409,48 @@ class MetasploitModule < Msf::Post
print_status("Attempting to add a default route.")
if !switch_board.route_exists?(subnet, mask)
begin
if Rex::Socket::SwitchBoard.add_route(subnet, mask, session)
print_good("Route added to subnet #{subnet}/#{mask}")
else
print_error("Could not add route to subnet #{subnet}/#{mask}")
end
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("Could not add route to subnet #{subnet}/(#{mask})")
print_error(error.to_s)
end
add_route(subnet, mask)
end
end
# Checks to see if the session is ready.
#
# Some Meterpreter types, like python, can take a few seconds to
# become fully established. This gracefully exits if the session
# is not ready yet.
#
# @return [true class] Session is good
# @return [false class] Session is not
def session_good?
if !session.info
print_error("Session is not yet fully established. Try again in a bit.")
return false
end
return true
end
# Checks to see if the session has routing capabilities
#
# @return [true class] Session has routing capabilities
# @return [false class] Session does not
def route_compatible?
session.respond_to?(:net) &&
session.net.config.respond_to?(:each_route)
end
# Checks to see if the session has capabilities of accessing network interfaces
#
# @return [true class] Session has ability to access network interfaces
# @return [false class] Session does not
def interface_compatible?
session.respond_to?(:net) &&
session.net.config.respond_to?(:each_interface)
end
# Validates the command options
#
# @return [true class] Everything is good
# @return [false class] Not so much
def validate_cmd(subnet=nil,netmask=nil)
if subnet.nil?
print_error "Missing subnet option"
@ -355,6 +471,6 @@ class MetasploitModule < Msf::Post
print_error "Netmask invalid"
return false
end
true
return true
end
end

View File

@ -28,6 +28,20 @@ remove_all_routes = false
"-D" => [false, "Delete all routes (does not require a subnet)"]
)
# Defines usage
def usage()
print_status "Usage: run autoroute [-r] -s subnet -n netmask"
print_status "Examples:"
print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0"
print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0"
print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay"
print_status " run autoroute -p # Print active routing table"
print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route"
print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes"
print_error "Deprecation warning: This script has been replaced by the post/multi/manage/autoroute module"
end
@@exec_opts.parse(args) { |opt, idx, val|
v = val.to_s.strip
case opt
@ -134,20 +148,6 @@ def delete_route(opts={})
Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session)
end
# Defines usage
def usage()
print_status "Usage: run autoroute [-r] -s subnet -n netmask"
print_status "Examples:"
print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0"
print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0"
print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay"
print_status " run autoroute -p # Print active routing table"
print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route"
print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes"
print_error "Deprecation warning: This script has been replaced by the post/windows/manage/autoroute module"
end
# Validates the command options
def validate_cmd(subnet=nil,netmask=nil)
if subnet.nil?