Syntribos Code Documentation

Configuration

This section describes the configuration specified in the second argument to the runner, your configuration file.

syntribos.config.handle_config_exception(exc)
syntribos.config.list_cli_opts()
syntribos.config.list_logger_opts()
syntribos.config.list_opts()
syntribos.config.list_remote_opts()

Method defining remote URIs for payloads and templates.

syntribos.config.list_syntribos_opts()
syntribos.config.list_test_opts()
syntribos.config.list_user_opts()
syntribos.config.register_opts()
syntribos.config.sub_commands(sub_parser)

Signals

This section describes Signals (syntribos.signal.SynSignal) and SignalHolders (syntribos.signal.SignalHolder).

class syntribos.signal.SynSignal(text='', slug='', strength=0.0, tags=None, data=None, check_name=None)

SynSignal represents a piece of information raised by a ‘check’

Variables:
  • text (str) – A message describing the signal
  • slug (str) – A unique slug that identifies the signal
  • strength (float) – A number from 0 to 1 representing confidence
  • tags (list) – Collection of tags associated with the signal
  • data (dict) – Information about the results of the check
matches_slug(slug)

Checks if a Signal has a given slug

Parameters:slug (str) – Slug to search for
Return type:bool
Returns:True if fuzzy match, else False
matches_tag(tag)

Checks if a Signal has a given tag

Parameters:tag (str) – Tag to search for
Return type:bool
Returns:True if fuzzy match, else False
class syntribos.signal.SignalHolder(signals=None)

SignalHolder represents a ‘set’ of SynSignals.

Variables:
  • signals (list) – Collection of SynSignal
  • all_slugs (list) – Collection of slugs in signals for fast search
__contains__(item)

This is used to search for signals in the ‘if __ in __’ pattern.

__init__(signals=None)

The SignalHolder can be initialized with a set of signals

Parameters:signals (SynSignal OR SignalHolder OR list) – Collection of signals (added with self.register())
compare(other)

Returns a dict with details of diff between 2 SignalHolders.

Param:

signal_holder1

Ptype:
class:Syntribos.signal.SignalHolder
Param:

signal_holder2

Ptype:
class:Syntribos.signal.SignalHolder
Returns:

data

Return type:

dict:

find(slugs=None, tags=None)

Get the signals that are matched by slugs and/or tags

Parameters:
  • slugs (list) – A list of slugs to search for
  • tags (list) – A list of tags to search for
Return type:

class

Returns:

A SignalHolder of matched SynSignal

register(signals)

Add a signal/list of signals to the SignalHolder

Maintains a set (won’t add signal if its slug is in self.all_slugs)

Parameters:signals (SynSignal OR list OR SynHolder) – A single SynSignal, or a collection of them

Checks

This section describes the checks, which analyze the HTTP response and returns a signal if it detects something that it knows about. It’s intended to make it easier to inspect HTTP responses.

syntribos.checks.content_validity.valid_content(test)

Checks if the response.content is valid.

Checks if the response.content is either xml or json and returns a signal based on if the content is valid or not.

Returns:SynSignal
syntribos.checks.fingerprint.remote_os(test)

Returns remote OS info.

Tries to identity which OS is running on the remote server

Returns:SynSignal
syntribos.checks.fingerprint.server_software(test)

Fingerprints the server and possible version.

Reads response headers and if server software information is present, returns a signal with server software slug.

Returns:SynSignal
syntribos.checks.http.check_content_type(response)

Returns a signal with info about a response’s content type

Parameters:response (requests.Response) –
Returns:Signal with content type info
Return type:syntribos.signal.SynSignal
syntribos.checks.http.check_fail(exception)

Checks for a requestslib exception, returns a signal if found.

If this Exception is an instance of requests.exceptions.RequestException, determine what kind of exception was raised. If not, return the results of from_generic_exception.

Parameters:exception (Exception) – An Exception object
Returns:Signal with exception details
Return type:syntribos.signal.SynSignal
syntribos.checks.http.check_status_code(response)

Returns a signal with info about a response’s HTTP status code

Parameters:response (requests.Response) – A Response object
Returns:Signal with status code details
Return type:syntribos.signal.SynSignal
syntribos.checks.length.max_body_length(test)

Checks if the response body length is more than max size in the config.

Checks the response body to see if the length is more than the given length
in the config. If it is, returns a Signal.
Returns:SynSignal or None
syntribos.checks.length.percentage_difference(test)

Validates length of two responses

Compares the length of a fuzzed response with a response to the baseline request. If the response is longer than expected, returns a LengthPercentageDiffSignal

Returns:SynSignal or None
syntribos.checks.ssl.https_check(test)

Checks if the returned response consists of non-secure endpoint URIs

Returns:syntribos.signal.SynSignal
syntribos.checks.stacktrace.stacktrace(test)

Checks if a stacktrace is returned by the response.

If a stacktrace is returned, attempts to identity if it was an application failure or a server failure and return appropriate tags.

returns a signal with the stacktrace slug.

Returns:SynSignal
syntribos.checks.string.has_string(test)

Checks if the response consists of any failure strings

Returns:syntribos.signal.SynSignal
syntribos.checks.time.absolute_time(test)

Checks response takes less than config.max_time seconds

Returns:SynSignal or None
syntribos.checks.time.percentage_difference(test)

Validates time taken for two responses

Compares the elapsed time of a fuzzed response with a response to the baseline request. If the response takes longer than expected, returns a TimePercentageDiffSignal

Returns:SynSignal or None

Tests

This section describes the components involved with writing your own tests with syntribos.

All syntribos tests inherit from syntribos.tests.base.BaseTestCase, either directly, or through a subclass such as syntribos.tests.fuzz.base_fuzz.BaseFuzzTestCase.

All tests are aggregated in the syntribos.tests.base.test_table variable.

syntribos.tests.base.ALLOWED_CHARS = '().-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

test_table is the master list of tests to be run by the runner

class syntribos.tests.base.BaseTestCase(methodName='runTest')

Bases: unittest.case.TestCase

Base class for building new tests

Attribute str test_name:
 A name like XML_EXTERNAL_ENTITY_BODY, containing the test type and the portion of the request template being tested
Attribute list failures:
 A collection of “failures” raised by tests
Attribute bool dead:
 Flip this if one of the requests doesn’t return a response object
Attribute client:
 HTTP client to be used by the test
Attribute init_req:
 Initial request (loaded from request template)
Attribute init_resp:
 Response to the initial request
Attribute test_req:
 Request sent by the test for analysis
Attribute test_resp:
 Response to the test request
Attribute init_signals:
 Holder for signals on init_req
Attribute test_signals:
 Holder for signals on test_req
Attribute diff_signals:
 Holder for signals between init_req and test_req
client = <syntribos.clients.http.client.SynHTTPClient object>
classmethod create_init_request(filename, file_content, meta_vars)

Parses template and creates init request object

This method does not send the initial request, instead, it only creates the object for use in the debug test

Parameters:
  • filename (str) – name of template file
  • file_content (str) – content of template file as string
dead = False
diff_signals = [""]
errors = []
classmethod extend_class(new_name, kwargs)

Creates an extension for the class

Each TestCase class created is added to the test_table, which is then read in by the test runner as the master list of tests to be run.

Parameters:
  • new_name (str) – Name of new class to be created
  • kwargs (dict) – Keyword arguments to pass to the new class
Return type:

class

Returns:

A TestCase class extending BaseTestCase

failures = []
classmethod get_test_cases(filename, file_content)

Returns tests for given TestCase class (overwritten by children).

init_req = None
init_resp = None
init_signals = [""]
register_issue(defect_type, severity, confidence, description)

Adds an issue to the test’s list of issues

Creates a syntribos.issue.Issue object, with given function parameters as instances variables, and registers the issue as a failure and associates the test’s metadata to it.

Parameters:defect_type – The type of vulnerability that Syntribos believes

it has found. This may be something like 500 error or DoS, regardless tof whathe Test Type is. :param severity: “Low”, “Medium”, or “High”, depending on the defect :param description: Description of the defect :param confidence: The confidence of the defect :returns: new issue object with metadata associated :rtype: Issue

classmethod register_opts()
run_test_case()

This kicks off the test(s) for a given TestCase class

After running the tests, an AssertionError is raised if any tests were added to self.failures.

Raises:AssertionError
classmethod send_init_request(filename, file_content, meta_vars)

Parses template, creates init request object, and sends init request

This method sends the initial request, which is the request created after parsing the template file. This request will not be modified any further by the test cases themselves.

Parameters:
  • filename (str) – name of template file
  • file_content (str) – content of template file as string
classmethod tearDown()
classmethod tearDownClass()
test_case()

This method is overwritten by individual TestCase classes

It represents the actual test that is called in run_test_case(), and handles populating self.failures

test_name = None
test_req = None
test_resp = None
test_signals = [""]
class syntribos.tests.base.TestType

Bases: type

This is the metaclass for each class extending BaseTestCase.

syntribos.tests.base.replace_invalid_characters(string, new_char='_')

Replace invalid characters in test names

This function corrects string so the following is true.

Identifiers (also referred to as names) are described by the following lexical definitions:

identifier ::=  (letter|"_") (letter | digit | "_")*
letter     ::=  lowercase | uppercase
lowercase  ::=  "a"..."z"
uppercase  ::=  "A"..."Z"
digit      ::=  "0"..."9"
Parameters:
  • string (str) – Test name
  • new_char (str) – The character to replace invalid characters with
Returns:

The test name, with invalid characters replaced with new_char

Return type:

str

syntribos.tests.fuzz.datagen.fuzz_request(req, strings, fuzz_type, name_prefix)

Creates the fuzzed RequestObject

Gets the name and the fuzzed request model from _fuzz_data, and creates a RequestObject from the parameters of the model.

Parameters:
  • req (syntribos.clients.http.parser.RequestObject) – The RequestObject to be fuzzed
  • strings (list) – List of strings to fuzz with
  • fuzz_type (str) – What attribute of the RequestObject to fuzz
  • name_prefix – (Used for ImpactedParameter)
Returns:

Generator of tuples: (name, request, fuzzstring, ImpactedParameter name)

Return type:

tuple

Issues

This section describes the representation of issues that are uncovered by syntribos.

class syntribos.issue.Issue(defect_type, severity, description, confidence, request=None, response=None, impacted_parameter=None, init_signals=[], test_signals=[], diff_signals=[])

Bases: object

Object that encapsulates a security vulnerability

This object is designed to hold the metadata associated with a vulnerability.

Variables:
  • defect_type – The type of vulnerability that Syntribos believes it has found. This may be something like 500 error or DoS, regardless of what the Test Type is.
  • severity – “Low”, “Medium”, or “High”, depending on the defect
  • description – Description of the defect
  • confidence – The confidence of the defect
  • request – The request object sent that generated this defect
  • response – The response object returned after sending the request
  • target – A hostname/IP/etc. to be tested
  • path – A specific REST API method, i.e. a URL path associated with a Target.
  • test_type – The type of vulnerability that is being tested for. This is not necessarily the same as the Defect Type, which may be something like 500 error or DoS.
  • content_type – The content-type of the unmodified request
  • impacted_parameter – For fuzz tests only, a syntribos.tests.fuzz.base_fuzz.ImpactedParameter that holds data about what part of the request was affected by the fuzz test.
as_dict()

Convert the issue to a dict of values for outputting.

Return type:dict
Returns:dictionary of issue data
get_details()

Returns the most relevant information needed for output.

Return type:dict
Returns:dictionary of issue details
request_as_dict(req)

Convert the request object to a dict of values for outputting.

Parameters:req – The request object
Return type:dict
Returns:dictionary of HTTP request data
response_as_dict(res)

Convert the response object to a dict of values for outputting.

Parameters:res – The result object
Return type:dict
Returns:dictionary of HTTP response data

Results

This section describes the representation of results (collections of issues) from a given syntribos run.

class syntribos.result.IssueTestResult(stream, descriptions, verbosity)

Bases: unittest.runner.TextTestResult

Custom unnittest results holder class

This class aggregates syntribos.issue.Issue objects from all the tests as they run

addError(test, err)

Duplicates parent class addError functionality.

Parameters:
addFailure(test, err)

Adds issues to data structures

Appends issues to the result’s list of failures, as well as updates the stats for the result. Each failure in the list of failures takes the form:

{
  "url": "host.com/blah",
  "type": "500_error",
  "description": "500 errors r bad, mkay?",
  "failure_id": 1234,
  "instances": [
    {
      "confidence": "HIGH",
      "param": {
        "location": "headers",
        "method": "POST",
        "variables": [
          "Content-Type"
        ]
      },
      "strings": [
        "derp"
      ],
      "severity": "LOW",
      "signals": {
        "diff_signals": [],
        "init_signals": [],
        "test_signals": []
      }
    }
  ]
}
Parameters:
addSuccess(test)

Duplicates parent class addSuccess functionality.

Parameters:test (syntribos.tests.base.BaseTestCase) – The test that was run
failure_id = 0
output = {'errors': [], 'stats': {'severity': {'MEDIUM': 0, 'LOW': 0, 'UNDEFINED': 0, 'HIGH': 0}}, 'failures': {}}
printErrors(output_format)

Print out each syntribos.issue.Issue that was encountered

Parameters:output_format (str) – Either “json” or “xml”
print_log_path_and_stats(start_time)

Print the path to the log folder for this run.

print_result(start_time)

Prints test summary/stats (e.g. # failures) to stdout.

severity_counter_dict = {}
stats = {'errors': 0, 'successes': 0, 'unique_failures': 0}
testsRunSinceLastPrint = 0

HTTP Requests

This section describes the components related to generating, fuzzing, and making HTTP requests.

class syntribos.clients.http.client.SynHTTPClient

Bases: syntribos.clients.http.base_http_client.HTTPClient

This is the basic HTTP client used by Syntribos.

It aliases send_request to request so logging/exception handling is done in one place, for all requests. Also checks for bad HTTP status codes and adds a signal if one is found.

request(method, url, headers=None, params=None, data=None, sanitize=False, requestslib_kwargs=None)

Sends a request (passes to requests.request)

Parameters:
  • method (str) – Request method
  • url (str) – URL to request
  • headers (dict) – Dictionary of headers in name:value format
  • params (dict) – Dictionary of params in name:value format
  • data (dict) – Data to send as part of request body
  • requestslib_kwargs (dict) – Keyword arguments to pass to requests
Returns:

tuple of (response, signals)

send_request(request_obj)

This sends a request based on a RequestObject.

RequestObjects are generated by a parser (e.g. syntribos.clients.http.parser.RequestCreator) from request template files, and passed to this method to send the request.

Parameters:request_obj (syntribos.clients.http.parser.RequestObject) – A RequestObject generated by a parser
Returns:tuple of (response, signals)
class syntribos.clients.http.parser.RequestCreator

Bases: object

ACTION_FIELD = 'ACTION_FIELD:'
EXTERNAL = 'CALL_EXTERNAL\\|([^:]+?):([^:]+?):([^|]+?)\\|'
FUNC_NO_ARGS = '([^:]+):([^:]+)'
FUNC_WITH_ARGS = '([^:]+):([^:]+):(\\[.+\\])'
METAVAR = '(\\|[^\\|]*\\|)'
classmethod call_external_functions(string)

Parse external function calls in the body of request templates

Parameters:string (str) – full HTTP request template as a string
Return type:str
Returns:the request, with EXTERNAL calls filled in with their values or UUIDs
classmethod call_one_external_function(string, args)

Calls one function read in from templates and returns the result.

classmethod create_request(string, endpoint, meta_vars=None)

Parse the HTTP request template into its components

Parameters:
  • string (str) – HTTP request template
  • endpoint (str) – URL of the target to be tested
  • meta_vars (dict) – Default None, dict parsed from meta.json
Return type:

syntribos.clients.http.parser.RequestObject

Returns:

RequestObject with method, url, params, etc. for use by runner

classmethod replace_one_variable(var_obj)

Evaluate a VariableObject according to its type

A meta variable’s type is optional. If a type is given, the parser will interpret the variable in one of 3 ways according to its type, and returns that value.

  • Type config: The parser will attempt to read the config value specified by the “val” attribute and returns that value.
  • Type function: The parser will call the function named in the “val” attribute with arguments given in the “args” attribute, and returns the value from calling the function. This value is cached, and will be returned on subsequent calls.
  • Type generator: works the same way as the function type, but its results are not cached and the function will be called every time.

Otherwise, the parser will interpret the variable as a static variable, and will return whatever is in the “val” attribute.

Parameters:var_obj – A syntribos.clients.http.parser.VariableObject
Returns:The evaluated value according to its meta variable type
class syntribos.clients.http.parser.RequestHelperMixin

Bases: object

Class that helps with fuzzing requests.

get_copy()
get_prepared_copy()

Create a copy of self, and prepare it for use by a fuzzer

Returns:Copy of request object that has been prepared for sending
Return type:RequestHelperMixin
prepare_request()

Prepare a request for sending off

It should be noted this function does not make a request copy, destroying iterators in request. A copy should be made if making multiple requests.

class syntribos.clients.http.parser.RequestObject(method, url, action_field=None, headers=None, params=None, data=None, sanitize=False)

Bases: syntribos.clients.http.parser.RequestHelperMixin

An object that holds information about an HTTP request.

Variables:
  • method (str) – Request method
  • url (str) – URL to request
  • action_field (dict) – Action Fields
  • headers (dict) – Dictionary of headers in name:value format
  • params (dict) – Dictionary of params in name:value format
  • data – Data to send as part of request body
  • sanitize (bool) – Boolean variable used to filter secrets
exception syntribos.clients.http.parser.TemplateParseException

Bases: exceptions.Exception

class syntribos.clients.http.parser.VariableObject(name, var_type='', args=[], val='', fuzz=True, fuzz_types=[], min_length=0, max_length=9223372036854775807, url_encode=False, prefix='', suffix='', **kwargs)

Bases: object

FUZZ_TYPES = ['int', 'ascii', 'url']
VAR_TYPES = ['function', 'generator', 'config']

Extensions

This section describes syntribos extensions, which are called by the CALL_EXTERNAL field in the request template.

class syntribos.extensions.identity.models.base.BaseIdentityModel(kwargs)

Bases: object

static _build_list(items, element=None)

Builds json object or xml element from model

Calls either item._obj_to_dict() or item.obj_to_xml_ele() on all objects in items, and either returns the dict objects as a list or appends items to element

Parameters:
  • items – list of objects for conversion
  • element – The element to be appended, or None if json
Returns:

list of dicts if element is None or element otherwise.

static _build_list_model(data, field_name, model)

Builds list of python objects from XML or json data

If data type is json, will find all json objects with field_name as key, and convert them into python objects of type model. If XML, will find all xml.etree.ElementTree.Element with field_name as tag, and convert them into python objects of type model

Parameters:
  • data – Either json or XML object
  • field_name (str) – json key or XML tag
  • model – Class of objects to be returned
Returns:

list of model objects

Return type:

list

static _create_text_element(name, text)

Creates element with text data

Returns:new element with name name and text text
Return type:xml.etree.ElementTree.Element
static _find(element, tag)

Finds element with tag

Parameters:
  • elementxml.etree.ElementTree.Element, the element through which to start searching
  • tag – the tag to search for
Returns:

The element with tag tag if found, or a new element with tag None if not found

Return type:

xml.etree.ElementTree.Element

static _get_sub_model(model, json=True)

Converts object to json or XML

Parameters:
  • model – Object to convert
  • json (boolean) – True if converting to json, false if XML
classmethod _json_to_obj(serialized_str)
_namespaces

alias of Namespaces

_obj_to_dict()
_obj_to_json()
_obj_to_xml()
_obj_to_xml_ele()
classmethod _remove_empty_values(data)

Remove empty values

Returns a new dictionary based on ‘dictionary’, minus any keys with values that evaluate to False.

Parameters:data (dict) – Dictionary to be pruned
Returns:dictionary without empty values
Return type:dict
classmethod _remove_xml_namespaces(element)

Prunes namespaces from XML element

Parameters:element – element to be trimmed
Returns:element with namespaces trimmed
Return type:xml.etree.ElementTree.Element
classmethod _xml_to_obj(serialized_str, encoding='iso-8859-2')
classmethod deserialize(serialized_str, format_type)
serialize(format_type)
class syntribos.extensions.identity.models.base.Namespaces

Bases: object

XMLNS = 'http://docs.openstack.org/identity/api/v2.0'
XMLNS_KSKEY = 'http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0'
XMLNS_RAX_AUTH = 'http://docs.rackspace.com/identity/api/ext/RAX-AUTH/v1.0'
XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'