def setUp(self):
        self.flt = DataObjectFilter()
        self.psr = PynspectFilterParser()
        self.psr.build()
        self.cpl = IDEAFilterCompiler()

        self.msg_idea = lite.Idea(self.test_msg1)
    def test_06_shortcuts(self):
        """
        Perform tests of shortcut methods.
        """
        self.maxDiff = None

        # Let the shortcut method initialize everything.
        flt = DataObjectFilter(
            parser   = PynspectFilterParser,
            compiler = IDEAFilterCompiler
        )
        rule = flt.prepare('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")
        self.assertEqual(self.check_rule(rule), True)

        # Create parser and compiler instances by hand, but register them into filter.
        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()
        flt = DataObjectFilter(
            parser   = psr,
            compiler = cpl
        )
        rule = flt.prepare('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")
        self.assertEqual(self.check_rule(rule), True)
Example #3
0
    def __init__(self, rule, actions, addrGroups, parser=None, compiler=None):
        # Check is we got parser instance
        if parser is None:
            self.parser = PynspectFilterParser()
            self.parser.build()
        else:
            self.parser = parser

        # Check is we got compiler instance
        if compiler is None:
            self.compiler = IDEAFilterCompiler()
        else:
            self.compiler = compiler

        # Instantiate filter
        self.__filter = DataObjectFilter()

        # Store rule condition in raw form
        self.__conditionRaw = rule["condition"]

        if not self.__matchvar(rule["condition"], addrGroups):
            self.__condition = self.__conditionRaw

        # Set inner rule ID
        self.id = rule["id"]

        if (self.__condition != None):
            self.parseRule()

        self.__actions = list()
        self.__elseactions = list()

        # Associate actions
        if "actions" in rule:
            for actionId in rule["actions"]:
                try:
                    logger.debug("Rule %s inserting %s", self.id, actionId)
                    self.__actions.append(actions[actionId])
                except KeyError as e:
                    raise Exception("Missing action with ID " + str(e))

        # Associate elseactions
        if "elseactions" in rule:
            for actionId in rule["elseactions"]:
                try:
                    self.__elseactions.append(actions[actionId])
                except KeyError as e:
                    raise Exception("Missing elseaction with ID " + str(e))
Example #4
0
    def test_03_idea_time_compilations(self):
        """
        Perform IDEA datetime compilation tests.
        """
        self.maxDiff = None

        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()

        rule = psr.parse('(DetectTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('DetectTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(CreateTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('CreateTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('CreateTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(EventTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('EventTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('EventTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(CeaseTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('CeaseTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('CeaseTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(WinStartTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('WinStartTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('WinStartTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(WinEndTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('WinEndTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('WinEndTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")
Example #5
0
    def test_05_idea_func_compilations(self):
        """
        Perform IDEA function compilation tests.
        """
        self.maxDiff = None

        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()

        rule = psr.parse('(DetectTime < utcnow())')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_LT FUNCTION(utcnow()))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('DetectTime') OP_LT FUNCTION(utcnow()))")

        rule = psr.parse('(DetectTime < (utcnow() - 3600))')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_LT MATHBINOP(FUNCTION(utcnow()) OP_MINUS INTEGER(3600)))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('DetectTime') OP_LT MATHBINOP(FUNCTION(utcnow()) OP_MINUS TIMEDELTA(datetime.timedelta(0, 3600))))")

        rule = psr.parse('(DetectTime + 3600) > utcnow()')
        self.assertEqual(repr(rule), "COMPBINOP(MATHBINOP(VARIABLE('DetectTime') OP_PLUS INTEGER(3600)) OP_GT FUNCTION(utcnow()))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(MATHBINOP(VARIABLE('DetectTime') OP_PLUS TIMEDELTA(datetime.timedelta(0, 3600))) OP_GT FUNCTION(utcnow()))")
Example #6
0
class Rule():
    def __init__(self, rule, actions, addrGroups, parser=None, compiler=None):
        # Check is we got parser instance
        if parser is None:
            self.parser = PynspectFilterParser()
            self.parser.build()
        else:
            self.parser = parser

        # Check is we got compiler instance
        if compiler is None:
            self.compiler = IDEAFilterCompiler()
        else:
            self.compiler = compiler

        # Instantiate filter
        self.__filter = DataObjectFilter()

        # Store rule condition in raw form
        self.__conditionRaw = rule["condition"]

        if not self.__matchvar(rule["condition"], addrGroups):
            self.__condition = self.__conditionRaw

        # Set inner rule ID
        self.id = rule["id"]

        if (self.__condition != None):
            self.parseRule()

        self.__actions = list()
        self.__elseactions = list()

        # Associate actions
        if "actions" in rule:
            for actionId in rule["actions"]:
                try:
                    logger.debug("Rule %s inserting %s", self.id, actionId)
                    self.__actions.append(actions[actionId])
                except KeyError as e:
                    raise Exception("Missing action with ID " + str(e))

        # Associate elseactions
        if "elseactions" in rule:
            for actionId in rule["elseactions"]:
                try:
                    self.__elseactions.append(actions[actionId])
                except KeyError as e:
                    raise Exception("Missing elseaction with ID " + str(e))

    def parseRule(self):
        cond = str(self.__condition).lower()
        if cond in ["none", "null", "true"]:
            self.__condition = True
        elif cond == "false":
            self.__condition = False
        else:
            try:
                self.__condition = self.parser.parse(self.__condition)
                self.__condition = self.compiler.compile(self.__condition)
            except Exception as e:
                print(
                    "Error while parsing condition: {0}\nOriginal exception: {1}"
                    .format(self.__condition, e))

    def filter(self, record):
        """
        Filter given record based on rule's condition

        @note If the rule's condition is empty, record is always matched.

        @return Boolean
        """
        # The message must be converted via idea module
        if not isinstance(record, lite.Idea):
            logger.info("Converting message to IDEA")
            record = lite.Idea(record)

        logger.debug(record)
        logger.debug(self.__condition)

        if self.__condition == None or self.__condition == True:
            # Rule condition is empty (tautology) - should always match the record
            res = True
        elif self.__condition == False:
            res = False
        else:
            # Match the record with non-empty rule's condition
            res = self.__filter.filter(self.__condition, record)

        logger.debug("RESULT: %s", res)

        return res

    def actions(self, record):
        for action in self.__actions:
            action.run(record)

    def elseactions(self, record):
        for action in self.__elseactions:
            action.run(record)

    def __repr__(self):
        return self.__conditionRaw

    def __str__(self):
        actions = []
        for i in self.__actions:
            actions.append("{0} ({1})".format(i.actionId, i.actionType))
        elseactions = []
        for i in self.__elseactions:
            elseactions.append("{0} ({1})".format(i.actionId, i.actionType))

        return "{0}: {1}\n{2}{3}".format(
            self.id,
            " ".join([i.strip()
                      for i in str(self.__conditionRaw).split("\n")]),
            "\tActions: " + (", ".join(actions)) + "\n" if actions else "",
            "\tElse Actions: " + (", ".join(elseactions)) +
            "\n" if elseactions else "")

    def rule(self):
        return str(self.__condition)

    def __matchvar(self, rule, addrGroups):
        """
        Since pyncspect doesn't support variables yet we had to provide
        a solution where every address group's name is matched against
        the rule's condition and eventually replaced with address group's values
        """

        matched = False
        """
        Tautology - empty rule should always match
        Don't try to match or replace any address group
        """
        if rule == None or isinstance(rule, bool):
            return False

        for key in addrGroups:
            if key in rule:
                rule = re.sub(r"\b{0}\b".format(re.escape(key)),
                              addrGroups[key].iplist(), rule)
                matched = True
        self.__condition = rule

        return matched
Example #7
0
    def test_04_idea_ip_compilations(self):
        """
        Perform IDEA IP address compilation tests.
        """
        self.maxDiff = None

        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()

        rule = psr.parse('(Source.IP4 == "192.168.1.1")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ CONSTANT('192.168.1.1'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('192.168.1.1')))")

        rule = psr.parse('(Target.IP4 == "192.168.1.1")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Target.IP4') OP_EQ CONSTANT('192.168.1.1'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Target.IP4') OP_EQ IPV4(IP4('192.168.1.1')))")

        rule = psr.parse('(Source.IP6 == "::1")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP6') OP_EQ CONSTANT('::1'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP6') OP_EQ IPV6(IP6('::1')))")

        rule = psr.parse('(Target.IP6 == "::1")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Target.IP6') OP_EQ CONSTANT('::1'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Target.IP6') OP_EQ IPV6(IP6('::1')))")

        rule = psr.parse('(Source.IP4 IN ["192.168.1.1","192.168.1.2"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_IN LIST(CONSTANT('192.168.1.1'), CONSTANT('192.168.1.2')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP4') OP_IN IPLIST(IPV4(IP4('192.168.1.1')), IPV4(IP4('192.168.1.2'))))")

        rule = psr.parse('(Target.IP4 IN ["192.168.1.1","192.168.1.2"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Target.IP4') OP_IN LIST(CONSTANT('192.168.1.1'), CONSTANT('192.168.1.2')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Target.IP4') OP_IN IPLIST(IPV4(IP4('192.168.1.1')), IPV4(IP4('192.168.1.2'))))")

        rule = psr.parse('(Source.IP6 IN ["::1","::2"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP6') OP_IN LIST(CONSTANT('::1'), CONSTANT('::2')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP6') OP_IN IPLIST(IPV6(IP6('::1')), IPV6(IP6('::2'))))")

        rule = psr.parse('(Target.IP6 IN ["::1","::2"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Target.IP6') OP_IN LIST(CONSTANT('::1'), CONSTANT('::2')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Target.IP6') OP_IN IPLIST(IPV6(IP6('::1')), IPV6(IP6('::2'))))")
Example #8
0
    def test_03_basic_compilations(self):
        """
        Perform basic compilation tests.
        """
        self.maxDiff = None

        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()

        rule = psr.parse('(DetectTime == "2016-06-21T13:08:27Z")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_EQ CONSTANT('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('DetectTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(DetectTime == 2016-06-21T13:08:27Z)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_EQ DATETIME('2016-06-21T13:08:27Z'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('DetectTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")

        rule = psr.parse('(Source.IP4 == "188.14.166.39")')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ CONSTANT('188.14.166.39'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")

        rule = psr.parse('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4('188.14.166.39'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")

        rule = psr.parse('5 + 6 - 9')
        self.assertEqual(repr(rule), "MATHBINOP(INTEGER(5) OP_PLUS MATHBINOP(INTEGER(6) OP_MINUS INTEGER(9)))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "INTEGER(2)")

        rule = psr.parse('Test + 10 - 9')
        self.assertEqual(repr(rule), "MATHBINOP(VARIABLE('Test') OP_PLUS MATHBINOP(INTEGER(10) OP_MINUS INTEGER(9)))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(1))")

        rule = psr.parse('Test + (10 - 9)')
        self.assertEqual(repr(rule), "MATHBINOP(VARIABLE('Test') OP_PLUS MATHBINOP(INTEGER(10) OP_MINUS INTEGER(9)))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(1))")

        rule = psr.parse('(Test + 10) - 9')
        self.assertEqual(repr(rule), "MATHBINOP(MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(10)) OP_MINUS INTEGER(9))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(10)) OP_MINUS INTEGER(9))")

        rule = psr.parse('9 - 6 + Test')
        self.assertEqual(repr(rule), "MATHBINOP(INTEGER(9) OP_MINUS MATHBINOP(INTEGER(6) OP_PLUS VARIABLE('Test')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(INTEGER(9) OP_MINUS MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(6)))")

        rule = psr.parse('9 - (6 + Test)')
        self.assertEqual(repr(rule), "MATHBINOP(INTEGER(9) OP_MINUS MATHBINOP(INTEGER(6) OP_PLUS VARIABLE('Test')))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(INTEGER(9) OP_MINUS MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(6)))")

        rule = psr.parse('(9 - 6) + Test')
        self.assertEqual(repr(rule), "MATHBINOP(MATHBINOP(INTEGER(9) OP_MINUS INTEGER(6)) OP_PLUS VARIABLE('Test'))")
        res = cpl.compile(rule)
        self.assertEqual(repr(res), "MATHBINOP(VARIABLE('Test') OP_PLUS INTEGER(3))")
class TestDataObjectFilterInspector(unittest.TestCase):
    """
    Unit test class for testing the :py:mod:`pynspect.filters` module.
    """
    def setUp(self):
        self.flt = DataObjectFilter()
        self.psr = PynspectFilterParser()
        self.psr.build()
        self.cpl = IDEAFilterCompiler()

    def build_rule(self, rule_str):
        """
        Build and compile rule tree from given rule string.
        """
        rule = self.psr.parse(rule_str)
        rule = self.cpl.compile(rule)
        return rule

    def test_01_inspector_filters(self):
        """
        Perform tests of filters currently used in mentat-inspector.py.
        """
        self.maxDiff = None

        inspection_rules = [{
            "filter":
            "Category in ['Attempt.Login'] and Target.Port in [3389]",
            "str":
            '((Category OP_IN ["Attempt.Login"]) OP_AND (Target.Port OP_IN [3389]))',
            "tests": [[{
                "Format":
                "IDEA0",
                "ID":
                "e214d2d9-359b-443d-993d-3cc5637107a0",
                "Source": [{
                    "IP4": ["188.14.166.39"]
                }],
                "Target": [{
                    "IP4": ["195.113.165.128/25"],
                    "Port": ["3389"],
                    "Proto": ["tcp", "ssh"]
                }],
                "Note":
                "SSH login attempt",
                "DetectTime":
                "2016-06-21 13:08:27Z",
                "Node": [{
                    "Type": ["Connection", "Honeypot"],
                    "SW": ["Kippo"],
                    "Name": "cz.uhk.apate.cowrie"
                }],
                "Category": ["Attempt.Login"]
            }, True],
                      [{
                          "Format":
                          "IDEA0",
                          "ID":
                          "e214d2d9-359b-443d-993d-3cc5637107a0",
                          "Source": [{
                              "IP4": ["188.14.166.39"]
                          }],
                          "Target": [{
                              "IP4": ["195.113.165.128/25"],
                              "Port": ["338"],
                              "Proto": ["tcp", "ssh"]
                          }],
                          "Note":
                          "SSH login attempt",
                          "DetectTime":
                          "2016-06-21 13:08:27Z",
                          "Node": [{
                              "Type": ["Connection", "Honeypot"],
                              "SW": ["Kippo"],
                              "Name": "cz.uhk.apate.cowrie"
                          }],
                          "Category": ["Attempt.Login"]
                      }, False]],
        }, {
            "filter":
            "Category in ['Attempt.Login'] and (Target.Proto in ['telnet'] or Source.Proto in ['telnet'] or Target.Port in [23])",
            "str":
            '((Category OP_IN ["Attempt.Login"]) OP_AND ((Target.Proto OP_IN ["telnet"]) OP_OR ((Source.Proto OP_IN ["telnet"]) OP_OR (Target.Port OP_IN [23]))))'
        }, {
            "filter":
            "Category in ['Attempt.Login'] and (Target.Proto in ['ssh'] or Source.Proto in ['ssh'] or Target.Port in [22])",
            "str":
            '((Category OP_IN ["Attempt.Login"]) OP_AND ((Target.Proto OP_IN ["ssh"]) OP_OR ((Source.Proto OP_IN ["ssh"]) OP_OR (Target.Port OP_IN [22]))))'
        }, {
            "filter":
            "Category in ['Attempt.Login'] and (Target.Proto in ['sip', 'sip-tls'] or Source.Proto in ['sip', 'sip-tls'] or Target.Port in [5060])",
            "str":
            '((Category OP_IN ["Attempt.Login"]) OP_AND ((Target.Proto OP_IN ["sip", "sip-tls"]) OP_OR ((Source.Proto OP_IN ["sip", "sip-tls"]) OP_OR (Target.Port OP_IN [5060]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Proto in ['sip', 'sip-tls'] or Source.Proto in ['sip', 'sip-tls'] or Target.Port in [5060])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Proto OP_IN ["sip", "sip-tls"]) OP_OR ((Source.Proto OP_IN ["sip", "sip-tls"]) OP_OR (Target.Port OP_IN [5060]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and Target.Port in [23]",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND (Target.Port OP_IN [23]))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [80, 443] or Source.Proto in ['http', 'https', 'http-alt'] or Target.Proto in ['http', 'https', 'http-alt'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [80, 443]) OP_OR ((Source.Proto OP_IN ["http", "https", "http-alt"]) OP_OR (Target.Proto OP_IN ["http", "https", "http-alt"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [3306] or Source.Proto in ['mysql'] or Target.Proto in ['mysql'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [3306]) OP_OR ((Source.Proto OP_IN ["mysql"]) OP_OR (Target.Proto OP_IN ["mysql"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [445] or Source.Proto in ['microsoft-ds', 'smb'] or Target.Proto in ['microsoft-ds', 'smb'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [445]) OP_OR ((Source.Proto OP_IN ["microsoft-ds", "smb"]) OP_OR (Target.Proto OP_IN ["microsoft-ds", "smb"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [135] or Source.Proto in ['loc-srv', 'epmap'] or Target.Proto in ['loc-srv', 'epmap'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [135]) OP_OR ((Source.Proto OP_IN ["loc-srv", "epmap"]) OP_OR (Target.Proto OP_IN ["loc-srv", "epmap"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [1900] or Source.Proto in ['upnp', 'ssdp'] or Target.Proto in ['upnp', 'ssdp'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [1900]) OP_OR ((Source.Proto OP_IN ["upnp", "ssdp"]) OP_OR (Target.Proto OP_IN ["upnp", "ssdp"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [20, 21, 989, 990] or Source.Proto in ['ftp', 'ftp-data', 'ftps', 'ftps-data'] or Target.Proto in ['ftp', 'ftp-data', 'ftps', 'ftps-data'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [20, 21, 989, 990]) OP_OR ((Source.Proto OP_IN ["ftp", "ftp-data", "ftps", "ftps-data"]) OP_OR (Target.Proto OP_IN ["ftp", "ftp-data", "ftps", "ftps-data"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [1433, 1434] or Source.Proto in ['ms-sql-s', 'ms-sql-m'] or Target.Proto in ['ms-sql-s', 'ms-sql-m'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [1433, 1434]) OP_OR ((Source.Proto OP_IN ["ms-sql-s", "ms-sql-m"]) OP_OR (Target.Proto OP_IN ["ms-sql-s", "ms-sql-m"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and (Target.Port in [42] or Source.Proto in ['nameserver'] or Target.Proto in ['nameserver'])",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND ((Target.Port OP_IN [42]) OP_OR ((Source.Proto OP_IN ["nameserver"]) OP_OR (Target.Proto OP_IN ["nameserver"]))))'
        }, {
            "filter":
            "Category in ['Attempt.Exploit'] and Node.SW in ['Dionaea']",
            "str":
            '((Category OP_IN ["Attempt.Exploit"]) OP_AND (Node.SW OP_IN ["Dionaea"]))'
        }, {
            "filter":
            "Category in ['Availability.DoS', 'Availability.DDoS'] and (Target.Proto in ['dns', 'domain'] or Source.Proto in ['dns', 'domain'] or Target.Port in [53] or Source.Port in [53])",
            "str":
            '((Category OP_IN ["Availability.DoS", "Availability.DDoS"]) OP_AND ((Target.Proto OP_IN ["dns", "domain"]) OP_OR ((Source.Proto OP_IN ["dns", "domain"]) OP_OR ((Target.Port OP_IN [53]) OP_OR (Source.Port OP_IN [53])))))'
        }, {
            "filter":
            "Category in ['Availability.DDoS'] and Node.Type in ['Flow'] and Node.Type in ['Statistical']",
            "str":
            '((Category OP_IN ["Availability.DDoS"]) OP_AND ((Node.Type OP_IN ["Flow"]) OP_AND (Node.Type OP_IN ["Statistical"])))'
        }, {
            "filter":
            "Category in ['Abusive.Spam'] and Node.SW in ['UCEPROT']",
            "str":
            '((Category OP_IN ["Abusive.Spam"]) OP_AND (Node.SW OP_IN ["UCEPROT"]))'
        }, {
            "filter":
            "Category in ['Abusive.Spam'] and Node.SW in ['Fail2Ban', 'IntelMQ']",
            "str":
            '((Category OP_IN ["Abusive.Spam"]) OP_AND (Node.SW OP_IN ["Fail2Ban", "IntelMQ"]))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['qotd'] or Source.Port in [17])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["qotd"]) OP_OR (Source.Port OP_IN [17])))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and Source.Proto in ['ssdp']",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND (Source.Proto OP_IN ["ssdp"]))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['ntp'] or Source.Port in [123])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["ntp"]) OP_OR (Source.Port OP_IN [123])))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['domain'] or Source.Port in [53])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["domain"]) OP_OR (Source.Port OP_IN [53])))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['netbios-ns'] or Source.Port in [137])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["netbios-ns"]) OP_OR (Source.Port OP_IN [137])))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['ipmi'] or Source.Port in [623])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["ipmi"]) OP_OR (Source.Port OP_IN [623])))'
        }, {
            "filter":
            "Category in ['Vulnerable.Config'] and (Source.Proto in ['chargen'] or Source.Port in [19])",
            "str":
            '((Category OP_IN ["Vulnerable.Config"]) OP_AND ((Source.Proto OP_IN ["chargen"]) OP_OR (Source.Port OP_IN [19])))'
        }, {
            "filter": "Category in ['Anomaly.Traffic']",
            "str": '(Category OP_IN ["Anomaly.Traffic"])'
        }, {
            "filter":
            "Category in ['Anomaly.Connection'] and Source.Type in ['Booter']",
            "str":
            '((Category OP_IN ["Anomaly.Connection"]) OP_AND (Source.Type OP_IN ["Booter"]))'
        }, {
            "filter":
            "Category in ['Intrusion.Botnet'] and Source.Type in ['Botnet']",
            "str":
            '((Category OP_IN ["Intrusion.Botnet"]) OP_AND (Source.Type OP_IN ["Botnet"]))'
        }, {
            "filter": "Category in ['Recon.Scanning']",
            "str": '(Category OP_IN ["Recon.Scanning"])'
        }]

        for insr in inspection_rules:
            rule = self.build_rule(insr['filter'])
            self.assertEqual(str(rule), insr['str'])
            if 'tests' in insr:
                for itemt in insr['tests']:
                    msg_idea = lite.Idea(itemt[0])
                    self.assertEqual(
                        [insr['filter'],
                         self.flt.filter(rule, msg_idea)],
                        [insr['filter'], itemt[1]])
Example #10
0
class Config():

    addrGroups = dict()
    actions = dict()
    rules = list()
    parser = None
    compiler = None

    def __init__(self, parsed_config, trap=None, warden=None):
        """
        :param parsed_config Parser with parsed Yaml configuration

        :param trap Instance of TRAP client used in TrapAction

        :param warden Instance of Warden Client used in WardenAction
        """

        self.conf = parsed_config

        if not self.conf:
            raise Exception(
                "Loading YAML file ({0}) failed. Isn't it empty?".format(path))

        # Build parser
        self.parser = PynspectFilterParser()
        self.parser.build()

        self.compiler = IDEAFilterCompiler()

        self.addrGroups = dict()

        if "namespace" not in self.conf:
            logger.error(
                "ERROR: 'namespace' is required but is missing. Please specify 'namespace' in YAML config to identify names of reporters (e.g., com.example.collectornemea)."
            )

        # Create all address groups if there are any
        if "addressgroups" in self.conf:
            for i in self.conf["addressgroups"]:
                self.addrGroups[i["id"]] = AddressGroup(i)

        self.smtp_conns = dict()

        # Check if "smtp_connections" exists when there is some "email" action in "custom_actions"
        if ("custom_actions" in self.conf and any(
            [i.__contains__("email") for i in self.conf["custom_actions"]])
                and "smtp_connections" not in self.conf):
            raise LookupError(
                "'smtp_connections' is required when there is at least one 'email' action but it is missing in YAML config. Check your YAML config."
            )

        # Parse parameters for all smtp connections
        if "smtp_connections" in self.conf:
            for i in self.conf["smtp_connections"]:
                self.smtp_conns[i["id"]] = i

        self.actions = dict()

        # Parse and instantiate all custom actions
        if "custom_actions" in self.conf:
            for i in self.conf["custom_actions"]:
                if "mark" in i:
                    from .actions.Mark import MarkAction
                    self.actions[i["id"]] = MarkAction(i)

                elif "mongo" in i:
                    from .actions.Mongo import MongoAction
                    self.actions[i["id"]] = MongoAction(i)

                elif "email" in i:
                    from .actions.Email import EmailAction
                    self.actions[i["id"]] = EmailAction(
                        i, self.smtp_conns[i['email']['smtp_connection']])

                elif "file" in i:
                    from .actions.File import FileAction
                    self.actions[i["id"]] = FileAction(i)

                elif "warden" in i:
                    """
                    Pass Warden Client instance to the Warden action
                    """
                    from .actions.Warden import WardenAction
                    self.actions[i["id"]] = WardenAction(i, warden)

                elif "trap" in i:
                    """
                    Pass TRAP context instance to the TRAP action
                    """
                    from .actions.Trap import TrapAction
                    self.actions[i["id"]] = TrapAction(i, trap)

                elif "drop" in i:
                    logger.warning(
                        "Drop action mustn't be specified in custom_actions!")
                    continue

                else:
                    raise Exception("Undefined action: " + str(i))

        self.actions["drop"] = DropAction()

        self.rules = list()
        # Parse all rules and match them with actions and address groups
        # There must be at least one rule (mandatory field)
        if "rules" in self.conf:
            if self.conf["rules"]:
                for i in self.conf["rules"]:
                    self.rules.append(
                        Rule(i,
                             self.actions,
                             self.addrGroups,
                             parser=self.parser,
                             compiler=self.compiler))
            if not self.rules:
                raise Exception(
                    "YAML file should contain at least one `rule` in `rules`.")
        else:
            raise Exception("YAML file must contain `rules`.")

    def match(self, msg):
        """
        Check if msg matches rules from config file.

        Return a list of bool values representing results of all tested rules.
        """
        results = []
        actionsDone = []

        try:
            for rule in self.rules:
                self.clearActionLog()
                res = rule.filter(msg)
                #logger.debug("Filter by rule: %s \n message: %s\n\nresult: %s", rule, msg, res)
                #print("Filter by rule: %s \n message: %s\n\nresult: %s" % (rule.rule(), msg, res))

                results.append(res)

                tmp_msg = copy.deepcopy(msg)
                if res:
                    # condition is True
                    rule.actions(tmp_msg)
                    logger.info("action running")
                else:
                    # condition is False
                    rule.elseactions(tmp_msg)
                    logger.info("else action running")
                actionsDone.append(self.getActionLog())
        except DropMsg:
            # This exception breaks the processing of rule list.
            pass
        return (results, actionsDone)

    def getActionLog(self):
        return Action.actionLog

    def clearActionLog(self):
        Action.actionLog = []

    def loglevel(self):
        """Get logging level

        CRITICAL	50
        ERROR		40
        WARNING		30
        INFO		20
        DEBUG		10
        NOTSET		 0
        """
        try:
            return (self.conf["reporter"]["loglevel"]) * 10
        except:
            return 30

    def __str__(self):
        smtp = dict()
        for smtp_id, conns in self.smtp_conns.items():
            smtp[smtp_id] = "\n".join([
                "\t{}: {}".format(param, val) for param, val in conns.items()
            ])

        s = "\n".join("{}:\n{}\n".format(smtp_id, params)
                      for smtp_id, params in smtp.items())
        ag = "\n".join([str(self.addrGroups[key]) for key in self.addrGroups])
        a = "\n".join(
            [key + ":\n\t" + str(self.actions[key]) for key in self.actions])
        r = "\n".join([str(val) for val in self.rules])
        string = "Namespace: {0}\n----------------\nSmtp connections:\n{1}\n----------------\nAddress Groups:\n{2}\n"\
                 "----------------\nCustom Actions:\n{3}\n----------------\nRules:\n{4}\n".format(self.conf.get("namespace"), s, ag, a, r)
        return string
Example #11
0
    def __init__(self, parsed_config, trap=None, warden=None):
        """
        :param parsed_config Parser with parsed Yaml configuration

        :param trap Instance of TRAP client used in TrapAction

        :param warden Instance of Warden Client used in WardenAction
        """

        self.conf = parsed_config

        if not self.conf:
            raise Exception(
                "Loading YAML file ({0}) failed. Isn't it empty?".format(path))

        # Build parser
        self.parser = PynspectFilterParser()
        self.parser.build()

        self.compiler = IDEAFilterCompiler()

        self.addrGroups = dict()

        if "namespace" not in self.conf:
            logger.error(
                "ERROR: 'namespace' is required but is missing. Please specify 'namespace' in YAML config to identify names of reporters (e.g., com.example.collectornemea)."
            )

        # Create all address groups if there are any
        if "addressgroups" in self.conf:
            for i in self.conf["addressgroups"]:
                self.addrGroups[i["id"]] = AddressGroup(i)

        self.smtp_conns = dict()

        # Check if "smtp_connections" exists when there is some "email" action in "custom_actions"
        if ("custom_actions" in self.conf and any(
            [i.__contains__("email") for i in self.conf["custom_actions"]])
                and "smtp_connections" not in self.conf):
            raise LookupError(
                "'smtp_connections' is required when there is at least one 'email' action but it is missing in YAML config. Check your YAML config."
            )

        # Parse parameters for all smtp connections
        if "smtp_connections" in self.conf:
            for i in self.conf["smtp_connections"]:
                self.smtp_conns[i["id"]] = i

        self.actions = dict()

        # Parse and instantiate all custom actions
        if "custom_actions" in self.conf:
            for i in self.conf["custom_actions"]:
                if "mark" in i:
                    from .actions.Mark import MarkAction
                    self.actions[i["id"]] = MarkAction(i)

                elif "mongo" in i:
                    from .actions.Mongo import MongoAction
                    self.actions[i["id"]] = MongoAction(i)

                elif "email" in i:
                    from .actions.Email import EmailAction
                    self.actions[i["id"]] = EmailAction(
                        i, self.smtp_conns[i['email']['smtp_connection']])

                elif "file" in i:
                    from .actions.File import FileAction
                    self.actions[i["id"]] = FileAction(i)

                elif "warden" in i:
                    """
                    Pass Warden Client instance to the Warden action
                    """
                    from .actions.Warden import WardenAction
                    self.actions[i["id"]] = WardenAction(i, warden)

                elif "trap" in i:
                    """
                    Pass TRAP context instance to the TRAP action
                    """
                    from .actions.Trap import TrapAction
                    self.actions[i["id"]] = TrapAction(i, trap)

                elif "drop" in i:
                    logger.warning(
                        "Drop action mustn't be specified in custom_actions!")
                    continue

                else:
                    raise Exception("Undefined action: " + str(i))

        self.actions["drop"] = DropAction()

        self.rules = list()
        # Parse all rules and match them with actions and address groups
        # There must be at least one rule (mandatory field)
        if "rules" in self.conf:
            if self.conf["rules"]:
                for i in self.conf["rules"]:
                    self.rules.append(
                        Rule(i,
                             self.actions,
                             self.addrGroups,
                             parser=self.parser,
                             compiler=self.compiler))
            if not self.rules:
                raise Exception(
                    "YAML file should contain at least one `rule` in `rules`.")
        else:
            raise Exception("YAML file must contain `rules`.")
Example #12
0
 def setUp(self):
     self.flt = DataObjectFilter()
     self.psr = PynspectFilterParser()
     self.psr.build()
Example #13
0
class TestDataObjectFilter(unittest.TestCase):
    """
    Unit test class for testing the :py:mod:`pynspect.filters` module.
    """

    test_msg1 = {
        "ID" : "e214d2d9-359b-443d-993d-3cc5637107a0",
        "WinEndTime" : "2016-06-21 11:25:01Z",
        "ConnCount" : 2,
        "Source" : [
            {
                "IP4" : [
                    "188.14.166.39"
                ]
            }
        ],
        "Format" : "IDEA0",
        "WinStartTime" : "2016-06-21 11:20:01Z",
        "_CESNET" : {
            "StorageTime" : 1466508305
        },
        "Target" : [
            {
                "IP4" : [
                    "195.113.165.128/25"
                ],
                "Port" : [
                    "22"
                ],
                "Proto" : [
                    "tcp",
                    "ssh"
                ],
                "Anonymised" : True
            }
        ],
        "Note" : "SSH login attempt",
        "DetectTime" : "2016-06-21 13:08:27Z",
        "Node" : [
            {
                "Name" : "cz.cesnet.mentat.warden_filer",
                "Type" : [
                    "Relay"
                ]
            },
            {
                "AggrWin" : "00:05:00",
                "Type" : [
                    "Connection",
                    "Honeypot",
                    "Recon"
                ],
                "SW" : [
                    "Kippo"
                ],
                "Name" : "cz.uhk.apate.cowrie"
            }
        ],
        "Category" : [
            "Attempt.Login"
        ]
    }

    def setUp(self):
        self.flt = DataObjectFilter()
        self.psr = PynspectFilterParser()
        self.psr.build()

    def test_01_basic_logical(self):
        """
        Perform filtering tests with basic logical expressions.
        """
        self.maxDiff = None

        rule = LogicalBinOpRule('OP_AND', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = LogicalBinOpRule('OP_OR', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = LogicalBinOpRule('OP_XOR', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = UnaryOperationRule('OP_NOT', ConstantRule(True))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = UnaryOperationRule('OP_NOT', ConstantRule(False))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = UnaryOperationRule('OP_NOT', VariableRule("Target.Anonymised"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

    def test_02_basic_comparison(self):
        """
        Perform filtering tests with basic comparison expressions.
        """
        self.maxDiff = None

        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107a0"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107a0"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = ComparisonBinOpRule('OP_LIKE', VariableRule("ID"), ConstantRule("e214d2d9"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_LIKE', VariableRule("ID"), ConstantRule("xxxxxxxx"))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_IN', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Attempt.Login"))))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_IN', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Spam"))))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_IS', VariableRule("Category"), ListRule(ConstantRule("Attempt.Login")))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_IS', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Attempt.Login"))))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ConnCount"), IntegerRule(4))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ConnCount"), IntegerRule(4))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_GT', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_GT', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_LT', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = ComparisonBinOpRule('OP_LT', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

    def test_03_parsed_comparison(self):
        """
        Perform filtering tests with parsed comparison expressions.
        """
        self.maxDiff = None

        rule = self.psr.parse('ID == "e214d2d9-359b-443d-993d-3cc5637107a0"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ID eq "e214d2d9-359b-443d-993d-3cc5637107"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ID != "e214d2d9-359b-443d-993d-3cc5637107a0"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ID ne "e214d2d9-359b-443d-993d-3cc5637107"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = self.psr.parse('ID like "e214d2d9"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ID LIKE "xxxxxxxx"')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('Category in ["Phishing" , "Attempt.Login"]')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('Category IN ["Phishing" , "Spam"]')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('Category is ["Attempt.Login"]')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('Category IS ["Phishing" , "Attempt.Login"]')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount == 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount eq 4')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount != 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount ne 4')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount > 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount gt 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount >= 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount ge 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount GE 3')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount < 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCount lt 3')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount <= 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount le 3')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('ConnCount LE 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)
        rule = self.psr.parse('ConnCounts LE 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)

    def test_04_basic_math(self):
        """
        Perform filtering tests with basic math expressions.
        """
        self.maxDiff = None

        rule = MathBinOpRule('OP_PLUS', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 3)
        rule = MathBinOpRule('OP_MINUS', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 1)
        rule = MathBinOpRule('OP_TIMES', VariableRule("ConnCount"), IntegerRule(5))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 10)
        rule = MathBinOpRule('OP_DIVIDE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 1)
        rule = MathBinOpRule('OP_MODULO', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 0)

    def test_05_parsed_math(self):
        """
        Perform filtering tests with parsed math expressions.
        """
        self.maxDiff = None

        rule = self.psr.parse('ConnCount + 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 3)
        rule = self.psr.parse('ConnCount - 1')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 1)
        rule = self.psr.parse('ConnCount * 5')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 10)
        rule = self.psr.parse('ConnCount / 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 1)
        rule = self.psr.parse('ConnCount % 2')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), 0)

    def test_06_advanced_filters(self):
        """
        Perform advanced filtering tests.
        """
        self.maxDiff = None

        rule = self.psr.parse('(ConnCount + 10) > 11')
        self.assertEqual(repr(rule), "COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(10)) OP_GT INTEGER(11))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('((ConnCount + 3) < 5) or ((ConnCount + 10) > 11)')
        self.assertEqual(repr(rule), "LOGBINOP(COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(3)) OP_LT INTEGER(5)) OP_OR COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(10)) OP_GT INTEGER(11)))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
        rule = self.psr.parse('1')
        self.assertEqual(repr(rule), "INTEGER(1)")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

    def test_07_non_existent_nodes(self):
        """
        Perform advanced filtering tests.
        """
        self.maxDiff = None

        rule = self.psr.parse('(ConnCounts + 10) > 11')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)
        rule = self.psr.parse('ConnCount > ConnCounts')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)
        rule = self.psr.parse('DetectTime < InspectionTime')
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)

    def test_08_functions(self):
        """
        Perform advanced filtering tests.
        """
        self.maxDiff = None

        rule = self.psr.parse('(size(Node.Type) > 2)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(size(VARIABLE('Node.Type'),)) OP_GT INTEGER(2))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = self.psr.parse('(size(Source.IP4) > 4)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(size(VARIABLE('Source.IP4'),)) OP_GT INTEGER(4))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = self.psr.parse('(strlen(ID) > 8)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('ID'),)) OP_GT INTEGER(8))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = self.psr.parse('(strlen(ID) < 8)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('ID'),)) OP_LT INTEGER(8))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = self.psr.parse('(strlen(Description) > 8)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Description'),)) OP_GT INTEGER(8))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)

        rule = self.psr.parse('(strlen(Description) < 8)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Description'),)) OP_LT INTEGER(8))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), None)

        rule = self.psr.parse('(strlen(Node.Name) > 20)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Node.Name'),)) OP_GT INTEGER(20))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = self.psr.parse('(strlen(Node.Name) > 30)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Node.Name'),)) OP_GT INTEGER(30))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = self.psr.parse('(strlen(Node.Name) < 20)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Node.Name'),)) OP_LT INTEGER(20))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)

        rule = self.psr.parse('(strlen(Node.Name) < 10)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(strlen(VARIABLE('Node.Name'),)) OP_LT INTEGER(10))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), False)

        rule = self.psr.parse('(time() > 500.12)')
        self.assertEqual(repr(rule), "COMPBINOP(FUNCTION(time()) OP_GT FLOAT(500.12))")
        self.assertEqual(self.flt.filter(rule, self.test_msg1), True)
 def setUp(self):
     self.flt = DataObjectFilter()
     self.psr = PynspectFilterParser()
     self.psr.build()
     self.cpl = IDEAFilterCompiler()
Example #15
0
class Config():

    addrGroups = dict()
    actions = dict()
    rules = list()
    parser = None
    compiler = None

    def __init__(self, path, trap=None, warden=None):
        """
        :param path Path to YAML configuration file

        :param trap Instance of TRAP client used in TrapAction

        :param warden Instance of Warden Client used in WardenAction
        """
        # Parse given config file
        with open(path, 'r') as f:
            self.conf = Parser(f)

        if not self.conf:
            raise Exception(
                "Loading YAML file ({0}) failed. Isn't it empty?".format(path))

        # Build parser
        self.parser = PynspectFilterParser()
        self.parser.build()

        self.compiler = IDEAFilterCompiler()

        self.addrGroups = dict()

        # Create all address groups if there are any
        if "addressgroups" in self.conf:
            for i in self.conf["addressgroups"]:
                self.addrGroups[i["id"]] = AddressGroup(i)

        self.actions = dict()

        # Parse and instantiate all custom actions
        if "custom_actions" in self.conf:
            for i in self.conf["custom_actions"]:
                if "mark" in i:
                    from .actions.Mark import MarkAction
                    self.actions[i["id"]] = MarkAction(i)

                elif "mongo" in i:
                    from .actions.Mongo import MongoAction
                    self.actions[i["id"]] = MongoAction(i)

                elif "email" in i:
                    from .actions.Email import EmailAction
                    self.actions[i["id"]] = EmailAction(i)

                elif "file" in i:
                    from .actions.File import FileAction
                    self.actions[i["id"]] = FileAction(i)

                elif "warden" in i:
                    """
                    Pass Warden Client instance to the Warden action
                    """
                    from .actions.Warden import WardenAction
                    self.actions[i["id"]] = WardenAction(i, warden)

                elif "trap" in i:
                    """
                    Pass TRAP context instance to the TRAP action
                    """
                    from .actions.Trap import TrapAction
                    self.actions[i["id"]] = TrapAction(i, trap)

                elif "drop" in i:
                    logger.warning(
                        "Drop action mustn't be specified in custom_actions!")
                    continue

                else:
                    raise Exception("Undefined action: " + str(i))

        self.actions["drop"] = DropAction()

        self.rules = list()

        # Parse all rules and match them with actions and address groups
        # There must be at least one rule (mandatory field)
        if "rules" in self.conf:
            if self.conf["rules"]:
                for i in self.conf["rules"]:
                    self.rules.append(
                        Rule(i,
                             self.actions,
                             self.addrGroups,
                             parser=self.parser,
                             compiler=self.compiler))
            if not self.rules:
                raise Exception(
                    "YAML file should contain at least one `rule` in `rules`.")
        else:
            raise Exception("YAML file must contain `rules`.")

    def match(self, msg):
        """
        Check if msg matches rules from config file.

        Return a list of bool values representing results of all tested rules.
        """
        results = []
        actionsDone = []

        try:
            for rule in self.rules:
                self.clearActionLog()
                res = rule.filter(msg)
                #logger.debug("Filter by rule: %s \n message: %s\n\nresult: %s", rule, msg, res)
                #print("Filter by rule: %s \n message: %s\n\nresult: %s" % (rule.rule(), msg, res))

                results.append(res)

                tmp_msg = copy.deepcopy(msg)
                if res:
                    # condition is True
                    rule.actions(tmp_msg)
                    logger.info("action running")
                else:
                    # condition is False
                    rule.elseactions(tmp_msg)
                    logger.info("else action running")
                actionsDone.append(self.getActionLog())
        except DropMsg:
            # This exception breaks the processing of rule list.
            pass
        return (results, actionsDone)

    def getActionLog(self):
        return Action.actionLog

    def clearActionLog(self):
        Action.actionLog = []

    def loglevel(self):
        """Get logging level

        CRITICAL	50
        ERROR		40
        WARNING		30
        INFO		20
        DEBUG		10
        NOTSET		 0
        """
        try:
            return (self.conf["reporter"]["loglevel"]) * 10
        except:
            return 30

    def __str__(self):
        ag = "\n".join([str(self.addrGroups[key]) for key in self.addrGroups])
        a = "\n".join(
            [key + ":\n\t" + str(self.actions[key]) for key in self.actions])
        r = "\n".join([str(val) for val in self.rules])
        string = "Address Groups:\n{0}\n----------------\nCustom Actions:\n{1}\n----------------\nRules:\n{2}\n".format(
            ag, a, r)
        return string
Example #16
0
class TestDataObjectFilterIDEA(unittest.TestCase):
    """
    Unit test class for testing the :py:mod:`pynspect.filters` module.
    """

    test_msg1 = {
        "ID" : "e214d2d9-359b-443d-993d-3cc5637107a0",
        "WinEndTime" : "2016-06-21 11:25:01Z",
        "ConnCount" : 2,
        "Source" : [
            {
                "IP4" : [
                    "188.14.166.39"
                ]
            }
        ],
        "Format" : "IDEA0",
        "WinStartTime" : "2016-06-21 11:20:01Z",
        "_CESNET" : {
            "StorageTime" : 1466508305
        },
        "Target" : [
            {
                "IP4" : [
                    "195.113.165.128/25"
                ],
                "Port" : [
                    "22"
                ],
                "Proto" : [
                    "tcp",
                    "ssh"
                ],
                "Anonymised" : True
            }
        ],
        "Note" : "SSH login attempt",
        "DetectTime" : "2016-06-21 13:08:27Z",
        "Node" : [
            {
                "Name" : "cz.cesnet.mentat.warden_filer",
                "Type" : [
                    "Relay"
                ]
            },
            {
                "AggrWin" : "00:05:00",
                "Type" : [
                    "Connection",
                    "Honeypot",
                    "Recon"
                ],
                "SW" : [
                    "Kippo"
                ],
                "Name" : "cz.uhk.apate.cowrie"
            }
        ],
        "Category" : [
            "Attempt.Login"
        ]
    }

    def setUp(self):
        self.flt = DataObjectFilter()
        self.psr = PynspectFilterParser()
        self.psr.build()
        self.cpl = IDEAFilterCompiler()

        self.msg_idea = lite.Idea(self.test_msg1)

    def build_rule(self, rule_str):
        """
        Build and compile rule tree from given rule string.
        """
        rule = self.psr.parse(rule_str)
        rule = self.cpl.compile(rule)
        return rule

    def check_rule(self, rule):
        """
        Check given rule against internal test message and filter.
        """
        return self.flt.filter(rule, self.msg_idea)

    def test_01_basic_logical(self):
        """
        Perform filtering tests with basic logical expressions.
        """
        self.maxDiff = None

        rule = LogicalBinOpRule('OP_AND', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), False)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), False)
        rule = LogicalBinOpRule('OP_AND', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), False)

        rule = LogicalBinOpRule('OP_OR', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_OR', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), False)

        rule = LogicalBinOpRule('OP_XOR', ConstantRule(True), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), False)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(True), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(False), ConstantRule(True))
        self.assertEqual(self.check_rule(rule), True)
        rule = LogicalBinOpRule('OP_XOR', ConstantRule(False), ConstantRule(False))
        self.assertEqual(self.check_rule(rule), False)

        rule = UnaryOperationRule('OP_NOT', ConstantRule(True))
        self.assertEqual(self.check_rule(rule), False)
        rule = UnaryOperationRule('OP_NOT', ConstantRule(False))
        self.assertEqual(self.check_rule(rule), True)
        rule = UnaryOperationRule('OP_NOT', VariableRule("Target.Anonymised"))
        self.assertEqual(self.check_rule(rule), False)

    def test_02_basic_comparison(self):
        """
        Perform filtering tests with basic comparison operations.
        """
        self.maxDiff = None

        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107a0"))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107"))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107a0"))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ID"), ConstantRule("e214d2d9-359b-443d-993d-3cc5637107"))
        self.assertEqual(self.check_rule(rule), True)

        rule = ComparisonBinOpRule('OP_LIKE', VariableRule("ID"), ConstantRule("e214d2d9"))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_LIKE', VariableRule("ID"), ConstantRule("xxxxxxxx"))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_IN', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Attempt.Login"))))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_IN', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Spam"))))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_IS', VariableRule("Category"), ListRule(ConstantRule("Attempt.Login")))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_IS', VariableRule("Category"), ListRule(ConstantRule("Phishing"), ListRule(ConstantRule("Attempt.Login"))))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_EQ', VariableRule("ConnCount"), IntegerRule(4))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_NE', VariableRule("ConnCount"), IntegerRule(4))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_GT', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_GT', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_GE', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_LT', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), False)
        rule = ComparisonBinOpRule('OP_LT', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(3))
        self.assertEqual(self.check_rule(rule), True)
        rule = ComparisonBinOpRule('OP_LE', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.check_rule(rule), False)

    def test_03_parsed_comparison(self):
        """
        Perform filtering tests with basic parsed comparison operations.
        """
        self.maxDiff = None

        rule = self.build_rule('ID == "e214d2d9-359b-443d-993d-3cc5637107a0"')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ID eq "e214d2d9-359b-443d-993d-3cc5637107"')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ID != "e214d2d9-359b-443d-993d-3cc5637107a0"')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ID ne "e214d2d9-359b-443d-993d-3cc5637107"')
        self.assertEqual(self.check_rule(rule), True)

        rule = self.build_rule('ID like "e214d2d9"')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ID LIKE "xxxxxxxx"')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('Category in ["Phishing" , "Attempt.Login"]')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('Category IN ["Phishing" , "Spam"]')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('Category is ["Attempt.Login"]')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('Category IS ["Phishing" , "Attempt.Login"]')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount == 2')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount eq 4')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount != 2')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount ne 4')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount > 2')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount gt 1')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount >= 2')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount ge 1')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount GE 3')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount < 2')
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('ConnCount lt 3')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount <= 2')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount le 3')
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('ConnCount LE 1')
        self.assertEqual(self.check_rule(rule), False)

    def test_04_basic_math(self):
        """
        Perform filtering tests with basic math operations.
        """
        self.maxDiff = None

        rule = MathBinOpRule('OP_PLUS', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.check_rule(rule), 3)
        rule = MathBinOpRule('OP_MINUS', VariableRule("ConnCount"), IntegerRule(1))
        self.assertEqual(self.check_rule(rule), 1)
        rule = MathBinOpRule('OP_TIMES', VariableRule("ConnCount"), IntegerRule(5))
        self.assertEqual(self.check_rule(rule), 10)
        rule = MathBinOpRule('OP_DIVIDE', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), 1)
        rule = MathBinOpRule('OP_MODULO', VariableRule("ConnCount"), IntegerRule(2))
        self.assertEqual(self.check_rule(rule), 0)

    def test_05_parsed_math(self):
        """
        Perform filtering tests with parsed math operations.
        """
        self.maxDiff = None

        rule = self.build_rule('ConnCount + 1')
        self.assertEqual(self.check_rule(rule), 3)
        rule = self.build_rule('ConnCount - 1')
        self.assertEqual(self.check_rule(rule), 1)
        rule = self.build_rule('ConnCount * 5')
        self.assertEqual(self.check_rule(rule), 10)
        rule = self.build_rule('ConnCount / 2')
        self.assertEqual(self.check_rule(rule), 1)
        rule = self.build_rule('ConnCount % 2')
        self.assertEqual(self.check_rule(rule), 0)

    def test_06_advanced_filters(self):
        """
        Perform advanced filtering tests.
        """
        self.maxDiff = None

        rule = self.build_rule('DetectTime + 3600')
        self.assertEqual(repr(rule), "MATHBINOP(VARIABLE('DetectTime') OP_PLUS TIMEDELTA(datetime.timedelta(0, 3600)))")
        expected_res = (datetime.datetime(2016, 6, 21, 13, 8, 27) + datetime.timedelta(seconds = 3600))
        self.assertEqual(self.check_rule(rule), expected_res)

        rule = self.build_rule('(ConnCount + 10) > 11')
        self.assertEqual(repr(rule), "COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(10)) OP_GT INTEGER(11))")
        self.assertEqual(self.check_rule(rule), True)

        rule = self.build_rule('(ConnCount + 3) < 5')
        self.assertEqual(repr(rule), "COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(3)) OP_LT INTEGER(5))")
        self.assertEqual(self.check_rule(rule), False)

        rule = self.build_rule('((ConnCount + 3) < 5) or ((ConnCount + 10) > 11)')
        self.assertEqual(repr(rule), "LOGBINOP(COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(3)) OP_LT INTEGER(5)) OP_OR COMPBINOP(MATHBINOP(VARIABLE('ConnCount') OP_PLUS INTEGER(10)) OP_GT INTEGER(11)))")
        self.assertEqual(self.check_rule(rule), True)

        rule = self.build_rule('(DetectTime == 2016-06-21T13:08:27Z)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_EQ DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('(DetectTime != 2016-06-21T13:08:27Z)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_NE DATETIME(datetime.datetime(2016, 6, 21, 13, 8, 27)))")
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('(DetectTime >= 2016-06-21T14:08:27Z)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_GE DATETIME(datetime.datetime(2016, 6, 21, 14, 8, 27)))")
        self.assertEqual(self.check_rule(rule), False)
        rule = self.build_rule('(DetectTime <= 2016-06-21T14:08:27Z)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_LE DATETIME(datetime.datetime(2016, 6, 21, 14, 8, 27)))")
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('DetectTime < (utcnow() + 05:00:00)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_LT MATHBINOP(FUNCTION(utcnow()) OP_PLUS TIMEDELTA(datetime.timedelta(0, 18000))))")
        self.assertEqual(self.check_rule(rule), True)
        rule = self.build_rule('DetectTime > (utcnow() - 05:00:00)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('DetectTime') OP_GT MATHBINOP(FUNCTION(utcnow()) OP_MINUS TIMEDELTA(datetime.timedelta(0, 18000))))")
        self.assertEqual(self.check_rule(rule), False)

        rule = self.build_rule('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")
        self.assertEqual(self.check_rule(rule), True)

        rule = self.build_rule('(Source.IP4 in ["188.14.166.39","188.14.166.40","188.14.166.41"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_IN IPLIST(IPV4(IP4('188.14.166.39')), IPV4(IP4('188.14.166.40')), IPV4(IP4('188.14.166.41'))))")
        self.assertEqual(self.check_rule(rule), True)

        # list with CIDR addresses
        rule = self.build_rule('(Source.IP4 in ["188.14.166.0/24","10.0.0.0/8","189.14.166.41"])')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_IN IPLIST(IPV4(IP4Net('188.14.166.0/24')), IPV4(IP4Net('10.0.0.0/8')), IPV4(IP4('189.14.166.41'))))")
        self.assertEqual(self.check_rule(rule), True)

    def test_06_shortcuts(self):
        """
        Perform tests of shortcut methods.
        """
        self.maxDiff = None

        # Let the shortcut method initialize everything.
        flt = DataObjectFilter(
            parser   = PynspectFilterParser,
            compiler = IDEAFilterCompiler
        )
        rule = flt.prepare('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")
        self.assertEqual(self.check_rule(rule), True)

        # Create parser and compiler instances by hand, but register them into filter.
        cpl = IDEAFilterCompiler()
        psr = PynspectFilterParser()
        psr.build()
        flt = DataObjectFilter(
            parser   = psr,
            compiler = cpl
        )
        rule = flt.prepare('(Source.IP4 == 188.14.166.39)')
        self.assertEqual(repr(rule), "COMPBINOP(VARIABLE('Source.IP4') OP_EQ IPV4(IP4('188.14.166.39')))")
        self.assertEqual(self.check_rule(rule), True)
Example #17
0
    def __init__(self, path, trap=None, warden=None):
        """
        :param path Path to YAML configuration file

        :param trap Instance of TRAP client used in TrapAction

        :param warden Instance of Warden Client used in WardenAction
        """
        # Parse given config file
        with open(path, 'r') as f:
            self.conf = Parser(f)

        if not self.conf:
            raise Exception(
                "Loading YAML file ({0}) failed. Isn't it empty?".format(path))

        # Build parser
        self.parser = PynspectFilterParser()
        self.parser.build()

        self.compiler = IDEAFilterCompiler()

        self.addrGroups = dict()

        # Create all address groups if there are any
        if "addressgroups" in self.conf:
            for i in self.conf["addressgroups"]:
                self.addrGroups[i["id"]] = AddressGroup(i)

        self.actions = dict()

        # Parse and instantiate all custom actions
        if "custom_actions" in self.conf:
            for i in self.conf["custom_actions"]:
                if "mark" in i:
                    from .actions.Mark import MarkAction
                    self.actions[i["id"]] = MarkAction(i)

                elif "mongo" in i:
                    from .actions.Mongo import MongoAction
                    self.actions[i["id"]] = MongoAction(i)

                elif "email" in i:
                    from .actions.Email import EmailAction
                    self.actions[i["id"]] = EmailAction(i)

                elif "file" in i:
                    from .actions.File import FileAction
                    self.actions[i["id"]] = FileAction(i)

                elif "warden" in i:
                    """
                    Pass Warden Client instance to the Warden action
                    """
                    from .actions.Warden import WardenAction
                    self.actions[i["id"]] = WardenAction(i, warden)

                elif "trap" in i:
                    """
                    Pass TRAP context instance to the TRAP action
                    """
                    from .actions.Trap import TrapAction
                    self.actions[i["id"]] = TrapAction(i, trap)

                elif "drop" in i:
                    logger.warning(
                        "Drop action mustn't be specified in custom_actions!")
                    continue

                else:
                    raise Exception("Undefined action: " + str(i))

        self.actions["drop"] = DropAction()

        self.rules = list()

        # Parse all rules and match them with actions and address groups
        # There must be at least one rule (mandatory field)
        if "rules" in self.conf:
            if self.conf["rules"]:
                for i in self.conf["rules"]:
                    self.rules.append(
                        Rule(i,
                             self.actions,
                             self.addrGroups,
                             parser=self.parser,
                             compiler=self.compiler))
            if not self.rules:
                raise Exception(
                    "YAML file should contain at least one `rule` in `rules`.")
        else:
            raise Exception("YAML file must contain `rules`.")
Example #18
0
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open

#
# Import local version of pynspect library, so that we can insert correct version
# number into documentation.
#
sys.path.insert(0, os.path.abspath('.'))
import pynspect

# Generate parsetab.py in advance so it can go into package.
from pynspect.gparser import PynspectFilterParser
parser = PynspectFilterParser()
parser.build()

here = os.path.abspath(os.path.dirname(__file__))

#-------------------------------------------------------------------------------

# Get the long description from the README file
with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
    long_description = f.read()

setup(
    name = 'pynspect',
    version = pynspect.__version__,
    description = 'Python data inspection library',
    long_description = long_description,