Skip to content

Create your own plugins

Martijn Veken edited this page May 5, 2023 · 7 revisions

Dettectinator comes with a rich set of plugins for common detection systems and data source platforms, but you can easily add new ones to accomodate your own flow. Dettectinator scans the ./plugins folder for modules containing data import plugins. The class name of the plugin has to be unique within Dettectinator.

Techniques

To create your own Technique plugins create a Python module in the ./plugins folder and a class like the example below:

from plugins.technique_import import TechniqueBase
from argparse import ArgumentParser
from collections.abc import Iterable


class TechniqueCustom(TechniqueBase):
    """
    Demo data import plugin
    """

    def __init__(self, parameters: dict) -> None:
        super().__init__(parameters)
        if 'dummy' not in self._parameters:
            raise Exception('TechniqueCustom: "dummy" parameter is required.')

    @staticmethod
    def set_plugin_params(parser: ArgumentParser) -> None:
        """
        Set command line arguments specific for the plugin
        :param parser: Argument parser
        """
        TechniqueBase.set_plugin_params(parser)

        parser.add_argument('--dummy', help='Dummy parameter', required=True)

    def get_data_from_source(self) -> Iterable:
        """
        Gets the use-case/technique data from the source.
        :return: Iterable, yields technique, detection, applicable_to
        """
        detections = {'T1055': 'Test', 'T1236': 'Another test'}

        for technique, detection in detections.items():
            detection = self._parameters["dummy"] + ": " + detection
            yield technique, detection, None

The custom data import plugin needs to derive from TechniqueBase and the name has to start with Technique. There are two methods that need to be implemented:

set_plugin_params: Call the function in the base class to add the default arguments. Then add arguments like in the example above, you can add as many as you need. When an argument is mandatory set the required argument to True. Validate if the required arguments are present in the __init__ method like in the example above.

get_data_from_source: Use this function to get the data from the source. The function is defined as an Iterable, yield every techique/ detection combination from the source. You can optionally specify a value voor applicable_to as well. This will overrule the value given from the command line.

You can load the plugin by specifying the class name after the -p argument on the commandline.

Datasources

To create your own Datasource plugins create a Python module in the ./plugins folder and a class like the example below:

from plugins.datasources_import import DatasourceBase
from argparse import ArgumentParser
from collections.abc import Iterable


class DatasourceCustom(DatasourceBase):
    """
    Demo data import plugin
    """

    def __init__(self, parameters: dict) -> None:
        super().__init__(parameters)

        if 'dummy' not in self._parameters:
            raise Exception('DatasourceCustom: "dummy" parameter is required.')

    @staticmethod
    def set_plugin_params(parser: ArgumentParser) -> None:
        """
        Set command line arguments specific for the plugin
        :param parser: Argument parser
        """
        parser.add_argument('--dummy', help='Dummy parameter', required=True)

    def get_data_from_source(self) -> Iterable:
        """
        Gets the datasource/product data from the source.
        :return: Iterable, yields datasource, product, applicable_to
        """
        datasources = {'Active Directory Object Access': 'Test',
                       'File Access': 'Another test'}

        for datasource, product in datasources.items():
            product = self._parameters["dummy"] + ": " + detection
            yield datasource, product, None

The custom data import plugin needs to derive from DatasourceBase and the name has to start with Datasource. There are two methods that need to be implemented:

set_plugin_params: Call the function in the base class to add the default arguments. Then add arguments like in the example above, you can add as many as you need. When an argument is mandatory set the required argument to True. Validate if the required arguments are present in the __init__ method like in the example above.

get_data_from_source: Use this function to get the data from the source. The function is defined as an Iterable, yield every technique/ detection combination from the source.

You can load the plugin by specifying the class name after the -p argument on the commandline. You can optionally specify a value for applicable_to as well. This will overrule the value given from the command line.

Groups

To create your own Groups plugins create a Python module in the ./plugins folder and a class like the example below:

from plugins.groups_import import GroupBase
from argparse import ArgumentParser
from collections.abc import Iterable


class GroupCustom(GroupBase):
    """
    Demo data import plugin
    """

    def __init__(self, parameters: dict) -> None:
        super().__init__(parameters)

        if 'dummy' not in self._parameters:
            raise Exception('GroupCustom: "dummy" parameter is required.')

    @staticmethod
    def set_plugin_params(parser: ArgumentParser) -> None:
        """
        Set command line arguments specific for the plugin
        :param parser: Argument parser
        """
        parser.add_argument('--dummy', help='Dummy parameter', required=True)

    def get_data_from_source(self) -> Iterable:
        """
        Retrieves group/technique/software data from the source
        :return: Iterable, yields group, campaign, techniques, software
        """
        data = {'APT1': {'campaign': 'P0wn them all', 'techniques': ['T1566.002', 'T1059.001', 'T1053.005'],
                         'software': ['S0002']}}

        for group, group_data in data.items():
            group_name = self._parameters["dummy"] + ": " + group
            yield group_name, group_data['campaign'], group_data['techniques'], group_data['software']

The custom data import plugin needs to derive from GroupBase and the name has to start with Group. There are two methods that need to be implemented:

set_plugin_params: Call the function in the base class to add the default arguments. Then add arguments like in the example above, you can add as many as you need. When an argument is mandatory set the required argument to True. Validate if the required arguments are present in the __init__ method like in the example above.

get_data_from_source: Use this function to get the data from the source. The function is defined as an Iterable, yield every group, campaign, techniques, software combination from the source.

You can load the plugin by specifying the class name after the -p argument on the commandline. You can optionally specify values for Group (-gr) and Campaign (-ca) as well on the commandline. These values will be used when no value for Group or Campaign has been supplied from the source.

Clone this wiki locally