King Phisher Jupyter Notebook

This notebook contains useful recipes for interactive analysis of the data collected by King Phisher. This uses a direct connection to the back end database to retrieve the information for analysis. Once the environment is setup the functionality provided by Jupyter notebooks can be leveraged to visualize arbitrary data including graphing it with matplotlib. Various examples are included to perform basic analysis.

Initial Setup

The setup cell below needs to be run to setup the environment and connect to the database. It will prompt for the directory where King Phisher is installed to import the necessary modules. It will also prompt for an optional database connection string. If no connection string is specified, it will be determined by reading the server configuration file in the install directory.

Remote Server Connections

A remote connection to a database can be opened via SSH using port forwarding. The following command will open and forward local port 5432 (the default PostgreSQL service port) to the same on the remote server:

ssh -f -N -L 5432:localhost:5432 king-phisher.lan

This notebook can then be run on a seperate host given that King Phisher is installed, and the necessary database connection can be made.


In [ ]:
# run this to be prompted for the necessary information to configure the environment
from ipywidgets import widgets
from IPython import display

import collections
import os
import sys
import tabulate
import yaml

from sqlalchemy import and_
from sqlalchemy import or_

king_phisher_home = widgets.Text(value='/opt/king-phisher', width='350px')
king_phisher_server_config = widgets.Text(value='server_config.yml', width='350px')
db_connection = widgets.Text(placeholder='optional', width='350px')
setup_button = widgets.Button(description='Setup', width='550px')
results = widgets.HTML()

db_manager = None
db_models = None

def setup_and_initialize(_):
    global db_manager
    global db_models
    directory = os.path.expanduser(king_phisher_home.value)
    if not os.path.isfile(os.path.join(directory, 'king_phisher', '__init__.py')):
        results.value = 'Invalid King Phisher Install Directory'
        return
    sys.path.append(directory)
    from king_phisher.server.database import manager as db_manager
    from king_phisher.server.database import models as db_models

    if db_connection.value:
        db_manager.init_database(db_connection.value)
    else:
        with open(os.path.join(directory, king_phisher_server_config.value), 'r') as file_h:
            server_config = yaml.load(file_h)
        db_manager.init_database(server_config['server']['database'])
    results.value = 'Successfully Initialized The Database'

setup_button.on_click(setup_and_initialize)
display.display(widgets.HBox([widgets.HTML(value='Install Directory', width='200px'), king_phisher_home]))
display.display(widgets.HBox([widgets.HTML(value='Server Configuration File', width='200px'), king_phisher_server_config]))
display.display(widgets.HBox([widgets.HTML(value='Database Connection String', width='200px'), db_connection]))
display.display(setup_button)
display.display(results)

In [ ]:
# get campaigns from a specific industry for comparison
session = db_manager.Session()
industries = dict([(industry.name, industry.id) for industry in session.query(db_models.Industry)])

industry = widgets.Dropdown(
    options=[''] + list(industries.keys()),
    description='Industry: ',
)
query = session.query(db_models.CampaignType)
query = query.order_by(db_models.CampaignType.name)
campaign_types = collections.OrderedDict()
for ctype in query:
    campaign_types[ctype.id] = widgets.Checkbox(description=ctype.name, value=True)

session.close()

html_display = widgets.HTML()
def on_generate_button_clicked(_):
    campaigns = []
    campaign_type_ids = [type_id for (type_id, checkbox) in campaign_types.items() if checkbox.value]
    if industry.value:
        industry_id = industries[industry.value]
    else:
        industry_id = None
    session = db_manager.Session()
    query = session.query(db_models.Campaign)
    query = query.filter(db_models.Campaign.campaign_type_id.in_(campaign_type_ids))
    query = query.join(db_models.Campaign.company, aliased=True)
    if industry_id is not None:
        query = query.filter(db_models.Company.industry_id == industry_id)
    table = []
    all_messages = 0
    all_visits = 0
    all_credentials = 0
    for campaign in query:
        messages = len(campaign.messages)
        all_messages += messages
        visits = len(campaign.visits)
        all_visits += visits
        credentials = len(campaign.credentials)
        all_credentials += credentials
        table.append((
            campaign.id,
            campaign.name,
            ('' if campaign.company is None else campaign.company.name),
            campaign.created.strftime('%Y-%m-%d'),
            campaign.user_id,
            "{0:,}".format(messages),
            "{0:,}".format(visits),
            "{0:,}".format(credentials),
            campaign.campaign_type.name if campaign.campaign_type else ''
        ))
    session.close()
    html_display.value = "<h3>Showing {0:,} Campaigns</h3>".format(len(table))
    table.append((
        '',
        'Grand Totals',
        '',
        '',
        '',
        "{:,}".format(all_messages),
        "{:,}".format(all_visits),
        "{:,}".format(all_credentials),
        ''
    ))
    html_display.value += '<table class="table table-striped">'
    html_display.value += '\n'.join(tabulate.tabulate(table, headers=('ID', 'Name', 'Company', 'Created', 'User', 'Messages', 'Visits', 'Creds', 'Type'), tablefmt='html').split('\n')[1:-1])
    html_display.value += '</table>'

generate_button = widgets.Button(description='Generate Stats')
generate_button.on_click(on_generate_button_clicked)

display.display(industry)
list(map(display.display, campaign_types.values()))
display.display(generate_button)
display.display(html_display)

In [ ]:
# display information about campaigns
session = db_manager.Session()
campaigns = session.query(db_models.Campaign)
campaigns = campaigns.order_by(db_models.Campaign.created)
table = []
for campaign in campaigns:
    table.append((
        campaign.id,
        campaign.name,
        campaign.created.strftime('%Y-%m-%d'),
        campaign.user_id,
        "{0:,}".format(len(campaign.messages)),
        "{0:,}".format(len(campaign.visits)),
        "{0:,}".format(len(campaign.credentials)),
        campaign.campaign_type.name if campaign.campaign_type else ''
    ))
session.close()
print(tabulate.tabulate(table, headers=('ID', 'Name', 'Created', 'User', 'Messages', 'Visits', 'Creds', 'Type')))

In [ ]:
# show information on the campaign type tag
session = db_manager.Session()
tags = session.query(db_models.CampaignType)
tags = tags.order_by(db_models.CampaignType.id)
table = []
for tag in tags:
    table.append((
        tag.id,
        tag.name,
        tag.description,
        "{0:,}".format(len(tag.campaigns))
    ))
session.close()
print(tabulate.tabulate(table, headers=('ID', 'Name', 'Description', 'Campaigns')))

In [ ]:
# display a pie chart of the campaigns grouped by their tag
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt

import collections

session = db_manager.Session()
tags = session.query(db_models.CampaignType)
tags = tags.order_by(db_models.CampaignType.id)
campaign_count = session.query(db_models.Campaign).count()
campaign_types = collections.OrderedDict(
    (tag.name, len(tag.campaigns) / campaign_count) for tag in tags
)
session.close()

plt.figure(1, figsize=(10, 10))
plt.pie(list(campaign_types.values()), labels=campaign_types.keys(), autopct='%1.1f%%')

plt.title('Campaign Types')
plt.show()

In [ ]:
# delete a campaign by its id
session = db_manager.Session()
campaign = session.query(db_models.Campaign).filter_by(id=1).first()
print(campaign)
session.delete(campaign)
session.commit()
session.close()

In [ ]: