#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# Faraday Penetration Test IDE
# Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
# See the file 'doc/LICENSE' for the license information
"""A module to handle request to the Faraday Server.
Note:
Before using this as an API, you should copy this file and edit
the FARADAY_UP and the SERVER_URL variables found inmediatly
below the imports.
FARADAY_UP should be set to False in the copy of the file, and SERVER_URL
must be a valid server url.
Warning:
This module was though of primarly as a way of querying and removing
information from the Faraday Server. Adding objects is supported, but should
be used with care, specially regarding the ID of objects, which must
be always unique.
"""
import urllib
import os
import json
import logging
try:
import urlparse
from urllib import urlencode
except: # For Python 3
import urllib.parse as urlparse
from urllib.parse import urlencode
import requests
from persistence.server.utils import force_unique
from persistence.server.server_io_exceptions import (WrongObjectSignature,
CantCommunicateWithServerError,
ConflictInDatabase,
ResourceDoesNotExist,
Unauthorized)
from persistence.server.changes_stream import (
CouchChangesStream,
WebsocketsChangesStream
)
# NOTE: Change is you want to use this module by itself.
# If FARADAY_UP is False, SERVER_URL must be a valid faraday server url
logger = logging.getLogger(__name__)
FARADAY_UP = True
FARADAY_UPLOAD_REPORTS_WEB_COOKIE = None
FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL = None
SERVER_URL = "http://127.0.0.1:5985"
AUTH_USER = ""
AUTH_PASS = ""
OBJECT_TYPE_END_POINT_MAPPER = {
'CommandRunInformation': 'commands',
'Host': 'hosts',
'Vulnerability': 'vulns',
'VulnerabilityWeb': 'vulns',
'Service': 'services',
'Note': 'comment',
'Cred': 'credential',
}
from config import constant as CONSTANTS
LOCAL_CONFIG_FILE = os.path.expanduser(
os.path.join(CONSTANTS.CONST_FARADAY_HOME_PATH, 'config/server.ini'))
def _conf():
from config.configuration import getInstanceConfiguration
CONF = getInstanceConfiguration()
# If you are running this libs outside of Faraday, cookies are not setted.
# you need get a valid cookie auth and set that.
# Fplugin run in other instance, so this dont generate any trouble.
if not CONF.getDBSessionCookies() and not FARADAY_UPLOAD_REPORTS_WEB_COOKIE:
server_url = CONF.getServerURI() if FARADAY_UP else SERVER_URL
cookie = login_user(server_url, CONF.getAPIUsername(), CONF.getAPIPassword())
CONF.setDBSessionCookies(cookie)
return CONF
def _get_base_server_url():
# Faraday server is running, and this module is used by upload_reports...
if FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL:
server_url = FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL
logger.info('Detected upload cookie Using server_url as {0}'.format(server_url))
elif FARADAY_UP:
server_url = _conf().getAPIUrl()
logger.info('Detected faraday client running. Using server_url as {0}'.format(server_url))
else:
server_url = SERVER_URL
logger.info('Could not detect upload or client running. Using default server_url as {0}'.format(server_url))
return server_url.rstrip('/')
def _create_server_api_url():
"""Return the server's api url."""
return "{0}/_api/v2".format(_get_base_server_url())
def _create_server_get_url(workspace_name, object_name=None, object_id=None, **params):
"""Creates a url to get from the server. Takes the workspace name
as a string, an object_name paramter which is the object you want to
query as a string ('hosts', 'interfaces', etc) .
object_name may be None if you want to get the workspace itself.
Return the get_url as a string.
"""
get_url = "/{0}".format(object_name) if object_name else ""
get_url += "/{0}/".format(object_id) if object_id else ""
get_url = '{0}/ws/{1}{2}'.format(_create_server_api_url(),
workspace_name,
get_url)
return get_url
def _create_server_post_url(workspace_name, obj_type, command_id):
server_api_url = _create_server_api_url()
object_end_point_name = OBJECT_TYPE_END_POINT_MAPPER[obj_type]
if obj_type == 'comment':
object_end_point_name = object_end_point_name.strip('/') + '_unique/'
post_url = '{0}/ws/{1}/{2}/'.format(server_api_url, workspace_name, object_end_point_name)
if command_id:
get_params = {'command_id': command_id}
post_url += '?' + urlencode(get_params)
return post_url
def _create_server_put_url(workspace_name, obj_type, obj_id, command_id):
server_api_url = _create_server_api_url()
object_end_point_name = OBJECT_TYPE_END_POINT_MAPPER[obj_type]
put_url = '{0}/ws/{1}/{2}/{3}/'.format(server_api_url, workspace_name, object_end_point_name, obj_id)
if command_id:
get_params = {'command_id': command_id}
put_url += '?' + urlencode(get_params)
return put_url
def _create_server_delete_url(workspace_name, obj_type, object_id, command_id=None):
return _create_server_put_url(workspace_name, obj_type, object_id, command_id)
# XXX: COUCH IT!
def _create_couch_get_url(workspace_name, object_id):
server_url = _get_base_server_url()
return "{0}/{1}/{2}".format(server_url, workspace_name, object_id)
# XXX: COUCH IT!
def _create_couch_post_url(workspace_name, object_id):
return _create_couch_get_url(workspace_name, object_id)
# XXX: COUCH IT!
def _create_couch_db_url(workspace_name):
server_base_url = _get_base_server_url()
db_url = '{0}/{1}'.format(server_base_url, workspace_name)
return db_url
def _create_server_db_url(workspace_name):
server_api_url = _create_server_api_url()
db_url = '{0}/ws/'.format(server_api_url)
return db_url
def _add_session_cookies(func):
"""A decorator which wrapps a function dealing with I/O with the server and
adds authentication to the parameters.
"""
def wrapper(*args, **kwargs):
if FARADAY_UPLOAD_REPORTS_WEB_COOKIE:
kwargs['cookies'] = FARADAY_UPLOAD_REPORTS_WEB_COOKIE
else:
kwargs['cookies'] = _conf().getDBSessionCookies()
response = func(*args, **kwargs)
return response
return wrapper if FARADAY_UP else func
@_add_session_cookies
def _unsafe_io_with_server(server_io_function, server_expected_responses,
server_url, **payload):
"""A wrapper for functions which deals with I/O to or from the server.
It calls the server_io_function with url server_url and the payload,
raising an CantCommunicateWithServerError if the response wasn't
server_expected_response or if there was a Connection Error.
Return the response from the server.
"""
answer = None
logger.debug('Sending request to api endpoint {0}'.format(server_url))
try:
answer = server_io_function(server_url, **payload)
if answer.status_code == 409:
raise ConflictInDatabase(answer)
if answer.status_code == 404:
raise ResourceDoesNotExist(server_url)
if answer.status_code == 403 or answer.status_code == 401:
raise Unauthorized(answer)
if answer.status_code not in server_expected_responses:
raise requests.exceptions.RequestException(response=answer)
except requests.exceptions.RequestException as ex:
logger.debug(ex)
try:
if answer and 'messages' in answer.json():
logger.info('Faraday server error message: {0}'.format(answer.json()['messages']))
except ValueError:
logger.debug('Could not decode json from server')
raise CantCommunicateWithServerError(server_io_function, server_url, payload, answer)
return answer
def _parse_json(response_object):
"""Takes a response object and return its response as a dictionary."""
try:
return response_object.json()
except ValueError:
return {}
def _get(request_url, **params):
"""Get from the request_url. Takes an arbitrary number of parameters
to customize the request_url if necessary.
Will raise a CantCommunicateWithServerError if requests cant stablish
connection to server or if response is not equal to 200.
Return a dictionary with the information in the json.
"""
return _parse_json(_unsafe_io_with_server(requests.get,
[200],
request_url,
params=params))
def _put(post_url, expected_response=201, **params):
"""Put to the post_url. If update is True, try to get the object
revision first so as to update the object in Couch. You can
customize the expected response (it should be 201, but Couchdbkit returns
200, so...). Also take an arbitrary number of parameters to put into the
post_url.
Will raise a CantCommunicateWithServerError if requests cant stablish
connection to server or if response is not equal to 201.
Return a dictionary with the response from couchdb, which looks like this:
{u'id': u'61', u'ok': True, u'rev': u'1-967a00dff5e02add41819138abb3284d'}
"""
return _parse_json(_unsafe_io_with_server(requests.put,
[expected_response],
post_url,
json=params))
def _post(post_url, update=False, expected_response=201, **params):
return _parse_json(_unsafe_io_with_server(requests.post,
[expected_response],
post_url,
json=params))
def _delete(delete_url, database=False):
"""Deletes the object on delete_url. If you're deleting a database,
specify the database parameter to True"""
params = {}
if not database:
last_rev = _get(delete_url)['_rev']
params = {'rev': last_rev}
return _parse_json(_unsafe_io_with_server(requests.delete,
[200,204],
delete_url,
params=params))
def _get_raw_hosts(workspace_name, **params):
"""Take a workspace_name and an arbitrary number of params and return
a dictionary with the hosts table."""
request_url = _create_server_get_url(workspace_name, 'hosts', **params)
return _get(request_url, **params)
def _get_raw_vulns(workspace_name, **params):
"""Take a workspace_name and an arbitrary number of params and return
a dictionary with the vulns table."""
params = {key: value for key, value in params.items() if value}
request_url = _create_server_get_url(workspace_name, 'vulns', **params)
return _get(request_url, **params)
def _get_raw_services(workspace_name, **params):
"""Take a workspace_name and an arbitrary number of params and return
a dictionary with the services table.
If you provide a host_id and services_of_host=True, it returns all services of that specified host"""
request_url = _create_server_get_url(workspace_name, 'services', **params)
return _get(request_url, **params)
def _get_raw_notes(workspace_name, **params):
"""Take a workspace name and an arbitrary number of params and
return a dictionary with the notes table."""
request_url = _create_server_get_url(workspace_name, 'comment', params.get('id', None))
return _get(request_url, **params)
def _get_raw_credentials(workspace_name, **params):
"""Take a workspace name and an arbitrary number of params and
return a dictionary with the credentials table."""
request_url = _create_server_get_url(workspace_name, 'credential', params.pop('id', None), **params)
return _get(request_url, **params)
def _get_raw_commands(workspace_name, **params):
request_url = _create_server_get_url(workspace_name, 'commands', params.get('id', None))
return _get(request_url, **params)
def _get_raw_workspace_summary(workspace_name):
request_url = _create_server_get_url(workspace_name)
return _get(request_url)
def _save_to_server(workspace_name, **params):
"""
:param workspace_name:
:param params:
:return:
"""
post_url = _create_server_post_url(workspace_name, params['type'], params.get('command_id', None))
return _post(post_url, update=False, expected_response=201, **params)
def _update_in_server(workspace_name, faraday_object_id, **params):
put_url = _create_server_put_url(workspace_name, params['type'], faraday_object_id, params.get('command_id', None))
return _put(put_url, expected_response=200, **params)
def _save_db_to_server(db_name, **params):
post_url = _create_server_db_url(db_name)
return _post(post_url, expected_response=201, **params)
def _delete_from_server(workspace_name, faraday_object_type, faraday_object_id):
delete_url = _create_server_delete_url(workspace_name, faraday_object_type, faraday_object_id)
return _delete(delete_url)
@_add_session_cookies
def _couch_changes(workspace_name, **params):
return CouchChangesStream(workspace_name,
_create_couch_db_url(workspace_name),
**params)
def _get_faraday_ready_dictionaries(workspace_name, faraday_object_name,
faraday_object_row_name, full_table=True,
**params):
"""Takes a workspace_name (str), a faraday_object_name (str),
a faraday_object_row_name (str) and an arbitrary number of params.
Return a list of dictionaries that hold the information for the objects
in table faraday_object_name.
The full_table paramether may be used to get the full dictionary instead
of just the one inside the 'value' key which holds information about the
object.
Preconditions:
faraday_object_name == 'host', 'vuln', 'interface', 'service', 'note'
or 'credential'
faraday_object_row_name must be the key to the dictionary which holds
the information of the object per se in the table. most times this is 'rows'
"""
object_to_func = {'hosts': _get_raw_hosts,
'vulns': _get_raw_vulns,
'services': _get_raw_services,
'notes': _get_raw_notes,
'credentials': _get_raw_credentials,
'commands': _get_raw_commands}
appropiate_function = object_to_func[faraday_object_name]
appropiate_dictionary = appropiate_function(workspace_name, **params)
faraday_ready_dictionaries = [appropiate_dictionary]
if faraday_object_row_name in appropiate_dictionary:
faraday_ready_dictionaries = []
for raw_dictionary in appropiate_dictionary[faraday_object_row_name]:
if not full_table:
faraday_ready_dictionaries.append(raw_dictionary['value'])
else:
faraday_ready_dictionaries.append(raw_dictionary)
return faraday_ready_dictionaries
def get_hosts(workspace_name, **params):
"""Get hosts from the server.
Args:
workspace_name (str): the workspace from which to get the hosts.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the hosts matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'hosts',
'rows', **params)
def get_all_vulns(workspace_name, **params):
"""Get vulns, both normal and web, from the server.
Args:
workspace_name (str): the workspace from which to get the vulns.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the vulns matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'vulns',
'vulnerabilities', **params)
def get_vulns(workspace_name, **params):
"""Get only normal vulns from the server.
Args:
workspace_name (str): the workspace from which to get the vulns.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the vulns matching the query.
"""
return get_all_vulns(workspace_name, type='Vulnerability', **params)
def get_web_vulns(workspace_name, **params):
"""Get only web vulns from the server.
Args:
workspace_name (str): the workspace from which to get the vulns.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the vulns matching the query.
"""
return get_all_vulns(workspace_name, type="VulnerabilityWeb", **params)
def get_interfaces(workspace_name, **params):
"""Get interfaces from the server.
Args:
workspace_name (str): the workspace from which to get the interfaces.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the interfaces matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'interfaces',
'interfaces', **params)
def get_services(workspace_name, **params):
"""Get services from the server.
Args:
workspace_name (str): the workspace from which to get the services.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the services matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'services',
'services', **params)
def get_credentials(workspace_name, **params):
"""Get credentials from the server.
Args:
workspace_name (str): the workspace from which to get the credentials.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the credentials matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'credentials',
'rows', **params)
def get_notes(workspace_name, **params):
"""Get notes from the server.
Args:
workspace_name (str): the workspace from which to get the notes.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the notes matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'notes',
'rows', **params)
def get_commands(workspace_name, **params):
"""Get commands from the server.
Args:
workspace_name (str): the workspace from which to get the commands.
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the commands matching the query.
"""
return _get_faraday_ready_dictionaries(workspace_name, 'commands',
'commands', **params)
def get_objects(workspace_name, object_signature, **params):
"""Get any type of object from the server, be it hosts, vulns, interfaces,
services, credentials, commands or notes.
Args:
workspace_name (str): the workspace from which to get the commands.
object_signature (str): the type of object to get. Must equal 'hosts',
'vulns', 'interfaces', 'services', 'credentials', 'notes' or 'commands'
**params: any of valid request parameters for CouchDB.
Returns:
A dictionary containing the commands matching the query.
Raises:
WrongObjectSignature: if the object_signature string didn't match
a faraday object.
"""
object_to_func = {'hosts': get_hosts,
'vulns': get_vulns,
'interfaces': get_interfaces,
'services': get_services,
'credentials': get_credentials,
'notes': get_notes,
'commands': get_commands}
try:
appropiate_function = object_to_func[object_signature]
except KeyError:
raise WrongObjectSignature(object_signature)
return appropiate_function(workspace_name, **params)
def _websockets_changes(workspace_name, **extra_params):
return WebsocketsChangesStream(workspace_name, 'localhost', **extra_params)
# cha cha cha chaaaanges!
def get_changes_stream(workspace_name, heartbeat='1000', stream_provider=_websockets_changes, **extra_params):
"""
stream_provider: A function that returns an instance of a Stream provider
"""
return stream_provider(workspace_name, feed='continuous',
heartbeat=heartbeat, **extra_params)
def get_workspaces_names():
"""Returns:
A dictionary with a list with the workspaces names."""
return _get("{0}/ws".format(_create_server_api_url()))
# XXX: COUCH IT!
def _clean_up_stupid_couch_response(response_string):
"""Couch likes to give invalid jsons as a response :). So nice."""
interesting_part = "{".join(response_string.split("{")[1:])
almost_there = interesting_part.split("}")[0:-1]
ok_yeah = "}".join(almost_there)
hopefully_valid_json = "{{{0}}}".format(ok_yeah)
return json.loads(hopefully_valid_json)
# XXX: COUCH IT!
# COUCH IT LEVEL: REVOLUTIONS
def get_object_before_last_revision(workspace_name, object_id):
"""Get an object before its last revision. Useful to get information about
recently deleted objects.
Warning:
Error-pronce process. You should check for 'None' after usage,
as that's the return value if any problem arose during execution.
Args:
workspace_name (str): the workspace where the object was
object_id (str): the id of the object
Returns:
A dictionary with the object's information.
"""
get_url = _create_couch_get_url(workspace_name, object_id)
response = _unsafe_io_with_server(requests.get, [200], get_url,
params={'revs': 'true', 'open_revs': 'all'})
try:
valid_json_response = _clean_up_stupid_couch_response(response.text)
except ValueError:
return None
try:
id_before_del = valid_json_response['_revisions']['ids'][1]
new_number_for_rev = valid_json_response['_revisions']['start'] - 1
except KeyError: # one if never too safe when you call a function called "_clean_up_stupid_couch_response"
return None
rev_id_before_del = "{0}-{1}".format(new_number_for_rev, id_before_del)
object_dict = _get(get_url, rev=rev_id_before_del)
return object_dict
def get_object(workspace_name, object_signature, object_id):
"""Get an unique object of arbitrary type.
Args:
workspace_name (str): the workspace where the object should be found.
object_signature (str): must be either 'hosts', 'vulns', 'interfaces'
'services', 'credentials', 'notes' or 'commands'.
object_id (str): the id of the object
Returns:
A dictionary containing information about the object.
Raises:
MoreThanOneObjectFoundByID: if for some reason the object_id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
objects = get_objects(workspace_name, object_signature, couchid=object_id)
return force_unique(objects)
def get_host(workspace_name, host_id):
"""Get an unique host.
Args:
workspace_name (str): the workspace where the object should be found.
host_id (str): the id of the host
Returns:
A dictionary containing information about the host.
Raises:
MoreThanOneObjectFoundByID: if for some reason the host id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_hosts(workspace_name, couchid=host_id))
def get_vuln(workspace_name, vuln_id):
"""Get an unique vuln.
Args:
workspace_name (str): the workspace where the object should be found.
vuln_id (str): the id of the vuln
Returns:
A dictionary containing information about the vuln.
Raises:
MoreThanOneObjectFoundByID: if for some reason the vuln id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_vulns(workspace_name, couchid=vuln_id))
def get_web_vuln(workspace_name, vuln_id):
"""Get an unique web vuln.
Args:
workspace_name (str): the workspace where the object should be found.
web vuln_id (str): the id of the web vuln
Returns:
A dictionary containing information about the web vuln.
Raises:
MoreThanOneObjectFoundByID: if for some reason the web vuln id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_web_vulns(workspace_name, couchid=vuln_id))
def get_interface(workspace_name, interface_id):
"""Get an unique interface.
Args:
workspace_name (str): the workspace where the object should be found.
interface_id (str): the id of the interface
Returns:
A dictionary containing information about the interface.
Raises:
MoreThanOneObjectFoundByID: if for some reason the interface id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_interfaces(workspace_name, couchid=interface_id))
def get_service(workspace_name, service_id):
"""Get an unique service.
Args:
workspace_name (str): the workspace where the object should be found.
service_id (str): the id of the service
Returns:
A dictionary containing information about the service.
Raises:
MoreThanOneObjectFoundByID: if for some reason the service id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_services(workspace_name, couchid=service_id))
def get_note(workspace_name, note_id):
"""Get an unique note.
Args:
workspace_name (str): the workspace where the object should be found.
note_id (str): the id of the note
Returns:
A dictionary containing information about the note.
Raises:
MoreThanOneObjectFoundByID: if for some reason the note id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_notes(workspace_name, couchid=note_id))
def get_credential(workspace_name, credential_id):
"""Get an unique credential.
Args:
workspace_name (str): the workspace where the object should be found.
credential_id (str): the id of the credential
Returns:
A dictionary containing information about the credential.
Raises:
MoreThanOneObjectFoundByID: if for some reason the credential id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_services(workspace_name, couchid=credential_id))
def get_command(workspace_name, command_id):
"""Get an unique command.
Args:
workspace_name (str): the workspace where the object should be found.
command_id (str): the id of the command
Returns:
A dictionary containing information about the command.
Raises:
MoreThanOneObjectFoundByID: if for some reason the command id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
return force_unique(get_commands(workspace_name, couchid=command_id))
def get_workspace(workspace_name, **params):
"""Get an unique command.
Args:
command_name (str): the command where the object should be found.
command_id (str): the id of the command
Returns:
A dictionary containing information about the command.
Raises:
MoreThanOneObjectFoundByID: if for some reason the command id is shared
by two or more objects. This should never happen. If it does,
contact Infobyte LCC.
"""
request_url = _create_server_get_url(workspace_name)
return _get(request_url, **params)
def get_workspace_summary(workspace_name):
"""Get a collection of data about the workspace.
Args:
workspace_name (str): the workspace to get the stats from.
Returns:
A dictionary with the workspace's information
"""
return _get_raw_workspace_summary(workspace_name)['stats']
def get_workspace_numbers(workspace_name):
"""Get the number of hosts, interfaces, services and vulns in the workspace.
Args:
workspace_name (str): the name of the workspace to query
Return:
A tuple of 4 elements with the amounts of hosts, interfaces, services and vulns.
"""
stats = _get_raw_workspace_summary(workspace_name)['stats']
return stats['hosts'], stats['services'], stats['total_vulns']
def get_hosts_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of hosts in the workspace as an integer.
"""
return int(get_workspace_summary(workspace_name)['hosts'])
def get_services_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of services in the workspace as an integer.
"""
return int(get_workspace_summary(workspace_name)['services'])
def get_interfaces_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of interfaces in the workspace as an integer.
"""
return int(get_workspace_summary(workspace_name)['interfaces'])
def get_vulns_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of vulns in the workspace as an integer.
"""
return int(get_workspace_summary(workspace_name)['total_vulns'])
def get_notes_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of notes in the workspace as an integer.
"""
return int(get_workspace_summary(workspace_name)['notes'])
def get_credentials_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of credentials in the workspace as an integer.
"""
return int(_get_raw_credentials(workspace_name, **params))
def get_commands_number(workspace_name, **params):
"""
Args:
workspace_name (str): the name of the workspace to query
**params: any of the Couchdb request parameters
Returns:
The amount of commands in the workspace as an integer.
"""
return int(_get_raw_commands(workspace_name, **params))
def create_host(workspace_name, command_id, ip, os, default_gateway=None,
description="", metadata=None, owned=False, owner="",
parent=None, hostnames=None):
"""Create a host.
Args:
workspace_name (str): the name of the workspace where the host will be saved.
id (str): the id of the host. Must be unique.
name (str): the host's name
os (str): the operative system of the host
default_gateway (str): the host's default_gateway
description (str): a description.
metadata: a collection of metadata. If you don't know the metada. leave
on None.
owned (bool): is the host owned or not?
owner (str): an owner for the host
parent (Faraday Object): the host's parent. If you don't know this, leave
on None.
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command_id=command_id,
ip=ip, os=os,
default_gateway=default_gateway,
owned=owned,
metadata=metadata,
owner=owner,
parent=parent,
description=description,
hostnames=hostnames,
type="Host")
def update_host(workspace_name, command_id, id, ip, os, default_gateway="",
description="", metadata=None, owned=False, owner="",
parent=None, hostnames=None):
"""Updates a host.
Args:
workspace_name (str): the name of the workspace where the host will be saved.
id (str): the id of the host. Must be unique.
name (str): the host's name
os (str): the operative system of the host
default_gateway (str): the host's default_gateway
description (str): a description.
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
owned (bool): is the host owned or not?
owner (str): an owner for the host
parent (Faraday Object): the host's parent. If you don't know this, leave
on None.
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
command_id=command_id,
ip=ip, os=os,
default_gateway=default_gateway,
owned=owned,
metadata=metadata,
owner=owner,
parent=parent,
description=description,
hostnames=hostnames,
type="Host")
def create_service(workspace_name, command_id, name, description, ports, parent,
owned=False, owner="", protocol="", status="", version="",
metadata=None):
"""Creates a service.
Args:
workspace_name (str): the name of the workspace where the service will be saved.
id (str): the id of the service. Must be unique.
name (str): the host's name
description (str): a description.
ports ([str]): a list of ports for the service.
owned (bool): is the service owned or not?
owner (str): an owner for the service
protocol (str): the service's protocol
status (str): the service's status
version (str): the service's version
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command_id=command_id,
name=name,
parent=parent,
description=description,
ports=ports,
owned=owned,
owner=owner,
protocol=protocol,
status=status,
version=version,
type="Service",
metadata=metadata)
def update_service(workspace_name, command_id, id, name, description, ports,
parent, owned=False, owner="", protocol="", status="",
version="", metadata=None):
"""Creates a service.
Args:
workspace_name (str): the name of the workspace where the service will be saved.
id (str): the id of the service. Must be unique.
name (str): the service's name
description (str): a description.
ports ([str]): a list of ports for the service.
owned (bool): is the host owned or not?
owner (str): an owner for the service
protocol (str): the service's protocol
status (str): the service's status
version (str): the service's version
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
parent=parent,
command_id=command_id,
name=name,
description=description,
ports=ports,
owned=owned,
owner=owner,
protocol=protocol,
status=status,
version=version,
type="Service",
metadata=metadata)
def create_vuln(workspace_name, command_id, name, description, parent, parent_type,
owned=None, owner="", confirmed=False,
resolution="", data="", refs=None, severity="info",
desc="", metadata=None, status=None, policyviolations=[]):
"""Creates a vuln.
Args:
workspace_name (str): the name of the workspace where the vuln will be saved.
id (str): the id of the vuln. Must be unique.
name (str): the vuln's name
description (str): a description.
owned (bool): is the vuln owned or not?
owner (str): an owner for the vuln
confirmed (bool): is the vulnerability confirmed?
data (str): any aditional data about the vuln
refs ([str]): references for the vulnerability
severity (str): a string indicating the vuln's severity. can be 'info',
'low', 'med', 'high', 'critical'
resolution (str): the vuln's possible resolution
desc (str): a vuln's description.
status (str): the service's status
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
policyviolations (lst) : the policy violations
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command_id=command_id,
name=name,
description=description,
parent=parent,
parent_type=parent_type,
owned=owned,
owner=owner,
confirmed=confirmed,
data=data,
refs=refs,
severity=severity,
resolution=resolution,
desc=desc,
type="Vulnerability",
status=status,
metadata=metadata,
policyviolations=policyviolations)
def update_vuln(workspace_name, command_id, id, name, description, parent,
parent_type, owned=None, owner="", confirmed=False, data="",
refs=None, severity="info", resolution="", desc="",
metadata=None, status=None, policyviolations=[]):
"""Updates a vuln.
Args:
workspace_name (str): the name of the workspace where the host will be saved.
id (str): the id of the host. Must be unique.
name (str): the host's name
description (str): a description.
owned (bool): is the vuln owned or not?
owner (str): an owner for the vuln
confirmed (bool): is the vulnerability confirmed?
data (str): any aditional data about the vuln
refs ([str]): references for the vulnerability
severity (str): a string indicating the vuln's severity. can be 'info',
'low', 'med', 'high', 'critical'
resolution (str): the vuln's possible resolution
desc (str): a vuln's description.
status (str): the service's status
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
policyviolations (lst) : the policy violations
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
parent=parent,
parent_type=parent_type,
command_id=command_id,
name=name,
description=description,
owned=owned,
owner=owner,
confirmed=confirmed,
data=data,
refs=refs,
severity=severity,
resolution=resolution,
desc=desc,
type="Vulnerability",
status=status,
metadata=metadata,
policyviolations=policyviolations)
def create_vuln_web(workspace_name, command_id, name, description, parent,
parent_type, owned=None, owner="", confirmed=False,
data="", refs=None, severity="info", resolution="",
desc="", metadata=None, method=None, params="",
path=None, pname=None, query=None, request=None,
response=None, category="", website=None,
status=None, policyviolations=[]):
"""Creates a vuln web.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
description (str): a description.
owner (str): an owner for the host
confirmed (bool): is the vulnerability confirmed?
data (str): any aditional data about the vuln
refs ([str]): references for the vulnerability
severity (str): a string indicating the vuln's severity. can be 'info',
'low', 'med', 'high', 'critical'
resolution (str): the vuln's possible resolution
desc (str): a vuln's description.
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
method (str): the web vuln method
params (str): the parameters for the web vuln
path (str): the web vuln's path
query (str): the web vuln's query
request (str): the web vuln's request
response (str): the web vuln's response
category (str): a category for the web vuln's
website (str): the website where the vuln was found
status (str): the web vulns's status
policyviolations (lst) : the policy violations
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
parent=parent,
parent_type=parent_type,
command_id=command_id,
name=name,
description=description,
owned=owned,
owner=owner,
confirmed=confirmed,
data=data,
refs=refs,
severity=severity,
resolution=resolution,
desc=desc,
metadata=metadata,
method=method,
params=params,
path=path,
pname=pname,
query=query,
request=request,
response=response,
website=website,
category=category,
status=status,
type='VulnerabilityWeb',
policyviolations=policyviolations)
def update_vuln_web(workspace_name, command_id, id, name, description,
parent, parent_type, owned=None, owner="",
confirmed=False, data="", refs=None, severity="info", resolution="",
desc="", metadata=None, method=None, params="", path=None, pname=None,
query=None, request=None, response=None, category="", website=None,
status=None, policyviolations=[]):
"""Creates a vuln web.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
description (str): a description.
owner (str): an owner for the host
confirmed (bool): is the vulnerability confirmed?
data (str): any aditional data about the vuln
refs ([str]): references for the vulnerability
severity (str): a string indicating the vuln's severity. can be 'info',
'low', 'med', 'high', 'critical'
resolution (str): the vuln's possible resolution
desc (str): a vuln's description.
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
method (str): the web vuln method
params (str): the parameters for the web vuln
path (str): the web vuln's path
query (str): the web vuln's query
request (str): the web vuln's request
response (str): the web vuln's response
category (str): a category for the web vuln's
website (str): the website where the vuln was found
status (str): the web vulns's status
policyviolations (lst) : the policy violations
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
parent=parent,
parent_type=parent_type,
command_id=command_id,
name=name,
description=description,
owned=owned,
owner=owner,
confirmed=confirmed,
data=data,
refs=refs,
severity=severity,
resolution=resolution,
desc=desc,
metadata=metadata,
method=method,
params=params,
path=path,
pname=pname,
query=query,
request=request,
response=response,
website=website,
category=category,
status=status,
type='VulnerabilityWeb',
policyviolations=policyviolations)
def create_note(workspace_name, command_id, object_type, object_id, name, text, owned=None, owner="",
description="", metadata=None):
"""Creates a note.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
text (str): the note's text
owned (bool): is the note owned?
owner (str): the note's owner
description (str): a description
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command_id=command_id,
object_id=object_id,
object_type=object_type,
name=name,
description=description,
owned=owned,
owner=owner,
text=text,
type="Note",
metadata=metadata)
def update_note(workspace_name, command_id, id, name, text,
object_type, object_id, owned=None,
owner="", description="", metadata=None):
"""Updates a note.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
text (str): the note's text
owned (bool): is the note owned?
owner (str): the note's owner
description (str): a description
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
object_id=object_id,
object_type=object_type,
command_id=command_id,
name=name,
description=description,
owned=owned,
owner=owner,
text=text,
type="Note",
metadata=metadata)
def create_credential(workspace_name, command_id, name, username, password,
parent, parent_type, owned=None, owner="",
description="", metadata=None):
"""Creates a credential.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
username (str)
password (str)
owned (bool): is the note owned?
owner (str): the note's owner
description (str): a description
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command_id=command_id,
parent=parent,
parent_type=parent_type,
name=name,
description=description,
owned=owned,
owner=owner,
metadata=metadata,
username=username,
password=password,
type="Cred")
def update_credential(workspace_name, command_id, id, name, username, password,
parent, parent_type, owned=None, owner="",
description="", metadata=None):
"""Updates a credential.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
name (str): the vuln web's name
username (str)
password (str)
owned (bool): is the note owned?
owner (str): the note's owner
description (str): a description
metadata: a collection of metadata. If you don't know the metada. leave
on None, it will be created automatically.
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
id,
parent=parent,
parent_type=parent_type,
command_id=command_id,
name=name,
description=description,
owned=owned,
owner=owner,
metadata=metadata,
username=username,
password=password,
type="Cred")
def create_command(workspace_name, command, tool, import_source, duration=None, hostname=None,
ip=None, itime=None, params=None, user=None):
"""Creates a command.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
command (str): the command to be created
duration (str). the command's duration
hostname (str): the hostname where the command was executed
ip (str): the ip of the host where the command was executed
itime (str): the time it took to run
params (str): the parameters given
user (str): the user that ran the command
Returns:
A dictionary with the server's response.
"""
return _save_to_server(workspace_name,
command=command,
tool=tool,
import_source=import_source,
duration=duration,
hostname=hostname,
ip=ip,
itime=itime,
params=params,
user=user,
workspace=workspace_name,
type="CommandRunInformation")
def update_command(workspace_name, command_id, command, tool, import_source
,duration=None, hostname=None,
ip=None, itime=None, params=None, user=None):
"""Updates a command.
Args:
workspace_name (str): the name of the workspace where the vuln web will be saved.
id (str): the id of the vuln web. Must be unique.
command (str): the command to be created
duration (str). the command's duration
hostname (str): the hostname where the command was executed
ip (str): the ip of the host where the command was executed
itime (str): the time it took to run
params (str): the parameters given
user (str): the user that ran the command
Returns:
A dictionary with the server's response.
"""
return _update_in_server(workspace_name,
command_id,
command=command,
tool=tool,
import_source=import_source,
duration=duration,
hostname=hostname,
ip=ip,
itime=itime,
params=params,
user=user,
workspace=workspace_name,
type="CommandRunInformation")
def create_workspace(workspace_name, description, start_date, finish_date,
customer=None, duration=None):
"""Create a workspace.
Args:
workspace_name (str): the workspace's name
description (str): a description for the worksapce
start_date (str): a date to represent when work began in the workspace
finish_date (str): a date to represent when work will be finished on the workspace
customer (str): the customer for which we are creating the workspace
Returns:
A dictionary with the server's response.
"""
if duration is None:
duration = {"start": start_date, "end": finish_date}
return _save_db_to_server(workspace_name,
name=workspace_name,
description=description,
customer=customer,
duration=duration,
type="Workspace")
def delete_host(workspace_name, host_id):
"""Delete host of id host_id from the database."""
return _delete_from_server(workspace_name, 'Host', host_id)
def delete_service(workspace_name, service_id):
"""Delete service of id service_id from the database."""
return _delete_from_server(workspace_name, 'Service', service_id)
def delete_vuln(workspace_name, vuln_id):
"""Delete vuln of id vuln_id from the database."""
return _delete_from_server(workspace_name, 'Vulnerability', vuln_id)
def delete_note(workspace_name, note_id):
"""Delete note of id note_id from the database."""
return _delete_from_server(workspace_name, note_id)
def delete_credential(workspace_name, credential_id):
"""Delete credential of id credential_id from the database."""
return _delete_from_server(workspace_name, 'Credential', credential_id)
def delete_command(workspace_name, command_id):
"""Delete command of id command_id from the database."""
return _delete_from_server(workspace_name, 'Command', command_id)
def delete_workspace(workspace_name):
"""Delete the couch database of id workspace_name"""
db_url = _create_server_db_url(workspace_name)
return _delete(db_url, database=True)
def server_info():
"""Return server info if we can stablish a connection with the server,
None otherwise.
"""
try:
return _get("{0}/info".format(_create_server_api_url()))
except:
return None
def login_user(uri, uname, upass):
auth = {"email": uname, "password": upass}
try:
resp = requests.post(urlparse.urljoin(uri, "/_api/login"), json=auth)
if resp.status_code == 401:
return None
else:
return resp.cookies
except requests.adapters.ConnectionError:
return None
except requests.adapters.ReadTimeout:
return None
def is_authenticated(uri, cookies):
try:
resp = requests.get(urlparse.urljoin(uri, "/_api/session"), cookies=cookies, timeout=1)
if resp.status_code != 403:
user_info = resp.json()
return bool(user_info.get('username', {}))
else:
return False
except requests.adapters.ConnectionError:
return False
except requests.adapters.ReadTimeout:
return False
def check_faraday_version():
"""Raise RuntimeError if client and server aren't running the same version"""
info = server_info()
faraday_directory = os.path.dirname(os.path.realpath('faraday.py'))
file_path = os.path.join(faraday_directory, 'VERSION')
with open(file_path, 'r') as version_file:
version = version_file.read().strip()
if info is not None and version != info['Version']:
raise RuntimeError('Client and server versions do not match')
def check_server_url(url_to_test):
"""Return True if the url_to_test is indeed a valid Faraday Server URL.
False otherwise.
"""
try:
resp = _get("{0}/_api/v2/info".format(url_to_test))
return 'Faraday Server' in resp
except Exception as ex:
logger.exception(ex)
test_okey = False
return test_okey
def get_user_info():
try:
resp = requests.get(urlparse.urljoin(_get_base_server_url(), "/_api/session"), cookies=_conf().getDBSessionCookies(), timeout=1)
if (resp.status_code != 401) and (resp.status_code != 403):
return resp.json()
else:
return False
except requests.adapters.ConnectionError:
return False
except requests.adapters.ReadTimeout:
return False