PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb

https://github.com/Jonono2/metasploit-framework
Ruby | 448 lines | 310 code | 71 blank | 67 comment | 25 complexity | 30c01b836cec9ec51f0c5d75d49fdd70 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, GPL-3.0, LGPL-2.1, GPL-2.0
  1. # -*- coding: binary -*-
  2. require 'rex/post/meterpreter'
  3. require 'rex/service_manager'
  4. module Rex
  5. module Post
  6. module Meterpreter
  7. module Ui
  8. ###
  9. #
  10. # The networking portion of the standard API extension.
  11. #
  12. ###
  13. class Console::CommandDispatcher::Stdapi::Net
  14. Klass = Console::CommandDispatcher::Stdapi::Net
  15. include Console::CommandDispatcher
  16. #
  17. # This module is used to extend the meterpreter session
  18. # so that local port forwards can be tracked and cleaned
  19. # up when the meterpreter session goes away
  20. #
  21. module PortForwardTracker
  22. def cleanup
  23. super
  24. if pfservice
  25. pfservice.deref
  26. end
  27. end
  28. attr_accessor :pfservice
  29. end
  30. #
  31. # Options for the route command.
  32. #
  33. @@route_opts = Rex::Parser::Arguments.new(
  34. "-h" => [ false, "Help banner." ])
  35. #
  36. # Options for the portfwd command.
  37. #
  38. @@portfwd_opts = Rex::Parser::Arguments.new(
  39. "-h" => [ false, "Help banner." ],
  40. "-l" => [ true, "The local port to listen on." ],
  41. "-r" => [ true, "The remote host to connect to." ],
  42. "-p" => [ true, "The remote port to connect to." ],
  43. "-L" => [ true, "The local host to listen on (optional)." ])
  44. #
  45. # List of supported commands.
  46. #
  47. def commands
  48. all = {
  49. "ipconfig" => "Display interfaces",
  50. "ifconfig" => "Display interfaces",
  51. "route" => "View and modify the routing table",
  52. "portfwd" => "Forward a local port to a remote service",
  53. "arp" => "Display the host ARP cache",
  54. "netstat" => "Display the network connections",
  55. "getproxy" => "Display the current proxy configuration",
  56. }
  57. reqs = {
  58. "ipconfig" => [ "stdapi_net_config_get_interfaces" ],
  59. "ifconfig" => [ "stdapi_net_config_get_interfaces" ],
  60. "route" => [
  61. # Also uses these, but we don't want to be unable to list them
  62. # just because we can't alter them.
  63. #"stdapi_net_config_add_route",
  64. #"stdapi_net_config_remove_route",
  65. "stdapi_net_config_get_routes"
  66. ],
  67. # Only creates tcp channels, which is something whose availability
  68. # we can't check directly at the moment.
  69. "portfwd" => [ ],
  70. "arp" => [ "stdapi_net_config_get_arp_table" ],
  71. "netstat" => [ "stdapi_net_config_get_netstat" ],
  72. "getproxy" => [ "stdapi_net_config_get_proxy" ],
  73. }
  74. all.delete_if do |cmd, desc|
  75. del = false
  76. reqs[cmd].each do |req|
  77. next if client.commands.include? req
  78. del = true
  79. break
  80. end
  81. del
  82. end
  83. all
  84. end
  85. #
  86. # Name for this dispatcher.
  87. #
  88. def name
  89. "Stdapi: Networking"
  90. end
  91. #
  92. # Displays network connections of the remote machine.
  93. #
  94. def cmd_netstat(*args)
  95. connection_table = client.net.config.netstat
  96. tbl = Rex::Ui::Text::Table.new(
  97. 'Header' => "Connection list",
  98. 'Indent' => 4,
  99. 'Columns' =>
  100. [
  101. "Proto",
  102. "Local address",
  103. "Remote address",
  104. "State",
  105. "User",
  106. "Inode",
  107. "PID/Program name"
  108. ])
  109. connection_table.each { |connection|
  110. tbl << [ connection.protocol, connection.local_addr_str, connection.remote_addr_str,
  111. connection.state, connection.uid, connection.inode, connection.pid_name]
  112. }
  113. if tbl.rows.length > 0
  114. print("\n" + tbl.to_s + "\n")
  115. else
  116. print_line("Connection list is empty.")
  117. end
  118. end
  119. #
  120. # Displays ARP cache of the remote machine.
  121. #
  122. def cmd_arp(*args)
  123. arp_table = client.net.config.arp_table
  124. tbl = Rex::Ui::Text::Table.new(
  125. 'Header' => "ARP cache",
  126. 'Indent' => 4,
  127. 'Columns' =>
  128. [
  129. "IP address",
  130. "MAC address",
  131. "Interface"
  132. ])
  133. arp_table.each { |arp|
  134. tbl << [ arp.ip_addr, arp.mac_addr, arp.interface ]
  135. }
  136. if tbl.rows.length > 0
  137. print("\n" + tbl.to_s + "\n")
  138. else
  139. print_line("ARP cache is empty.")
  140. end
  141. end
  142. #
  143. # Displays interfaces on the remote machine.
  144. #
  145. def cmd_ipconfig(*args)
  146. ifaces = client.net.config.interfaces
  147. if (ifaces.length == 0)
  148. print_line("No interfaces were found.")
  149. else
  150. ifaces.sort{|a,b| a.index <=> b.index}.each do |iface|
  151. print("\n" + iface.pretty + "\n")
  152. end
  153. end
  154. end
  155. alias :cmd_ifconfig :cmd_ipconfig
  156. #
  157. # Displays or modifies the routing table on the remote machine.
  158. #
  159. def cmd_route(*args)
  160. # Default to list
  161. if (args.length == 0)
  162. args.unshift("list")
  163. end
  164. # Check to see if they specified -h
  165. @@route_opts.parse(args) { |opt, idx, val|
  166. case opt
  167. when "-h"
  168. print(
  169. "Usage: route [-h] command [args]\n\n" +
  170. "Display or modify the routing table on the remote machine.\n\n" +
  171. "Supported commands:\n\n" +
  172. " add [subnet] [netmask] [gateway]\n" +
  173. " delete [subnet] [netmask] [gateway]\n" +
  174. " list\n\n")
  175. return true
  176. end
  177. }
  178. cmd = args.shift
  179. # Process the commands
  180. case cmd
  181. when "list"
  182. routes = client.net.config.routes
  183. # IPv4
  184. tbl = Rex::Ui::Text::Table.new(
  185. 'Header' => "IPv4 network routes",
  186. 'Indent' => 4,
  187. 'Columns' =>
  188. [
  189. "Subnet",
  190. "Netmask",
  191. "Gateway",
  192. "Metric",
  193. "Interface"
  194. ])
  195. routes.select {|route|
  196. Rex::Socket.is_ipv4?(route.netmask)
  197. }.each { |route|
  198. tbl << [ route.subnet, route.netmask, route.gateway, route.metric, route.interface ]
  199. }
  200. if tbl.rows.length > 0
  201. print("\n" + tbl.to_s + "\n")
  202. else
  203. print_line("No IPv4 routes were found.")
  204. end
  205. # IPv6
  206. tbl = Rex::Ui::Text::Table.new(
  207. 'Header' => "IPv6 network routes",
  208. 'Indent' => 4,
  209. 'Columns' =>
  210. [
  211. "Subnet",
  212. "Netmask",
  213. "Gateway",
  214. "Metric",
  215. "Interface"
  216. ])
  217. routes.select {|route|
  218. Rex::Socket.is_ipv6?(route.netmask)
  219. }.each { |route|
  220. tbl << [ route.subnet, route.netmask, route.gateway, route.metric, route.interface ]
  221. }
  222. if tbl.rows.length > 0
  223. print("\n" + tbl.to_s + "\n")
  224. else
  225. print_line("No IPv6 routes were found.")
  226. end
  227. when "add"
  228. # Satisfy check to see that formatting is correct
  229. unless Rex::Socket::RangeWalker.new(args[0]).length == 1
  230. print_error "Invalid IP Address"
  231. return false
  232. end
  233. unless Rex::Socket::RangeWalker.new(args[1]).length == 1
  234. print_error "Invalid Subnet mask"
  235. return false
  236. end
  237. print_line("Creating route #{args[0]}/#{args[1]} -> #{args[2]}")
  238. client.net.config.add_route(*args)
  239. when "delete"
  240. # Satisfy check to see that formatting is correct
  241. unless Rex::Socket::RangeWalker.new(args[0]).length == 1
  242. print_error "Invalid IP Address"
  243. return false
  244. end
  245. unless Rex::Socket::RangeWalker.new(args[1]).length == 1
  246. print_error "Invalid Subnet mask"
  247. return false
  248. end
  249. print_line("Deleting route #{args[0]}/#{args[1]} -> #{args[2]}")
  250. client.net.config.remove_route(*args)
  251. else
  252. print_error("Unsupported command: #{cmd}")
  253. end
  254. end
  255. #
  256. # Starts and stops local port forwards to remote hosts on the target
  257. # network. This provides an elementary pivoting interface.
  258. #
  259. def cmd_portfwd(*args)
  260. args.unshift("list") if args.empty?
  261. # For clarity's sake.
  262. lport = nil
  263. lhost = nil
  264. rport = nil
  265. rhost = nil
  266. # Parse the options
  267. @@portfwd_opts.parse(args) { |opt, idx, val|
  268. case opt
  269. when "-h"
  270. cmd_portfwd_help
  271. return true
  272. when "-l"
  273. lport = val.to_i
  274. when "-L"
  275. lhost = val
  276. when "-p"
  277. rport = val.to_i
  278. when "-r"
  279. rhost = val
  280. end
  281. }
  282. # If we haven't extended the session, then do it now since we'll
  283. # need to track port forwards
  284. if client.kind_of?(PortForwardTracker) == false
  285. client.extend(PortForwardTracker)
  286. client.pfservice = Rex::ServiceManager.start(Rex::Services::LocalRelay)
  287. end
  288. # Build a local port forward in association with the channel
  289. service = client.pfservice
  290. # Process the command
  291. case args.shift
  292. when "list"
  293. cnt = 0
  294. # Enumerate each TCP relay
  295. service.each_tcp_relay { |lhost, lport, rhost, rport, opts|
  296. next if (opts['MeterpreterRelay'] == nil)
  297. print_line("#{cnt}: #{lhost}:#{lport} -> #{rhost}:#{rport}")
  298. cnt += 1
  299. }
  300. print_line
  301. print_line("#{cnt} total local port forwards.")
  302. when "add"
  303. # Validate parameters
  304. if (!lport or !rhost or !rport)
  305. print_error("You must supply a local port, remote host, and remote port.")
  306. return
  307. end
  308. # Start the local TCP relay in association with this stream
  309. service.start_tcp_relay(lport,
  310. 'LocalHost' => lhost,
  311. 'PeerHost' => rhost,
  312. 'PeerPort' => rport,
  313. 'MeterpreterRelay' => true,
  314. 'OnLocalConnection' => Proc.new { |relay, lfd|
  315. create_tcp_channel(relay)
  316. })
  317. print_status("Local TCP relay created: #{lhost || '0.0.0.0'}:#{lport} <-> #{rhost}:#{rport}")
  318. # Delete local port forwards
  319. when "delete"
  320. # No local port, no love.
  321. if (!lport)
  322. print_error("You must supply a local port.")
  323. return
  324. end
  325. # Stop the service
  326. if (service.stop_tcp_relay(lport, lhost))
  327. print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
  328. else
  329. print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
  330. end
  331. when "flush"
  332. counter = 0
  333. service.each_tcp_relay do |lhost, lport, rhost, rport, opts|
  334. next if (opts['MeterpreterRelay'] == nil)
  335. if (service.stop_tcp_relay(lport, lhost))
  336. print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
  337. else
  338. print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
  339. next
  340. end
  341. counter += 1
  342. end
  343. print_status("Successfully flushed #{counter} rules")
  344. else
  345. cmd_portfwd_help
  346. end
  347. end
  348. def cmd_portfwd_help
  349. print_line "Usage: portfwd [-h] [add | delete | list | flush] [args]"
  350. print_line
  351. print @@portfwd_opts.usage
  352. end
  353. def cmd_getproxy
  354. p = client.net.config.get_proxy_config()
  355. print_line( "Auto-detect : #{p[:autodetect] ? "Yes" : "No"}" )
  356. print_line( "Auto config URL : #{p[:autoconfigurl]}" )
  357. print_line( "Proxy URL : #{p[:proxy]}" )
  358. print_line( "Proxy Bypass : #{p[:proxybypass]}" )
  359. end
  360. protected
  361. #
  362. # Creates a TCP channel using the supplied relay context.
  363. #
  364. def create_tcp_channel(relay)
  365. client.net.socket.create(
  366. Rex::Socket::Parameters.new(
  367. 'PeerHost' => relay.opts['PeerHost'],
  368. 'PeerPort' => relay.opts['PeerPort'],
  369. 'Proto' => 'tcp'
  370. )
  371. )
  372. end
  373. end
  374. end
  375. end
  376. end
  377. end