Example #1
0
class Integration(Main):
    def __init__(self):
        super().__init__()
        self.lexsi = LexsiConnector(self.cfg)
        self.TheHiveConnector = TheHiveConnector(self.cfg)

    def validateRequest(self, request):

        if request.is_json:
            content = request.get_json()
            if 'type' in content and content['type'] == "Active":
                workflowReport = self.allIncidents2Alert(content['type'])
                if workflowReport['success']:
                    return json.dumps(workflowReport), 200
                else:
                    return json.dumps(workflowReport), 500
            else:
                self.logger.error('Missing type or type is not supported')
                return json.dumps({
                    'sucess':
                    False,
                    'message':
                    "Missing type or type is not supported"
                }), 500
        else:
            self.logger.error('Not json request')
            return json.dumps({
                'sucess': False,
                'message': "Request didn't contain valid JSON"
            }), 400

    def allIncidents2Alert(self, status):
        """
        Get all opened incidents created within lexsi
        and create alerts for them in TheHive
        """
        self.logger.info('%s.allincident2Alert starts', __name__)

        incidentsList = self.lexsi.getOpenItems()['result']

        report = dict()
        report['success'] = True
        report['incidents'] = list()

        try:
            # each incidents in the list is represented as a dict
            # we enrich this dict with additional details
            for incident in incidentsList:

                # Prepare new alert
                incident_report = dict()
                self.logger.debug("incident: %s" % incident)

                theHiveAlert = self.IncidentToHiveAlert(incident)

                # searching if the incident has already been converted to alert
                query = dict()
                query['sourceRef'] = str(incident['incident'])
                self.logger.info('Looking for incident %s in TheHive alerts',
                                 str(incident['incident']))
                results = self.TheHiveConnector.findAlert(query)
                if len(results) == 0:
                    self.logger.info(
                        'incident %s not found in TheHive alerts, creating it',
                        str(incident['incident']))
                    try:

                        theHiveEsAlertId = self.TheHiveConnector.createAlert(
                            theHiveAlert)['id']
                        self.TheHiveConnector.promoteAlertToCase(
                            theHiveEsAlertId)

                        incident_report['raised_alert_id'] = theHiveEsAlertId
                        incident_report['lexsi_incident_id'] = incident[
                            'incident']
                        incident_report['success'] = True

                    except Exception as e:
                        self.logger.error(incident_report)
                        self.logger.error('%s.allincident2Alert failed',
                                          __name__,
                                          exc_info=True)
                        incident_report['success'] = False
                        if isinstance(e, ValueError):
                            errorMessage = json.loads(str(e))['message']
                            incident_report['message'] = errorMessage
                        else:
                            incident_report['message'] = str(
                                e) + ": Couldn't raise alert in TheHive"
                        incident_report['incident_id'] = incident['incident']
                        # Set overall success if any fails
                        report['success'] = False

                else:
                    self.logger.info(
                        'incident %s already imported as alert, checking for updates',
                        str(incident['incident']))
                    alert_found = results[0]

                    if self.TheHiveConnector.checkForUpdates(
                            theHiveAlert, alert_found,
                            str(incident['incident'])):
                        # Mark the alert as read
                        self.TheHiveConnector.markAlertAsRead(
                            alert_found['id'])
                        incident_report['updated_alert_id'] = alert_found['id']
                        incident_report['sentinel_incident_id'] = str(
                            incident['incident'])
                        incident_report['success'] = True
                    else:
                        incident_report['sentinel_incident_id'] = str(
                            incident['incident'])
                        incident_report['success'] = True
                report['incidents'].append(incident_report)

            thehiveAlerts, open_lexsi_cases = self.lexsi_opened_alerts_thehive(
            )
            self.set_alert_status_ignored(incidentsList, thehiveAlerts,
                                          open_lexsi_cases)

        except Exception as e:

            self.logger.error(
                'Failed to create alert from Lexsi incident (retrieving incidents failed)',
                exc_info=True)
            report['success'] = False
            report[
                'message'] = "%s: Failed to create alert from incident" % str(
                    e)

        return report

    def IncidentToHiveAlert(self, incident):

        #
        # Creating the alert
        #

        # Setup Tags
        tags = ['Lexsi', 'incident', 'Synapse']

        # Skip for now
        artifacts = []

        # Retrieve the configured case_template
        CaseTemplate = self.cfg.get('Lexsi', 'case_template')

        # Build TheHive alert
        alert = self.TheHiveConnector.craftAlert(
            "{}: {}".format(incident['incident'], incident['title']),
            self.craftAlertDescription(incident),
            self.getHiveSeverity(incident),
            self.timestamp_to_epoch(incident['detected'], "%Y-%m-%d %H:%M:%S"),
            tags, 2, 'New', 'internal', 'Lexsi', str(incident['incident']),
            artifacts, CaseTemplate)

        return alert

    def craftAlertDescription(self, incident):
        """
            From the incident metadata, crafts a nice description in markdown
            for TheHive
        """
        self.logger.debug('craftAlertDescription starts')

        # Start empty
        description = ""

        # Add incident details table
        description += (
            '#### Summary\n\n' +
            '|                         |               |\n' +
            '| ----------------------- | ------------- |\n' +
            '| **URL**          | ' +
            "{}{}{}".format("```", str(incident['url']), "```") + ' |\n' +
            '| **Type**          | ' + str(incident['type']) + ' |\n' +
            '| **Severity**          | ' + str(incident['severity']) + ' |\n' +
            '| **Category**         | ' + str(incident['category']) + ' |\n' +
            '| **Updated**        | ' + str(incident['updated']) + ' |\n' +
            '| **Detected**        | ' + str(incident['detected']) + ' |\n' +
            '| **Source**        | ' + str(incident['source']) + ' |\n' +
            '| **Analyst Name(Lexsi)**        | ' +
            str(incident['analystName']) + ' |\n' +
            '| **Link to Orange Portal**        | ' +
            str("https://portal.cert.orangecyberdefense.com/cybercrime/{}".
                format(incident['id'])) + ' |\n' + '\n\n\n\n')

        return description

    def timestamp_to_epoch(self, date_time, pattern):
        return int(time.mktime(time.strptime(date_time, pattern))) * 1000

    def getHiveSeverity(self, incident):
        # severity in TheHive is either low, medium, high or critical
        # while severity in Lexsi is from 0 to 5
        if int(incident['severity']) in {0, 5}:
            return 1
        # elif int(incident['severity']) in {2,3}:
        #    return 2
        # elif int(incident['severity']) in {4,5}:
        #    return 3
        else:
            return 2

    def lexsi_opened_alerts_thehive(self):
        thehiveAlerts = []
        open_lexsi_cases = {}
        query = In('tags', ['Lexsi'])

        self.logger.info(
            'Looking for incident in TheHive alerts with tag Lexsi')
        # self.logger.info(query)
        results = self.TheHiveConnector.findAlert(query)
        for alert_found in results:
            # Check if a case is linked
            if 'case' in alert_found:
                try:
                    case_found = self.TheHiveConnector.getCase(
                        alert_found['case'])
                    # Check if the status is open. Only then append it to the list
                    if case_found['status'] == "Open":
                        open_lexsi_cases[alert_found['sourceRef']] = case_found
                        thehiveAlerts.append(alert_found['sourceRef'])
                except Exception as e:
                    self.logger.error("Could not find case: {}".format(e),
                                      exc_info=True)
                    continue
        self.logger.debug(
            "Lexsi Alerts opened in theHive: {}".format(thehiveAlerts))
        return thehiveAlerts, open_lexsi_cases

    def compare_lists(self, list1, list2):
        return list(set(list1) - set(list2))

    def set_alert_status_ignored(self, incidentsList, thehiveAlerts,
                                 open_lexsi_cases):
        lexsi_reporting = []
        # incidentsList = self.lexsi.getOpenItems()['result']

        for incident in incidentsList:
            lexsi_reporting.append(incident['incident'])

        self.logger.debug(
            "the list of opened Lexsi Incidents: {}".format(lexsi_reporting))
        uncommon_elements = self.compare_lists(thehiveAlerts, lexsi_reporting)
        # uncommon_elements=['476121']
        self.logger.debug(
            "Open cases present in TheHive but not in list of opened Lexsi Incidents: {}"
            .format((uncommon_elements)))

        for element in uncommon_elements:
            self.logger.info(
                "Preparing to close the case for {}".format(element))
            query = dict()
            query['sourceRef'] = str(element)
            self.logger.debug('Looking for incident %s in TheHive alerts',
                              str(element))
            try:
                if element in open_lexsi_cases:
                    # Resolve the case
                    case_id = open_lexsi_cases[element]['id']
                    self.logger.debug("Case id for element {}: {}".format(
                        element, case_id))
                    self.logger.debug("Preparing to resolve the case")
                    self.TheHiveConnector.closeCase(case_id)
                    self.logger.debug("Closed case with id {} for {}".format(
                        case_id, element))

            except Exception as e:
                self.logger.error("Could not close case: {}".format(e),
                                  exc_info=True)
                continue
Example #2
0
class Automators(Main):
    def __init__(self, cfg, use_case_config):
        self.logger = logging.getLogger(__name__)
        self.logger.info('Initiating The Hive Automator')

        self.cfg = cfg
        self.TheHiveConnector = TheHiveConnector(cfg)
        if self.cfg.getboolean('Cortex', 'enabled'):
            self.CortexConnector = CortexConnector(cfg)

        #Read mail config
        self.mailsettings = self.cfg.get('TheHive', 'mail')

    '''
    Can be used to check if there is a match between tags and the provided list.
    Useful for checking if there is a customer tag (having a list of customers) present where only one can match.
    '''

    def MatchValueAgainstTags(self, tags, list):
        for tag in tags:
            if tag in list:
                return tag

    def craftUcTask(self, title, description):
        self.logger.debug('%s.craftUcTask starts', __name__)

        self.uc_task = CaseTask(title=title, description=description)

        return self.uc_task

    def createBasicTask(self, action_config, webhook):
        #Only continue if the right webhook is triggered
        if webhook.isImportedAlert():
            pass
        else:
            return False

        #Perform actions for the CreateBasicTask action
        self.case_id = webhook.data['object']['case']
        self.title = action_config['title']
        self.description = action_config['description']

        self.logger.info('Found basic task to create: %s' % self.title)

        #Create Task
        self.uc_task = self.craftUcTask(self.title, self.description)
        self.uc_task_id = self.TheHiveConnector.createTask(
            self.case_id, self.uc_task)

        return True

    def createMailTask(self, action_config, webhook):
        #Only continue if the right webhook is triggered
        if webhook.isImportedAlert():
            pass
        else:
            return False

        self.tags = webhook.data['object']['tags']
        self.case_id = webhook.data['object']['case']
        if self.cfg.getboolean('Automation',
                               'enable_customer_list',
                               fallback=False):
            self.customer_id = self.MatchValueAgainstTags(
                self.tags, self.customers)
            self.logger.info('Found customer %s, retrieving recipient' %
                             self.customer_id)
        else:
            self.customer_id = None
        self.notification_type = "email"
        self.title = action_config['title']
        self.description = self.renderTemplate(action_config['long_template'],
                                               self.tags,
                                               webhook,
                                               self.notification_type,
                                               customer_id=self.customer_id,
                                               mail_settings=self.mailsettings)

        self.logger.info('Found mail task to create: %s' % self.title)

        #Create Task
        self.ucTask = self.craftUcTask(self.title, self.description)
        self.ucTaskId = self.TheHiveConnector.createTask(
            self.case_id, self.ucTask)
        if 'auto_send_mail' in action_config and action_config[
                'auto_send_mail'] and not self.stopsend:
            self.logger.info('Sending mail for task with id: %s' %
                             self.ucTaskId)
            self.TheHiveConnector.runResponder(
                'case_task', self.ucTaskId,
                self.use_case_config['configuration']['mail']['responder_id'])

    def runAnalyzer(self, action_config, webhook):
        #Automatically run Analyzers for newly created cases where supported IOC's are present
        if webhook.isNewArtifact():
            self.logger.debug(
                'Case artifact found. Checking if observable is of a supported type to automatically fire the analyzer'
            )

            #Retrieve caseid
            self.caseid = webhook.data['rootId']

            #List all supported ioc's for the case
            self.observable = webhook.data['object']

            #When supported, start a cortex analyzer for it
            if self.observable['dataType'] in action_config['datatypes']:
                self.supported_observable = self.observable['_id']

                #Blacklist IP addresses, make sure the blacklist is present
                if self.observable[
                        'dataType'] == "ip" and 'blacklist' in action_config and 'ip' in action_config[
                            'blacklist']:
                    for entry in action_config['blacklist']['ip']:
                        #Initial values
                        match = False
                        observable_ip = ipaddress.ip_address(
                            self.observable['data'])

                        #Match ip with CIDR syntax
                        if entry[-3:] == "/32":
                            bl_entry = ipaddress.ip_address(entry[:-3])
                            match = observable_ip == bl_entry
                        #Match ip without CIDR syntax
                        elif "/" not in entry:
                            bl_entry = ipaddress.ip_address(entry)
                            match = observable_ip == bl_entry
                        #Capture actual network entries
                        else:
                            bl_entry = ipaddress.ip_network(entry,
                                                            strict=False)
                            match = observable_ip in bl_entry

                        #If matched add it to new entries to use outside of the loop
                        if match:
                            self.logger.debug(
                                "Observable {} has matched {} of blacklist. Ignoring..."
                                .format(self.observable['data'], entry))
                            return

                #Trigger a search for the supported ioc
                self.logger.debug(
                    'Launching analyzers for observable: {}'.format(
                        self.observable['_id']))
                self.TheHiveConnector.runAnalyzer(
                    action_config['cortex_instance'],
                    self.supported_observable, action_config['analyzer'])

    def closeCaseForTaxonomyInAnalyzerResults(self, action_config, webhook):
        #If the Job result contains a successful search with minimum of 1 hit, create a task to investigate the results
        if webhook.isCaseArtifactJob() and webhook.isSuccess():
            #Case ID
            self.caseid = webhook.data['rootId']
            #Load Case information
            self.case_data = self.TheHiveConnector.getCase(self.caseid)

            self.logger.debug('Job {} has just finished'.format(
                webhook.data['object']['cortexJobId']))

            #Check if the result count higher than 0
            if webhook.data['object']['report']['summary']['taxonomies'][0][
                    'level'] in action_config["taxonomy_level"]:
                self.logger.info(
                    'Job {} has configured taxonomy level, checking if a task is already present for this observable'
                    .format(webhook.data['object']['cortexJobId']))
                #Check if task is present for investigating the new results
                if self.case_data['status'] != "Resolved":
                    self.logger.info(
                        'Case is not yet closed, closing case for {} now...'.
                        format(webhook.data['object']['cortexJobId']))
                    #Close the case
                    self.TheHiveConnector.closeCase(self.caseid)

        self.report_action = 'closeCase'

        return self.report_action

    def createTaskForTaxonomyinAnalyzerResults(self, action_config, webhook):
        #If the Job result contains a successful search with minimum of 1 hit, create a task to investigate the results
        if webhook.isCaseArtifactJob() and webhook.isSuccess():
            #Case ID
            self.caseid = webhook.data['rootId']
            #Load Case information
            self.case_data = self.TheHiveConnector.getCase(self.caseid)

            self.logger.debug('Job {} has just finished'.format(
                webhook.data['object']['cortexJobId']))

            #Check if the result count higher than 0
            if webhook.data['object']['report']['summary']['taxonomies'][0][
                    'level'] in action_config["taxonomy_level"]:
                self.logger.info(
                    'Job {} has configured taxonomy level, checking if a task is already present for this observable'
                    .format(webhook.data['object']['cortexJobId']))
                #Retrieve case task information
                self.response = self.TheHiveConnector.getCaseTasks(self.caseid)
                self.case_tasks = self.response.json()

                #Load CaseTask template
                self.casetask = CaseTask()

                #Observable + Link
                self.observable = webhook.data['object']['artifactId']
                self.observable_link = self.cfg.get(
                    'Automation', 'hive_url', fallback="https://localhost"
                ) + "/index.html#!/case/" + self.caseid + "/observables/" + webhook.data[
                    'object']['artifactId']

                #Task name
                self.casetask.title = "{} {}".format(action_config['title'],
                                                     self.observable)

                #Date
                self.date_found = time.strftime("%d-%m-%Y %H:%M")

                self.case_task_found = False
                for case_task in self.case_tasks:

                    #Check if task is present for investigating the new results
                    if self.casetask.title == case_task['title']:
                        self.case_task_found = True

                if not self.case_task_found:
                    self.logger.info(
                        'No task found, creating task for observable found in job {}'
                        .format(webhook.data['object']['cortexJobId']))
                    #Add description
                    self.casetask.description = action_config['description']
                    self.casetask.description = self.casetask.description + "\n\n {} is seen on {}\n".format(
                        self.observable_link, self.date_found)

                    #Check if case is closed
                    if self.case_data['status'] == "Resolved":
                        #Create a Case object
                        case = Case()

                        #Add the case id to the object
                        case.id = self.caseid

                        self.logger.info('Updating case %s' % case.id)

                        #Define which fields need to get updated
                        fields = ['status']

                        #Reopen the case
                        case.status = "Open"

                        #Update the case
                        self.TheHiveConnector.updateCase(case, fields)

                    #Add the case task
                    self.TheHiveConnector.createTask(self.caseid,
                                                     self.casetask)

                self.report_action = 'createTask'
                return self.report_action