diff --git a/data/msf/README.md b/data/msf/README.md new file mode 100644 index 00000000..b3ae243e --- /dev/null +++ b/data/msf/README.md @@ -0,0 +1,67 @@ +# SMS Plugin +The [SMS](sms) plugin uses King Phisher's [REST API](rest-api-docs) to send SMS +messages when a new [Metasploit](metasploit) session is received. King Phisher's +REST API is accessible externally which allows running the SMS plugin within +Metasploit, on a system other than the King Phisher server. The system running +Metasploit only needs to be able to make a HTTP GET request to the King Phisher +server. + +## King Phisher Configuration +Edit King Phisher's `server_config.yml` file, under the `rest_api` set the +`enabled` value to `true`. + +Change the `token` value from `null` to a secret string that will be used to +access the King Phisher server's REST API remotely. Running this one-liner in +Linux will return a randomly generated 32 character string which can be used. + +```cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1``` + +Save the server configuration file and restart the King Phisher server. + +## Metasploit Configuration and Usage +Add the [sms.rb](sms) file to your Metasploit `~/.msf4/plugins` directory. + +If this is the first time using the SMS plugin, you will need to set four values +which will be saved in `~/.msf4/sms.yaml`. On future use, these settings will +automatically be loaded and do not need to be set again. Additionally you can +see descriptions of the SMS plugin commands by running `help` in msfconsole. + + * Start Metasploit and load the SMS plugin. + + `load sms` + + * Set the domain name of your King Phisher server. + + `sms_set_server king-phisher.com` + + * Set the King Phisher server's REST API token. + + `sms_set_token 0123456789abcdefABCDEF` + + * Set the cellphone number where you would like to receive SMS messages. + + `sms_set_number 0123456789` + + * Set your cell phone carrier. Currently King Phisher supports AT&T, Boost, Sprint, T-Mobile, Verizon, Virgin Mobile. + + `sms_set_carrier Boost` + + * Before saving, review your plugin settings. + + `sms_show_params` + + * If everything looks good, save your settings. + + `sms_save` + + * Start the SMS plugin, which will wait for incoming sessions. + + `sms_start` + + * When finished, stop the SMS plugin. + + `sms_stop` + +[metasploit]: https://github.com/rapid7/metasploit-framework +[rest-api-docs]: https://king-phisher.readthedocs.org/en/latest/server_api/rest_api.html?highlight=sms#get--_-api-sms-send +[sms]: ./sms.rb diff --git a/data/msf/sms.rb b/data/msf/sms.rb new file mode 100644 index 00000000..5b4f4083 --- /dev/null +++ b/data/msf/sms.rb @@ -0,0 +1,196 @@ +# Copyright (c) 2015, Brandan [coldfusion] +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of the nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +require 'uri' + +module Msf + class Plugin::SessionSMS < Msf::Plugin + include Msf::SessionEvent + + CARRIERS = ['AT&T', 'Boost', 'Sprint', 'T-Mobile', 'Verizon', 'Virgin Mobile'] + + def initialize(framework, opts) + super + add_console_dispatcher(SMSCommandDispatcher) + end + + def cleanup + self.framework.events.remove_session_subscriber(@inst) + remove_console_dispatcher('sms') + end + + def name + "sms" + end + + def desc + "Sends a SMS message when recieving a new session through the use of the King Phisher's REST API." + end + + def sms_yaml + "#{Msf::Config.get_config_root}/sms.yaml" + end + + class SMSCommandDispatcher < Plugin::SessionSMS + include Msf::Ui::Console::CommandDispatcher + + @king_phisher_server = nil + @king_phisher_token = nil + @sms_number = nil + @sms_carrier = nil + + def on_session_open(session) + print_status('Session received, sending SMS...') + begin + http_client = Rex::Proto::Http::Client.new("#{@king_phisher_server}") + http_client.connect + request = http_client.request_cgi({ + 'uri' => '/_/api/sms/send', + 'query' => "token=#{@king_phisher_token}&message=Shells+On+Deck!!+Session:+#{session.sid}&phone_number=#{@sms_number}&carrier=#{@sms_carrier}" + }) + response = http_client.send_recv(request) + rescue Exception => e + print_error('Exception occured, you done goofed!') + ensure + http_client.close + end + end + + def name + "sms" + end + + def read_settings + return false unless File.exist?(sms_yaml) + + ldconfig = YAML.load_file(sms_yaml) + @king_phisher_server = ldconfig['king_phisher_server'] + @king_phisher_token = ldconfig['king_phisher_token'] + @sms_number = ldconfig['sms_number'] + @sms_carrier = ldconfig['sms_carrier'] + return true + end + + def commands + { + 'sms_start' => 'Start SMS alerts for new sessions', + 'sms_stop' => 'Stop SMS alerts for new sessions', + 'sms_save' => "Save SMS settings to #{sms_yaml}", + 'sms_set_server' => 'Set domain name of the King Phisher server', + 'sms_set_token' => 'Set King Phisher\'s API token', + 'sms_set_number' => 'Set number to send SMS alerts to on new session', + 'sms_set_carrier' => 'Set carrier for sending SMS messages', + 'sms_show_params' => 'Shows currently set or saved parameters' + } + end + + def cmd_sms_start + unless read_settings + print_error('Could not read SMS settings!') + return + end + + self.framework.events.add_session_subscriber(self) + print_good('Starting SMS plugin, monitoring sessions...') + end + + def cmd_sms_stop + print_good('Stopping SMS alerting!') + self.framework.events.remove_session_subscriber(self) + end + + def cmd_sms_save + unless @king_phisher_server && @king_phisher_token && @sms_number && @sms_carrier + print_error('You have not provided all the parameters!') + return + end + + config = { + 'king_phisher_server' => @king_phisher_server, + 'king_phisher_token' => @king_phisher_token, + 'sms_number' => @sms_number, + 'sms_carrier' => @sms_carrier + } + File.open(sms_yaml, 'w') do |out| + YAML.dump(config, out) + end + print_good("All parameters saved to #{sms_yaml}") + end + + def cmd_sms_set_server(*args) + if args.length > 0 + print_status("Setting the King Phisher server to #{args[0]}") + @king_phisher_server = args[0] + else + print_error('Please provide the domain name of your King Phisher server!') + end + end + + def cmd_sms_set_token(*args) + if args.length > 0 + print_status("Setting King Phisher's REST API token to #{args[0]}") + @king_phisher_token = args[0] + else + print_error('Please provide the REST API token of your King Phisher server!') + end + end + + def cmd_sms_set_number(*args) + if args[0].length == 10 + print_status("Setting SMS number to #{args[0]}") + @sms_number = args[0] + else + print_error('Please provide a valid SMS number!') + end + end + + def cmd_sms_set_carrier(*args) + if args.length > 0 && CARRIERS.include?(args[0]) + print_status("Setting SMS carrier to #{args[0]}") + @sms_carrier = args[0] + else + print_error("Please provide a valid SMS carrier (#{CARRIERS.join(', ')})!") + end + end + + def cmd_sms_show_params + unless read_settings + print_error("Could not read settings from #{sms_yaml}!") + return + end + + print_status('Parameters:') + print_good(" King Phisher Server: #{@king_phisher_server}") + print_good(" King Phisher Token: #{@king_phisher_token}") + print_good(" SMS Number: #{@sms_number}") + print_good(" SMS Carrier: #{@sms_carrier}") + end + end + end +end diff --git a/docs/source/change_log.rst b/docs/source/change_log.rst index a50f97f4..14f62674 100644 --- a/docs/source/change_log.rst +++ b/docs/source/change_log.rst @@ -10,13 +10,15 @@ Version 0.2.x Version 0.2.1 ^^^^^^^^^^^^^ -*In Progress* +Released :release:`0.2.1` on July 14th, 2015 * Added syntax highlighting to the message edit tab * Technical documentation improvements, including documenting the REST API * Support reloading message templates when they change from an external editor * Support for pulling the client IP from a cookie set by an upstream proxy * Support for embedding training videos from YouTube +* Added a Metasploit plugin for using the REST API to send SMS messages +* Support for exporting visit information to GeoJSON Version 0.2.0 ^^^^^^^^^^^^^ diff --git a/docs/source/index.rst b/docs/source/index.rst index 2b1f02a4..10dada8a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -23,6 +23,7 @@ the `GitHub homepage`_. server_api/index.rst environment_vars.rst change_log.rst + release_steps.rst Indices and tables ================== diff --git a/docs/source/king_phisher/errors.rst b/docs/source/king_phisher/errors.rst index b9e788ad..86c7ed7b 100644 --- a/docs/source/king_phisher/errors.rst +++ b/docs/source/king_phisher/errors.rst @@ -8,9 +8,16 @@ Exceptions ---------- .. autoexception:: king_phisher.errors.KingPhisherError + :show-inheritance: .. autoexception:: king_phisher.errors.KingPhisherAbortRequestError + :show-inheritance: + :members: + :special-members: __init__ + .. autoexception:: king_phisher.errors.KingPhisherDatabaseError + :show-inheritance: .. autoexception:: king_phisher.errors.KingPhisherInputValidationError + :show-inheritance: diff --git a/docs/source/release_steps.rst b/docs/source/release_steps.rst new file mode 100644 index 00000000..350c0cec --- /dev/null +++ b/docs/source/release_steps.rst @@ -0,0 +1,35 @@ +Release Steps +============= + +This document contains the steps that are followed for each point version +release of King Phisher. + +Pre Release Steps +----------------- + +#. Test and fix any issues with the Windows MSI build +#. Ensure unit tests pass with Python 2.7 & Python 3.4 +#. Remove the version label +#. Create the final Windows build +#. Update the change log + +Release Steps +------------- + +#. Create a final signed commit on the dev branch +#. Push the dev branch to GitHub and ensure the Travis-CI build passes +#. Merge dev into master and push master to GitHub +#. Create and push a signed tag of the release commit +#. Create a new release on GitHub + + #. Upload the final Windows build + #. Insert the changes from the change log + +#. Update the Docker build +#. Publicize the release + +Post Release Steps +------------------ + +#. Increment the version number on the dev branch and re-set the version label +#. Update Python packages with pip diff --git a/king_phisher/errors.py b/king_phisher/errors.py index 7cb02cac..f8f355f1 100644 --- a/king_phisher/errors.py +++ b/king_phisher/errors.py @@ -42,7 +42,11 @@ class KingPhisherAbortRequestError(KingPhisherError): An exception that can be raised which when caught will cause the handler to immediately stop processing the current request. """ - pass + def __init__(self, response_sent=False): + """ + :param bool response_sent: Whether or not a response has already been sent to the client. + """ + self.response_sent = response_sent class KingPhisherDatabaseError(KingPhisherError): """ diff --git a/king_phisher/server/server.py b/king_phisher/server/server.py index 5b035a19..447f7fca 100644 --- a/king_phisher/server/server.py +++ b/king_phisher/server/server.py @@ -172,8 +172,9 @@ def _do_http_method(self, *args, **kwargs): self.server.throttle_semaphore.acquire() try: http_method_handler(*args, **kwargs) - except errors.KingPhisherAbortRequestError: - self.respond_not_found() + except errors.KingPhisherAbortRequestError as error: + if not error.response_sent: + self.respond_not_found() finally: self.server.throttle_semaphore.release() do_GET = _do_http_method diff --git a/king_phisher/version.py b/king_phisher/version.py index 2dbbcdbe..faeab41b 100644 --- a/king_phisher/version.py +++ b/king_phisher/version.py @@ -35,14 +35,14 @@ version_info = collections.namedtuple('version_info', ['major', 'minor', 'micro'])(0, 2, 1) """A tuple representing the version information in the format ('major', 'minor', 'micro')""" -version_label = 'beta' -"""A version lable such as alpha or beta.""" +version_label = '' +"""A version label such as alpha or beta.""" version = "{0}.{1}.{2}".format(version_info.major, version_info.minor, version_info.micro) """A string representing the full version information.""" # distutils_version is compatible with distutils.version classes distutils_version = version -"""A string sutiable for being parsed by :py:mod:`distutils.version` classes.""" +"""A string suitable for being parsed by :py:mod:`distutils.version` classes.""" if version_label: version += '-' + version_label