# $Id: autoroute.rb 9795 2010-07-12 20:04:09Z todb $

#
# Meterpreter script for setting up a route from within a
# Meterpreter session, without having to background the
# current session.

# Default options
session = client
subnet = nil
netmask = "255.255.255.0"
print_only = false
remove_route = false
remove_all_routes = false

# Options parsing
@@exec_opts = Rex::Parser::Arguments.new(
	"-h" => [false, "Help and usage"],
	"-s" => [true, "Subnet (IPv4, for example, 10.10.10.0)"],
	"-n" => [true, "Netmask (IPv4, for example, 255.255.255.0"],
	"-p" => [false, "Print active routing table. All other options are ignored"],
	"-d" => [false, "Delete the named route instead of adding it"],
	"-D" => [false, "Delete all routes (does not require a subnet)"]
)

@@exec_opts.parse(args) { |opt, idx, val|
	v = val.to_s.strip
	case opt
	when "-h"
		usage
		raise Rex::Script::Completed
	when "-s"
		if v =~ /[0-9\x2e]+\x2f[0-9]{1,2}/
			subnet,cidr = v.split("\x2f")
			netmask = Rex::Socket.addr_ctoa(cidr.to_i)
		else
			subnet = v
		end
	when "-n"
		if (0..32) === v.to_i
			netmask = Rex::Socket.addr_ctoa(v.to_i)
		else
			netmask = v
		end
	when "-p"
		print_only = true
	when "-d"
		remove_route = true
	when "-D"
		remove_all_routes = true
	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
	raise Rex::Script::Completed
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 tbl.to_s
	else
		print_status "No routes have been added yet"
	end
	raise Rex::Script::Completed
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

# Adds a route to the framework instance
def add_route(opts={})
	subnet = opts[:subnet]
	netmask = opts[:netmask] || "255.255.255.0" # Default class C
	Rex::Socket::SwitchBoard.add_route(subnet, netmask, session)
end

# Removes a route to the framework instance
def delete_route(opts={})
	subnet = opts[:subnet]
	netmask = opts[:netmask] || "255.255.255.0" # Default class C
	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"
end

# Validates the command options
def validate_cmd(subnet=nil,netmask=nil)
	if subnet.nil?
		print_error "Missing -s (subnet) option"
		return false
	end

	unless(check_ip(subnet))
		print_error "Subnet invalid (must be IPv4)"
		usage
		return false
	end

	if(netmask and !(Rex::Socket.addr_atoc(netmask)))
		print_error "Netmask invalid (must define contiguous IP addressing)"
		usage
		return false
	end

	if(netmask and !check_ip(netmask))
		print_error "Netmask invalid"
		return usage
	end
	true
end

if print_only
	print_routes()
	raise Rex::Script::Completed
end

if remove_all_routes
	delete_all_routes()
	raise Rex::Script::Completed
end

raise Rex::Script::Completed unless validate_cmd(subnet,netmask)

if remove_route
	print_status("Deleting route to %s/%s..." % [subnet,netmask])
	route_result = delete_route(:subnet => subnet, :netmask => netmask)
else
	print_status("Adding a route to %s/%s..." % [subnet,netmask])
	route_result = add_route(:subnet => subnet, :netmask => netmask)
end

if route_result
	print_good "%s route to %s/%s via %s" % [
		(remove_route ? "Deleted" : "Added"),
		subnet,netmask,client.sock.peerhost
	]
else
	print_error "Could not %s route" % [(remove_route ? "delete" : "add")]
end

if Rex::Socket::SwitchBoard.routes.size > 0
	print_status "Use the -p option to list all active routes"
end

