Пример #1
0
class ThreatConnectPlugin(Plugin):
    @property
    def enabled(self):
        return settings.THREATCONNECT_ACCESS_ID is not None and \
            settings.THREATCONNECT_SECRET_KEY is not None

    def activate(self):
        logger.debug('Activating %s' % self.__class__.__name__)
        self.service = ThreatConnect(settings.THREATCONNECT_ACCESS_ID,
                                     settings.THREATCONNECT_SECRET_KEY,
                                     settings.THREATCONNECT_DEFAULT_ORG,
                                     settings.THREATCONNECT_BASE_URL)
        self.service.set_api_result_limit(100)

    def retrieve_indicator(self, indicator, indicator_type=None):
        indicators = self.service.indicators()
        if indicator_type:
            f = indicators.add_filter(indicator_type)
        else:
            f = indicators.add_filter()
        f.add_owner(['Common Community'])
        f.add_indicator(indicator)
        results = sorted(indicators.retrieve(), key=lambda x: x.rating)
        return results[-1] if results else None

    def threshold_met(self, report):
        return hasattr(report, 'rating') and report.rating >= 1 and \
            hasattr(report, 'confidence') and report.confidence >= 50

    def react(self):
        if not any(self.reports.values()):
            return
        rating = max([0] + [
            r.rating for r in self.reports.values()
            if r and hasattr(r, 'rating') and r.rating
        ])
        return self.reaction(rating)

    def reaction(self, score):
        if score == 0:
            return 'sunny'
        elif 0 <= score < 0.5:
            return 'mostly_sunny'
        elif 0.5 <= score < 1:
            return 'partly_sunny'
        elif 1 <= score < 2:
            return 'barely_sunny'
        elif 2 <= score < 2.5:
            return 'cloud'
        elif 2.5 <= score < 3:
            return 'rain_cloud'
        elif 3 <= score < 3.5:
            return 'thunder_cloud_and_rain'
        elif 3.5 <= score < 4:
            return 'lightning'
        elif score >= 4:
            return 'tornado'

    def risk_level(self, score):
        try:
            score = float(score)
        except TypeError:
            return 'UNKNOWN'

        if score == 0:
            return 'UNKNOWN'
        elif 0 < score < 1:
            return 'VERY LOW'
        elif 1 <= score < 2:
            return 'LOW'
        elif 2 <= score < 3:
            return 'MODERATE'
        elif 3 <= score < 4:
            return 'HIGH'
        elif score >= 4:
            return 'VERY HIGH'
class CbThreatConnectConnector(object):
    def __init__(self,
                 access_id="",
                 secret_key="",
                 default_org="",
                 base_url="",
                 out_file="tc.json",
                 sources="",
                 ioc_types="",
                 custom_ioc_key="",
                 feed_url="",
                 cb_server_token="",
                 cb_server_url="https://127.0.0.1",
                 cb_server_ssl_verify=False,
                 ioc_min=None,
                 niceness=None,
                 debug=False,
                 log_file=None,
                 max_iocs=5000):

        logger.info("ThreatConnect Base URL: {0}".format(base_url))

        self.tcapi = ThreatConnect(api_aid=access_id,
                                   api_sec=secret_key,
                                   api_url=base_url,
                                   api_org=default_org)

        self.sources = sources

        self.ioc_min = ioc_min

        self.ioc_types = ioc_types

        logger.info("Configured IOC Types are : {0}".format(self.ioc_types))
        logger.info("Configured IOC Min is  : {0}".format(self.ioc_min))

        self.custom_ioc_key = custom_ioc_key

        self.max_iocs = max_iocs

        if self.sources[0] == "*":
            owners = self.tcapi.owners()
            try:
                # retrieve the Owners
                owners.retrieve()
            except RuntimeError as e:
                logger.error(traceback.format_exc())
                sys.exit(1)
            # iterate through the Owners
            self.sources = [owner.name for owner in owners]

        logger.info("Sources = {0}".format(self.sources))

        self.niceness = niceness
        if self.niceness is not None:
            os.nice(self.niceness)

        self.debug = debug
        if self.debug:
            logger.setLevel(logging.DEBUG)

        self.log_file = log_file

        self.out_file = out_file

        self.feed = None

        self.cb = CbResponseAPI(url=cb_server_url,
                                token=cb_server_token,
                                ssl_verify=cb_server_ssl_verify)

        self.feed_url = feed_url

    def stop(self):
        self.stopEvent.set()

    def getDebugMode(self):
        return self._debug

    def setDebugMode(self, debugOn):
        self._debug = debugOn
        if self._debug == True:
            logger.setLevel(logging.DEBUG)
        else:
            logger.setLevel(logging.INFO)

    debug = property(getDebugMode, setDebugMode)

    def _PollThreatConnect(self):
        self.generate_feed_from_threatconnect()
        self.createFeed()
        last = None
        while (True):
            if self.stopEvent.isSet():
                logger.info(
                    "Threatconnect Connector was signalled to stop...stopping")
                return
            else:
                #poll threat connect if the time delta has passed since the last time we did
                now = datetime.now()
                delta = now - last if last is not None else self.interval
                last = now
                if delta >= self.interval:
                    self.generate_feed_from_threatconnect()
                else:
                    time.sleep(self.interval.seconds + 1)
                    logger.debug("Done sleeping...")

    def RunForever(self):
        logger.info("ThreatConnect agent starting...")
        threading.Thread(target=self._PollThreatConnect).start()

    def createFeed(self):
        if self.feed is not None:
            self.feed.upload(self.cb, self.feed_url)

    def generate_feed_from_threatconnect(self):
        first = True
        reports = []
        feedinfo = {
            'name': 'threatconnect',
            'display_name': "ThreatConnect",
            'provider_url': "http://www.threatconnect.com",
            'summary':
            "Sends threat intelligence from Threatconnect platform to Carbon Black Response",
            'tech_data':
            "There are no requirements to share any data with Carbon Black to use this feed.",
            'icon': 'threatconnect-logo.png',
            'category': "Connectors",
        }

        feedinfo = CbFeedInfo(**feedinfo)
        self.feed = CbFeed(feedinfo, reports)
        created_feed = self.feed.dump(validate=False, indent=0)
        with open(self.out_file, 'w') as fp:
            fp.write(created_feed)

            fp.seek(0)

            offset = len(created_feed) - 1

            # create an Indicators object
            for source in self.sources:
                for t in self.ioc_types:
                    indicators = self.tcapi.indicators()
                    filter1 = indicators.add_filter()
                    # filter1.add_owner(source)
                    filter1.add_pf_type(t, FilterOperator.EQ)
                    if self.ioc_min is not None:
                        filter1.add_pf_rating(self.ioc_min, FilterOperator.GE)
                    try:
                        # retrieve Indicators
                        indicators.retrieve()
                    except RuntimeError as e:
                        print('Error: {0}'.format(e))

                    logger.info("Number of indicators:{0}".format(
                        len(indicators)))

                    for index, indicator in enumerate(indicators):

                        if index > self.max_iocs:
                            logger.info("Max number of IOCs reached")
                            break
                        # print (indicator.type)
                        score = indicator.rating * 20 if indicator.rating is not None else 0
                        # int(row.get('rating', 0)) * 20
                        # Many entries are missing a description so I placed this here to default them
                        # to the IOC value in the absence of a description.
                        title = indicator.description if indicator.description is not None else "{0}-{1}".format(
                            source,
                            indicator.id)  # row.get('description', None)
                        # if not title:
                        #    title = row.get('summary')
                        fields = {
                            'iocs': {},
                            'id':
                            str(indicator.id),
                            'link':
                            indicator.weblink,
                            'title':
                            title,
                            'score':
                            int(score),
                            'timestamp':
                            int(
                                datetime.strptime(
                                    indicator.date_added,
                                    "%Y-%m-%dT%H:%M:%SZ").timestamp()),
                        }
                        # The next few lines are designed to insert the Cb supported IOCs into the record.
                        logger.debug("Indacator is {0}".format(indicator))
                        if indicator.type == "File":
                            fields['iocs'] = {
                                k: [indicator.indicator[k]]
                                for k in indicator.indicator
                                if indicator.indicator[k] is not None
                            }
                        elif indicator.type == "Address":
                            fields['iocs']['ipv4'] = [indicator.indicator]
                        elif indicator.type == "Host":
                            fields['iocs']['dns'] = [indicator.indicator]
                        else:
                            squery = urllib.parse.urlencode({
                                "cb.urlver":
                                "1",
                                "q":
                                indicator.indicator[self.custom_ioc_key]
                            })
                            fields['iocs']['query'] = [{
                                'index_type': 'modules',
                                'search_query': squery
                            }]

                        report = CbReport(**fields)
                        try:
                            report.dump(validate=True)
                        except:
                            logger.info("This query is not valid: {0}".format(
                                indicator.indicator[self.custom_ioc_key]))
                            continue
                        # APPEND EACH NEW REPORT ONTO THE LIST IN THE JSON FEED
                        # THIS METHOD IS VERY LONG LIVED
                        # THIS METHOD CALL WILL LAST FOR
                        #  HOURS -> DAYS IN LARGE ORGS
                        reports.append(report)
                        self.feed = CbFeed(feedinfo, reports)
                    fp.write(self.feed.dump(validate=False, indent=0))
Пример #3
0
class ThreatConnectPlugin(Plugin):
    @property
    def enabled(self):
        return settings.THREATCONNECT_ACCESS_ID is not None and \
            settings.THREATCONNECT_SECRET_KEY is not None

    def activate(self):
        logger.debug('Activating %s' % self.__class__.__name__)
        self.service = ThreatConnect(
            settings.THREATCONNECT_ACCESS_ID,
            settings.THREATCONNECT_SECRET_KEY,
            settings.THREATCONNECT_DEFAULT_ORG,
            settings.THREATCONNECT_BASE_URL)
        self.service.set_api_result_limit(100)

    def retrieve_indicator(self, indicator, indicator_type=None):
        indicators = self.service.indicators()
        if indicator_type:
            f = indicators.add_filter(indicator_type)
        else:
            f = indicators.add_filter()
        f.add_owner(['Common Community'])
        f.add_indicator(indicator)
        results = sorted(indicators.retrieve(), key=lambda x: x.rating)
        return results[-1] if results else None

    def threshold_met(self, report):
        return hasattr(report, 'rating') and report.rating >= 1 and \
            hasattr(report, 'confidence') and report.confidence >= 50

    def react(self):
        if not any(self.reports.values()):
            return
        rating = max([0] + [r.rating for r in self.reports.values()
                     if r and hasattr(r, 'rating') and r.rating])
        return self.reaction(rating)

    def reaction(self, score):
        if score == 0:
            return 'sunny'
        elif 0 <= score < 0.5:
            return 'mostly_sunny'
        elif 0.5 <= score < 1:
            return 'partly_sunny'
        elif 1 <= score < 2:
            return 'barely_sunny'
        elif 2 <= score < 2.5:
            return 'cloud'
        elif 2.5 <= score < 3:
            return 'rain_cloud'
        elif 3 <= score < 3.5:
            return 'thunder_cloud_and_rain'
        elif 3.5 <= score < 4:
            return 'lightning'
        elif score >= 4:
            return 'tornado'

    def risk_level(self, score):
        try:
            score = float(score)
        except TypeError:
            return 'UNKNOWN'

        if score == 0:
            return 'UNKNOWN'
        elif 0 < score < 1:
            return 'VERY LOW'
        elif 1 <= score < 2:
            return 'LOW'
        elif 2 <= score < 3:
            return 'MODERATE'
        elif 3 <= score < 4:
            return 'HIGH'
        elif score >= 4:
            return 'VERY HIGH'