msfrpcd 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #!/usr/bin/env ruby
  2. # -*- coding: binary -*-
  3. #
  4. # $Id$
  5. #
  6. # This user interface listens on a port and provides clients that connect to
  7. # it with an RPC or JSON-RPC interface to the Metasploit Framework.
  8. #
  9. # $Revision$
  10. #
  11. RPC_TYPE = 'Msg'
  12. WS_TAG = 'msf-ws'
  13. WS_RPC_TAG = 'msf-json-rpc'
  14. WS_CONF = "#{WS_RPC_TAG}.ru"
  15. WS_ENV = 'production'
  16. def start_json_rpc_service(conf:, address:, port:, ssl:, ssl_key:, ssl_cert:,
  17. ssl_disable_verify:, daemonize:, log:, pid:)
  18. unless File.file?(conf)
  19. $stdout.puts "[-] No MSF JSON-RPC web service configuration found at #{conf}, not starting"
  20. return false
  21. end
  22. # check if MSF JSON-RPC web service is already started
  23. if File.file?(pid)
  24. ws_pid = Msf::Util::ServiceHelper.tail(pid)
  25. if ws_pid.nil? || !Msf::Util::ServiceHelper.process_active?(ws_pid.to_i)
  26. $stdout.puts "[-] MSF JSON-RPC web service PID file found, but no active process running as PID #{ws_pid}"
  27. $stdout.puts "[*] Deleting MSF JSON-RPC web service PID file #{pid}"
  28. File.delete(pid)
  29. else
  30. $stdout.puts "[*] MSF JSON-RPC web service is already running as PID #{ws_pid}"
  31. return false
  32. end
  33. end
  34. # attempt to start MSF JSON-RPC service
  35. thin_cmd = Msf::Util::ServiceHelper.thin_cmd(conf: conf,
  36. address: address,
  37. port: port,
  38. ssl: ssl,
  39. ssl_key: ssl_key,
  40. ssl_cert: ssl_cert,
  41. ssl_disable_verify: ssl_disable_verify,
  42. env: WS_ENV,
  43. daemonize: daemonize,
  44. log: log,
  45. pid: pid,
  46. tag: WS_RPC_TAG)
  47. Msf::Util::ServiceHelper.run_cmd("#{thin_cmd} start")
  48. end
  49. def stop_json_rpc_service(conf:, address:, port:, ssl:, ssl_key:, ssl_cert:,
  50. ssl_disable_verify:, daemonize:, log:, pid:)
  51. ws_pid = Msf::Util::ServiceHelper.tail(pid)
  52. $stdout.puts ''
  53. if ws_pid.nil? || !Msf::Util::ServiceHelper.process_active?(ws_pid.to_i)
  54. $stdout.puts '[*] MSF JSON-RPC web service is no longer running'
  55. if File.file?(pid)
  56. $stdout.puts "[*] Deleting MSF JSON-RPC web service PID file #{pid}"
  57. File.delete(pid)
  58. end
  59. else
  60. $stdout.puts "[*] Stopping MSF JSON-RPC web service PID #{ws_pid}"
  61. thin_cmd = Msf::Util::ServiceHelper.thin_cmd(conf: conf,
  62. address: address,
  63. port: port,
  64. ssl: ssl,
  65. ssl_key: ssl_key,
  66. ssl_cert: ssl_cert,
  67. ssl_disable_verify: ssl_disable_verify,
  68. env: WS_ENV,
  69. daemonize: daemonize,
  70. log: log,
  71. pid: pid,
  72. tag: WS_RPC_TAG)
  73. Msf::Util::ServiceHelper.run_cmd("#{thin_cmd} stop")
  74. end
  75. end
  76. def start_rpc_service(opts, frameworkOpts, foreground)
  77. # Fork into the background if requested
  78. begin
  79. if foreground
  80. $stdout.puts "[*] #{RPC_TYPE.upcase}RPC ready at #{Time.now}."
  81. else
  82. $stderr.puts "[*] #{RPC_TYPE.upcase}RPC backgrounding at #{Time.now}..."
  83. child_pid = Process.fork()
  84. if child_pid
  85. $stderr.puts "[*] #{RPC_TYPE.upcase}RPC background PID #{child_pid}"
  86. exit(0)
  87. end
  88. end
  89. rescue ::NotImplementedError
  90. $stderr.puts "[-] Background mode is not available on this platform"
  91. end
  92. # Create an instance of the framework
  93. $framework = Msf::Simple::Framework.create(frameworkOpts)
  94. # Run the plugin instance in the foreground.
  95. begin
  96. $framework.plugins.load("#{RPC_TYPE.downcase}rpc", opts).run
  97. rescue ::Interrupt
  98. $stderr.puts "[*] Shutting down"
  99. end
  100. end
  101. if $PROGRAM_NAME == __FILE__
  102. msfbase = __FILE__
  103. while File.symlink?(msfbase)
  104. msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
  105. end
  106. $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
  107. require 'msfenv'
  108. $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
  109. require 'rex/parser/arguments'
  110. ws_ssl_key_default = File.join(Msf::Config.config_directory, "#{WS_TAG}-key.pem")
  111. ws_ssl_cert_default = File.join(Msf::Config.config_directory, "#{WS_TAG}-cert.pem")
  112. ws_log = File.join(Msf::Config.config_directory, 'logs', "#{WS_RPC_TAG}.log")
  113. ws_rpc_pid = File.join(Msf::Config.config_directory, "#{WS_RPC_TAG}.pid")
  114. ws_ssl_key = ws_ssl_key_default
  115. ws_ssl_cert = ws_ssl_cert_default
  116. ssl_enable_verify = false
  117. foreground = false
  118. json_rpc = false
  119. frameworkOpts = {}
  120. opts = {
  121. 'RunInForeground' => true,
  122. 'SSL' => true,
  123. 'ServerHost' => '0.0.0.0',
  124. 'ServerPort' => 55553,
  125. 'ServerType' => RPC_TYPE,
  126. 'TokenTimeout' => 300,
  127. }
  128. # Declare the argument parser for msfrpcd
  129. arguments = Rex::Parser::Arguments.new(
  130. "-a" => [ true, "Bind to this IP address (default: #{opts['ServerHost']})" ],
  131. "-p" => [ true, "Bind to this port (default: #{opts['ServerPort']})" ],
  132. "-U" => [ true, "Specify the username to access msfrpcd" ],
  133. "-P" => [ true, "Specify the password to access msfrpcd" ],
  134. "-u" => [ true, "URI for Web server" ],
  135. "-t" => [ true, "Token Timeout seconds (default: #{opts['TokenTimeout']})" ],
  136. "-S" => [ false, "Disable SSL on the RPC socket" ],
  137. "-f" => [ false, "Run the daemon in the foreground" ],
  138. "-n" => [ false, "Disable database" ],
  139. "-j" => [ false, "(JSON-RPC) Start JSON-RPC server" ],
  140. "-k" => [ false, "(JSON-RPC) Path to private key (default: #{ws_ssl_key_default})" ],
  141. "-c" => [ false, "(JSON-RPC) Path to certificate (default: #{ws_ssl_cert_default})" ],
  142. "-v" => [ false, "(JSON-RPC) SSL enable verify (optional) client cert requests" ],
  143. "-h" => [ false, "Help banner" ])
  144. # Parse command line arguments.
  145. arguments.parse(ARGV) { |opt, idx, val|
  146. case opt
  147. when "-a"
  148. opts['ServerHost'] = val
  149. when "-S"
  150. opts['SSL'] = false
  151. when "-p"
  152. opts['ServerPort'] = val
  153. when '-U'
  154. opts['User'] = val
  155. when '-P'
  156. opts['Pass'] = val
  157. when "-t"
  158. opts['TokenTimeout'] = val.to_i
  159. when "-f"
  160. foreground = true
  161. when "-u"
  162. opts['URI'] = val
  163. when "-n"
  164. frameworkOpts['DisableDatabase'] = true
  165. when "-j"
  166. json_rpc = true
  167. when "-k"
  168. ws_ssl_key = val
  169. when "-c"
  170. ws_ssl_cert = val
  171. when "-v"
  172. ssl_enable_verify = true
  173. when "-h"
  174. print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage)
  175. exit
  176. end
  177. }
  178. $0 = "msfrpcd"
  179. begin
  180. if json_rpc
  181. if !File.file?(ws_ssl_key_default) || !File.file?(ws_ssl_cert_default)
  182. $stdout.puts "[-] It doesn't appear msfdb has been run; please run 'msfdb init' first."
  183. abort
  184. end
  185. $stderr.puts "[*] JSON-RPC starting on #{opts['ServerHost']}:#{opts['ServerPort']} (#{opts['SSL'] ? "SSL" : "NO SSL"})..."
  186. $stderr.puts "[*] URI: /api/v1/json-rpc"
  187. $stderr.puts "[*] JSON-RPC server log: #{ws_log}" unless foreground
  188. $stderr.puts "[*] JSON-RPC server PID file: #{ws_rpc_pid}" unless foreground
  189. ws_conf_full_path = File.expand_path(File.join(File.dirname(msfbase), WS_CONF))
  190. start_json_rpc_service(conf: ws_conf_full_path,
  191. address: opts['ServerHost'],
  192. port: opts['ServerPort'],
  193. ssl: opts['SSL'],
  194. ssl_key: ws_ssl_key,
  195. ssl_cert: ws_ssl_cert,
  196. ssl_disable_verify: !ssl_enable_verify,
  197. daemonize: !foreground,
  198. log: ws_log,
  199. pid: ws_rpc_pid)
  200. else
  201. unless opts['Pass']
  202. $stderr.puts "[-] Error: a password must be specified (-P)"
  203. exit(0)
  204. end
  205. $stderr.puts "[*] #{RPC_TYPE.upcase}RPC starting on #{opts['ServerHost']}:#{opts['ServerPort']} (#{opts['SSL'] ? "SSL" : "NO SSL"}):#{opts['ServerType']}..."
  206. $stderr.puts "[*] URI: #{opts['URI']}" if opts['URI']
  207. start_rpc_service(opts, frameworkOpts, foreground)
  208. end
  209. rescue ::Interrupt
  210. stop_json_rpc_service(conf: ws_conf_full_path,
  211. address: opts['ServerHost'],
  212. port: opts['ServerPort'],
  213. ssl: opts['SSL'],
  214. ssl_key: ws_ssl_key,
  215. ssl_cert: ws_ssl_cert,
  216. ssl_disable_verify: !ssl_enable_verify,
  217. daemonize: !foreground,
  218. log: ws_log,
  219. pid: ws_rpc_pid) if json_rpc
  220. end
  221. end