123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #!/usr/bin/env ruby
- # -*- coding: binary -*-
- #
- # $Id$
- #
- # This user interface listens on a port and provides clients that connect to
- # it with an RPC or JSON-RPC interface to the Metasploit Framework.
- #
- # $Revision$
- #
- RPC_TYPE = 'Msg'
- WS_TAG = 'msf-ws'
- WS_RPC_TAG = 'msf-json-rpc'
- WS_CONF = "#{WS_RPC_TAG}.ru"
- WS_ENV = 'production'
- def start_json_rpc_service(conf:, address:, port:, ssl:, ssl_key:, ssl_cert:,
- ssl_disable_verify:, daemonize:, log:, pid:)
- unless File.file?(conf)
- $stdout.puts "[-] No MSF JSON-RPC web service configuration found at #{conf}, not starting"
- return false
- end
- # check if MSF JSON-RPC web service is already started
- if File.file?(pid)
- ws_pid = Msf::Util::ServiceHelper.tail(pid)
- if ws_pid.nil? || !Msf::Util::ServiceHelper.process_active?(ws_pid.to_i)
- $stdout.puts "[-] MSF JSON-RPC web service PID file found, but no active process running as PID #{ws_pid}"
- $stdout.puts "[*] Deleting MSF JSON-RPC web service PID file #{pid}"
- File.delete(pid)
- else
- $stdout.puts "[*] MSF JSON-RPC web service is already running as PID #{ws_pid}"
- return false
- end
- end
- # attempt to start MSF JSON-RPC service
- thin_cmd = Msf::Util::ServiceHelper.thin_cmd(conf: conf,
- address: address,
- port: port,
- ssl: ssl,
- ssl_key: ssl_key,
- ssl_cert: ssl_cert,
- ssl_disable_verify: ssl_disable_verify,
- env: WS_ENV,
- daemonize: daemonize,
- log: log,
- pid: pid,
- tag: WS_RPC_TAG)
- Msf::Util::ServiceHelper.run_cmd("#{thin_cmd} start")
- end
- def stop_json_rpc_service(conf:, address:, port:, ssl:, ssl_key:, ssl_cert:,
- ssl_disable_verify:, daemonize:, log:, pid:)
- ws_pid = Msf::Util::ServiceHelper.tail(pid)
- $stdout.puts ''
- if ws_pid.nil? || !Msf::Util::ServiceHelper.process_active?(ws_pid.to_i)
- $stdout.puts '[*] MSF JSON-RPC web service is no longer running'
- if File.file?(pid)
- $stdout.puts "[*] Deleting MSF JSON-RPC web service PID file #{pid}"
- File.delete(pid)
- end
- else
- $stdout.puts "[*] Stopping MSF JSON-RPC web service PID #{ws_pid}"
- thin_cmd = Msf::Util::ServiceHelper.thin_cmd(conf: conf,
- address: address,
- port: port,
- ssl: ssl,
- ssl_key: ssl_key,
- ssl_cert: ssl_cert,
- ssl_disable_verify: ssl_disable_verify,
- env: WS_ENV,
- daemonize: daemonize,
- log: log,
- pid: pid,
- tag: WS_RPC_TAG)
- Msf::Util::ServiceHelper.run_cmd("#{thin_cmd} stop")
- end
- end
- def start_rpc_service(opts, frameworkOpts, foreground)
- # Fork into the background if requested
- begin
- if foreground
- $stdout.puts "[*] #{RPC_TYPE.upcase}RPC ready at #{Time.now}."
- else
- $stderr.puts "[*] #{RPC_TYPE.upcase}RPC backgrounding at #{Time.now}..."
- child_pid = Process.fork()
- if child_pid
- $stderr.puts "[*] #{RPC_TYPE.upcase}RPC background PID #{child_pid}"
- exit(0)
- end
- end
- rescue ::NotImplementedError
- $stderr.puts "[-] Background mode is not available on this platform"
- end
- # Create an instance of the framework
- $framework = Msf::Simple::Framework.create(frameworkOpts)
- # Run the plugin instance in the foreground.
- begin
- $framework.plugins.load("#{RPC_TYPE.downcase}rpc", opts).run
- rescue ::Interrupt
- $stderr.puts "[*] Shutting down"
- end
- end
- if $PROGRAM_NAME == __FILE__
- msfbase = __FILE__
- while File.symlink?(msfbase)
- msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
- end
- $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
- require 'msfenv'
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'rex/parser/arguments'
- ws_ssl_key_default = File.join(Msf::Config.config_directory, "#{WS_TAG}-key.pem")
- ws_ssl_cert_default = File.join(Msf::Config.config_directory, "#{WS_TAG}-cert.pem")
- ws_log = File.join(Msf::Config.config_directory, 'logs', "#{WS_RPC_TAG}.log")
- ws_rpc_pid = File.join(Msf::Config.config_directory, "#{WS_RPC_TAG}.pid")
- ws_ssl_key = ws_ssl_key_default
- ws_ssl_cert = ws_ssl_cert_default
- ssl_enable_verify = false
- foreground = false
- json_rpc = false
- frameworkOpts = {}
- opts = {
- 'RunInForeground' => true,
- 'SSL' => true,
- 'ServerHost' => '0.0.0.0',
- 'ServerPort' => 55553,
- 'ServerType' => RPC_TYPE,
- 'TokenTimeout' => 300,
- }
- # Declare the argument parser for msfrpcd
- arguments = Rex::Parser::Arguments.new(
- "-a" => [ true, "Bind to this IP address (default: #{opts['ServerHost']})" ],
- "-p" => [ true, "Bind to this port (default: #{opts['ServerPort']})" ],
- "-U" => [ true, "Specify the username to access msfrpcd" ],
- "-P" => [ true, "Specify the password to access msfrpcd" ],
- "-u" => [ true, "URI for Web server" ],
- "-t" => [ true, "Token Timeout seconds (default: #{opts['TokenTimeout']})" ],
- "-S" => [ false, "Disable SSL on the RPC socket" ],
- "-f" => [ false, "Run the daemon in the foreground" ],
- "-n" => [ false, "Disable database" ],
- "-j" => [ false, "(JSON-RPC) Start JSON-RPC server" ],
- "-k" => [ false, "(JSON-RPC) Path to private key (default: #{ws_ssl_key_default})" ],
- "-c" => [ false, "(JSON-RPC) Path to certificate (default: #{ws_ssl_cert_default})" ],
- "-v" => [ false, "(JSON-RPC) SSL enable verify (optional) client cert requests" ],
- "-h" => [ false, "Help banner" ])
- # Parse command line arguments.
- arguments.parse(ARGV) { |opt, idx, val|
- case opt
- when "-a"
- opts['ServerHost'] = val
- when "-S"
- opts['SSL'] = false
- when "-p"
- opts['ServerPort'] = val
- when '-U'
- opts['User'] = val
- when '-P'
- opts['Pass'] = val
- when "-t"
- opts['TokenTimeout'] = val.to_i
- when "-f"
- foreground = true
- when "-u"
- opts['URI'] = val
- when "-n"
- frameworkOpts['DisableDatabase'] = true
- when "-j"
- json_rpc = true
- when "-k"
- ws_ssl_key = val
- when "-c"
- ws_ssl_cert = val
- when "-v"
- ssl_enable_verify = true
- when "-h"
- print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage)
- exit
- end
- }
- $0 = "msfrpcd"
- begin
- if json_rpc
- if !File.file?(ws_ssl_key_default) || !File.file?(ws_ssl_cert_default)
- $stdout.puts "[-] It doesn't appear msfdb has been run; please run 'msfdb init' first."
- abort
- end
- $stderr.puts "[*] JSON-RPC starting on #{opts['ServerHost']}:#{opts['ServerPort']} (#{opts['SSL'] ? "SSL" : "NO SSL"})..."
- $stderr.puts "[*] URI: /api/v1/json-rpc"
- $stderr.puts "[*] JSON-RPC server log: #{ws_log}" unless foreground
- $stderr.puts "[*] JSON-RPC server PID file: #{ws_rpc_pid}" unless foreground
- ws_conf_full_path = File.expand_path(File.join(File.dirname(msfbase), WS_CONF))
- start_json_rpc_service(conf: ws_conf_full_path,
- address: opts['ServerHost'],
- port: opts['ServerPort'],
- ssl: opts['SSL'],
- ssl_key: ws_ssl_key,
- ssl_cert: ws_ssl_cert,
- ssl_disable_verify: !ssl_enable_verify,
- daemonize: !foreground,
- log: ws_log,
- pid: ws_rpc_pid)
- else
- unless opts['Pass']
- $stderr.puts "[-] Error: a password must be specified (-P)"
- exit(0)
- end
- $stderr.puts "[*] #{RPC_TYPE.upcase}RPC starting on #{opts['ServerHost']}:#{opts['ServerPort']} (#{opts['SSL'] ? "SSL" : "NO SSL"}):#{opts['ServerType']}..."
- $stderr.puts "[*] URI: #{opts['URI']}" if opts['URI']
- start_rpc_service(opts, frameworkOpts, foreground)
- end
- rescue ::Interrupt
- stop_json_rpc_service(conf: ws_conf_full_path,
- address: opts['ServerHost'],
- port: opts['ServerPort'],
- ssl: opts['SSL'],
- ssl_key: ws_ssl_key,
- ssl_cert: ws_ssl_cert,
- ssl_disable_verify: !ssl_enable_verify,
- daemonize: !foreground,
- log: ws_log,
- pid: ws_rpc_pid) if json_rpc
- end
- end
|