Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/multi/manage/autoroute.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post67def initialize(info = {})8super(9update_info(10info,11'Name' => 'Multi Manage Network Route via Meterpreter Session',12'Description' => %q{13This module manages session routing via an existing14Meterpreter session. It enables other modules to 'pivot' through a15compromised host when connecting to the named NETWORK and SUBMASK.16Autoadd will search a session for valid subnets from the routing table17and interface list then add routes to them. Default will add a default18route so that all TCP/IP traffic not specified in the MSF routing table19will be routed through the session when pivoting. See documentation for more20'info -d' and click 'Knowledge Base'21},22'License' => MSF_LICENSE,23'Author' => [24'todb',25'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>'26],27'SessionTypes' => [ 'meterpreter'],28'Compat' => {29'Meterpreter' => {30'Commands' => %w[31stdapi_net_config_get_interfaces32stdapi_net_config_get_routes33]34}35}36)37)3839register_options(40[41OptString.new('SUBNET', [false, 'Subnet (IPv4, for example, 10.10.10.0)', nil]),42OptString.new('NETMASK', [false, 'Netmask (IPv4 as "255.255.255.0" or CIDR as "/24"', '255.255.255.0']),43OptEnum.new('CMD', [true, 'Specify the autoroute command', 'autoadd', ['add', 'autoadd', 'print', 'delete', 'default']])44]45)46end4748# Get the CMD string vs ACTION49#50# Backwards compatability: This was changed because the option name of "ACTION"51# is special for some things, and indicates the :action attribute, not a datastore option.52# However, this is a semi-popular module, though, so I'd prefer not to break people's53# RC scripts that set ACTION. Note that ACTION is preferred over CMD.54#55# TODO: The better solution is to use 'Action' and 'DefaultAction' info elements,56# but there are some squirelly problems right now with rendering these for post modules.57#58# @return [string class] cmd string59def route_cmd60if datastore['ACTION'].to_s.empty?61datastore['CMD'].to_s.downcase.to_sym62else63wlog("Warning, deprecated use of 'ACTION' datastore option for #{fullname}'. Use 'CMD' instead.")64datastore['ACTION'].to_s.downcase.to_sym65end66end6768# Run Method for when run command is issued69#70# @return [void] A useful return value is not expected here71def run72return unless session_good?7374print_status("Running module against #{sysinfo['Computer']}")7576case route_cmd77when :print78print_routes79when :add80if validate_cmd(datastore['SUBNET'], netmask)81print_status('Adding a route to %s/%s...' % [datastore['SUBNET'], netmask])82add_route(datastore['SUBNET'], netmask)83end84when :autoadd85autoadd_routes86when :default87add_default88when :delete89if datastore['SUBNET']90print_status('Deleting route to %s/%s...' % [datastore['SUBNET'], netmask])91delete_route(datastore['SUBNET'], netmask)92else93delete_all_routes94end95end96end9798# Delete all routes from framework routing table.99#100# @return [void] A useful return value is not expected here101def delete_all_routes102if !Rex::Socket::SwitchBoard.routes.empty?103print_status("Deleting all routes associated with session: #{session.sid}.")104loop do105count = 0106Rex::Socket::SwitchBoard.each do |route|107if route.comm == session108print_status("Deleting: #{route.subnet}/#{route.netmask}")109delete_route(route.subnet, route.netmask)110end111end112Rex::Socket::SwitchBoard.each do |route|113count += 1 if route.comm == session114end115break if count == 0116end117print_status('Deleted all routes')118else119print_status('No routes associated with this session to delete.')120end121end122123# Print all of the active routes defined on the framework124#125# Identical functionality to command_dispatcher/core.rb, and126# nearly identical code127#128# @return [void] A useful return value is not expected here129def print_routes130# IPv4 Table131tbl_ipv4 = Msf::Ui::Console::Table.new(132Msf::Ui::Console::Table::Style::Default,133'Header' => 'IPv4 Active Routing Table',134'Prefix' => "\n",135'Postfix' => "\n",136'Columns' =>137[138'Subnet',139'Netmask',140'Gateway',141],142'ColProps' =>143{144'Subnet' => { 'Width' => 17 },145'Netmask' => { 'Width' => 17 }146}147)148149# IPv6 Table150tbl_ipv6 = Msf::Ui::Console::Table.new(151Msf::Ui::Console::Table::Style::Default,152'Header' => 'IPv6 Active Routing Table',153'Prefix' => "\n",154'Postfix' => "\n",155'Columns' =>156[157'Subnet',158'Netmask',159'Gateway',160],161'ColProps' =>162{163'Subnet' => { 'Width' => 17 },164'Netmask' => { 'Width' => 17 }165}166)167168# Populate Route Tables169Rex::Socket::SwitchBoard.each do |route|170if route.comm.is_a?(Msf::Session)171gw = "Session #{route.comm.sid}"172else173gw = route.comm.name.split(/::/)[-1]174end175176tbl_ipv4 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv4?(route.netmask)177tbl_ipv6 << [ route.subnet, route.netmask, gw ] if Rex::Socket.is_ipv6?(route.netmask)178end179180# Print Route Tables181print_status(tbl_ipv4.to_s) if !tbl_ipv4.rows.empty?182print_status(tbl_ipv6.to_s) if !tbl_ipv6.rows.empty?183if (tbl_ipv4.rows.length + tbl_ipv6.rows.length) < 1184print_status('There are currently no routes defined.')185elsif tbl_ipv4.rows.empty? && !tbl_ipv6.rows.empty?186print_status('There are currently no IPv4 routes defined.')187elsif !tbl_ipv4.rows.empty? && tbl_ipv6.rows.empty?188print_status('There are currently no IPv6 routes defined.')189end190end191192# Validation check on an IPv4 address193#194# Yet another IP validator. I'm sure there's some Rex195# function that can just do this.196#197# @return [string class] IPv4 subnet198def check_ip(ip = nil)199return false if (ip.nil? || ip.strip.empty?)200201begin202rw = Rex::Socket::RangeWalker.new(ip.strip)203(rw.valid? && rw.length == 1) ? true : false204rescue StandardError205false206end207end208209# Converts a CIDR value to a netmask210#211# @return [string class] IPv4 netmask212def cidr_to_netmask(cidr)213int = cidr.gsub(/\x2f/, '').to_i214Rex::Socket.addr_ctoa(int)215end216217# Validates the user input 'NETMASK'218#219# @return [string class] IPv4 netmask220def netmask221case datastore['NETMASK']222when /^\x2f[0-9]{1,2}/223cidr_to_netmask(datastore['NETMASK'])224when /^[0-9]{1,3}\.[0-9]/ # Close enough, if it's wrong it'll fail out later.225datastore['NETMASK']226else227'255.255.255.0'228end229end230231# This function adds a route to the framework routing table232#233# @subnet [string class] subnet to add234# @netmask [string class] netmask235# @origin [string class] where route is coming from. Nill for none.236#237# @return [true] If added238# @return [false] If not239def add_route(subnet, netmask, origin = nil)240if origin241origin = " from #{origin}"242else243origin = ''244end245246begin247if Rex::Socket::SwitchBoard.add_route(subnet, netmask, session)248print_good("Route added to subnet #{subnet}/#{netmask}#{origin}.")249return true250else251print_error("Could not add route to subnet #{subnet}/#{netmask}#{origin}.")252return false253end254rescue ::Rex::Post::Meterpreter::RequestError => e255print_error("Could not add route to subnet #{subnet}/#{netmask}#{origin}.")256print_error("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")257return false258end259end260261# This function removes a route to the framework routing table262#263# @subnet [string class] subnet to add264# @netmask [string class] netmask265# @origin [string class] where route is coming from.266#267# @return [true] If removed268# @return [false] If not269def delete_route(subnet, netmask)270Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session)271rescue ::Rex::Post::Meterpreter::RequestError => e272print_error("Could not remove route to subnet #{subnet}/#{netmask}")273print_error("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")274return false275end276277# This function will exclude loopback, multicast, and default routes278#279# @subnet [string class] IPv4 subnet or address to check280# @netmask [string class] IPv4 netmask to check281#282# @return [true] If good to add283# @return [false] If not284def is_routable?(subnet, netmask)285if subnet =~ /^224\.|^127\./286return false287elsif subnet == '0.0.0.0'288return false289elsif netmask == '255.255.255.255'290return false291end292293return true294end295296# Search for valid subnets on the target and attempt297# add a route to each. (Operation from auto_add_route plugin.)298#299# @return [void] A useful return value is not expected here300def autoadd_routes301return unless route_compatible?302303print_status('Searching for subnets to autoroute.')304found = false305306begin307session.net.config.each_route do |route|308next unless (Rex::Socket.is_ipv4?(route.subnet) && Rex::Socket.is_ipv4?(route.netmask)) # Pick out the IPv4 addresses309310subnet = 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.311next unless is_routable?(subnet, route.netmask)312313if !Rex::Socket::SwitchBoard.route_exists?(subnet, route.netmask) && add_route(subnet, route.netmask, "host's routing table")314found = true315end316end317rescue ::Rex::Post::Meterpreter::RequestError => e318print_status('Unable to get routes from session, trying interface list.')319end320321if !autoadd_interface_routes && !found # Check interface list for more possible routes322print_status('Did not find any new subnets to add.')323end324end325326# Look at network interfaces as options for additional routes.327# If the routes are not already included they will be added.328#329# @return [true] A route from the interface list was added330# @return [false] No additional routes were added331def autoadd_interface_routes332return unless interface_compatible?333334found = false335336begin337session.net.config.each_interface do |interface| # Step through each of the network interfaces338(0..(interface.addrs.size - 1)).each do |index| # Step through the addresses for the interface339ip_addr = interface.addrs[index]340netmask = interface.netmasks[index]341342next unless (Rex::Socket.is_ipv4?(ip_addr) && Rex::Socket.is_ipv4?(netmask)) # Pick out the IPv4 addresses343next unless is_routable?(ip_addr, netmask)344345subnet = get_subnet(ip_addr, netmask)346347if subnet && !Rex::Socket::SwitchBoard.route_exists?(subnet, netmask) && add_route(subnet, netmask, interface.mac_name)348found = true349end350end351end352rescue ::Rex::Post::Meterpreter::RequestError => e353print_error('Unable to get interface information from session.')354end355return found356end357358# Take an IP address and a netmask and return the appropreate subnet "Network"359#360# @ip_addr [string class] Input IPv4 Address361# @netmask [string class] Input IPv4 Netmask362#363# @return [string class] The subnet related to the IP address and netmask364# @return [nil class] Something is out of range365def get_subnet(ip_addr, netmask)366return nil if !validate_cmd(ip_addr, netmask) # make sure IP and netmask are valid367368nets = ip_addr.split('.')369masks = netmask.split('.')370output = ''3713724.times do |index|373octet = get_subnet_octet(int_or_nil(nets[index]), int_or_nil(masks[index]))374return nil if !octet375376output << octet.to_s377output << '.' if index < 3378end379return output380end381382# Input an octet of an IPv4 address and the cooresponding octet of the383# IPv4 netmask then return the appropreate subnet octet.384#385# @net [integer class] IPv4 address octet386# @mask [integer class] Ipv4 netmask octet387#388# @return [integer class] Octet of the subnet389# @return [nil class] If an input is nil390def get_subnet_octet(net, mask)391return nil if !net || !mask392393subnet_range = 256 - mask # This is the address space of the subnet octet394395multi = net / subnet_range # Integer division to get the multiplier needed to determine subnet octet396397return(subnet_range * multi) # Multiply to get subnet octet398end399400# Take a string of numbers and converts it to an integer.401#402# @string [string class] Input string, needs to be all numbers (0..9)403#404# @return [integer class] Integer representation of the number string405# @return [nil class] string contains non-numbers, cannot convert406def int_or_nil(string)407num = string.to_i408num if num.to_s == string409end410411# Add a default route to the routing table412#413# @return [void] A useful return value is not expected here414def add_default415subnet = '0.0.0.0'416mask = '0.0.0.0'417418switch_board = Rex::Socket::SwitchBoard.instance419print_status('Attempting to add a default route.')420421if !switch_board.route_exists?(subnet, mask)422add_route(subnet, mask)423end424end425426# Checks to see if the session is ready.427#428# Some Meterpreter types, like python, can take a few seconds to429# become fully established. This gracefully exits if the session430# is not ready yet.431#432# @return [true class] Session is good433# @return [false class] Session is not434def session_good?435if !session.info436print_error('Session is not yet fully established. Try again in a bit.')437return false438end439return true440end441442# Checks to see if the session has routing capabilities443#444# @return [true class] Session has routing capabilities445# @return [false class] Session does not446def route_compatible?447session.respond_to?(:net) &&448session.net.config.respond_to?(:each_route)449end450451# Checks to see if the session has capabilities of accessing network interfaces452#453# @return [true class] Session has ability to access network interfaces454# @return [false class] Session does not455def interface_compatible?456session.respond_to?(:net) &&457session.net.config.respond_to?(:each_interface)458end459460# Validates the command options461#462# @return [true class] Everything is good463# @return [false class] Not so much464def validate_cmd(subnet = nil, netmask = nil)465if subnet.nil?466print_error 'Missing subnet option'467return false468end469470unless check_ip(subnet)471print_error 'Subnet invalid (must be IPv4)'472return false473end474475if (netmask && !Rex::Socket.addr_atoc(netmask))476print_error 'Netmask invalid (must define contiguous IP addressing)'477return false478end479480if (netmask && !check_ip(netmask))481print_error 'Netmask invalid'482return false483end484return true485end486end487488489