diff --git a/analyzers/GreyNoise/GreyNoise.json b/analyzers/GreyNoise/GreyNoise.json index 97457b97e..af7d0932d 100644 --- a/analyzers/GreyNoise/GreyNoise.json +++ b/analyzers/GreyNoise/GreyNoise.json @@ -1,6 +1,6 @@ { "name": "GreyNoise", - "version": "2.3", + "version": "3.0", "author": "Nclose", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "APLv2", diff --git a/analyzers/GreyNoise/greynoise.py b/analyzers/GreyNoise/greynoise.py index cf9e52b1e..8dcd1dba8 100755 --- a/analyzers/GreyNoise/greynoise.py +++ b/analyzers/GreyNoise/greynoise.py @@ -1,134 +1,104 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import requests - from collections import defaultdict, OrderedDict + from cortexutils.analyzer import Analyzer +from greynoise import GreyNoise +import requests class GreyNoiseAnalyzer(Analyzer): """ - GreyNoise API docs: https://github.com/GreyNoise-Intelligence/api.greynoise.io + GreyNoise API docs: https://developer.greynoise.io/reference#noisecontextip-1 """ - @staticmethod - def _get_level(current_level, new_intention): - """ - Map GreyNoise intentions to Cortex maliciousness levels. - Accept a Cortex level and a GreyNoise intention, the return the more malicious of the two. - - :param current_level: A Cortex maliciousness level - https://github.com/TheHive-Project/CortexDocs/blob/master/api/how-to-create-an-analyzer.md#output - :param new_intention: An intention field value from a GreyNoise record - https://github.com/GreyNoise-Intelligence/api.greynoise.io#v1queryip - :return: The more malicious of the 2 submitted values as a Cortex maliciousness level - """ - - intention_level_map = OrderedDict([ - ('info', 'info'), - ('benign', 'safe'), - ('suspicious', 'suspicious'), - ('malicious', 'malicious') - ]) - levels = intention_level_map.values() - - new_level = intention_level_map.get(new_intention, 'info') - new_index = levels.index(new_level) - - try: - current_index = levels.index(current_level) - except ValueError: # There is no existing level - current_index = -1 - - return new_level if new_index > current_index else current_level - def run(self): if self.data_type == "ip": api_key = self.get_param('config.key', None) - url = 'https://api.greynoise.io/v1/query/ip' - headers = {'Content-Type': 'application/x-www-form-urlencoded'} - data = {'ip': self.get_data()} - if api_key: - data['key'] = api_key - response = requests.post(url, data=data, headers=headers) - if not (200 <= response.status_code < 300): - self.error('Unable to query GreyNoise API\n{}'.format(response.text)) - self.report(response.json()) + api_client = GreyNoise(api_key=api_key, timeout=30) + try: + self.report(api_client.ip(self.get_data())) + except Exception as e: + self.error('Unable to query GreyNoise API\n{}'.format(e)) else: self.notSupported() def summary(self, raw): """ - Return one taxonomy summarizing the reported tags - If there is only one tag, use it as the predicate - If there are multiple tags, use "entries" as the predicate - Use the total count as the value - Use the most malicious level found - + Return two taxonomies Examples: - Input { - "name": SCANNER1, - "intention": "" + "actor": "SCANNER1", + "classification": "benign", + "tags": ['a', 'b', 'c'] } Output - GreyNoise:SCANNER1 = 1 (info) - + GreyNoise:tags = 3 (Safe) + GreyNoise:actor = SCANNER1 (Safe) Input { - "name": SCANNER1, - "intention": "malicious" - }, - { - "name": SCANNER1, - "intention": "benign" + "actor": "SCANNER1", + "classification": "unknown", + "tags": ['a', 'b', 'c'] } Output - GreyNoise:SCANNER1 = 2 (malicious) - + GreyNoise:tags = 3 (Suspicious) + GreyNoise:classification = unknown (Info) Input { - "name": SCANNER1, - "intention": "" - }, - { - "name": SCANNER1, - "intention": "safe" - }, + "actor": "SCANNER1", + "classification": "unknown", + "tags": ['a', 'b'] + } + Output + GreyNoise:tags = 2 (Info) + GreyNoise:classification = unknown (Info) + + Input { - "name": SCANNER2, - "intention": "" + "actor": "SCANNER1", + "classification": "malicious", + "tags": ['a', 'b', 'c'] } Output - GreyNoise:entries = 3 (safe) + GreyNoise:tags = 3 (Malicious) + GreyNoise:classification = malicious (Malicious) """ + + classification_level_map = { + 'benign': lambda x: 'Safe', + 'unknown': lambda tag_count: 'Info' if (not tag_count) or (tag_count <= 2) else 'Suspicious', + 'malicious': lambda x: 'Malicious' + } + try: taxonomies = [] - if raw.get('records'): - final_level = None - taxonomy_data = defaultdict(int) - for record in raw.get('records', []): - name = record.get('name', 'unknown') - intention = record.get('intention', 'unknown') - taxonomy_data[name] += 1 - final_level = self._get_level(final_level, intention) - - if len(taxonomy_data) > 1: # Multiple tags have been found - taxonomies.append(self.build_taxonomy(final_level, 'GreyNoise', 'entries', len(taxonomy_data))) - else: # There is only one tag found, possibly multiple times - for name, count in taxonomy_data.iteritems(): - taxonomies.append(self.build_taxonomy(final_level, 'GreyNoise', name, count)) - - else: - taxonomies.append(self.build_taxonomy('info', 'GreyNoise', 'Records', 'None')) + + tag_count = len(raw.get('tags', [])) + classification = raw.get('classification', 'unknown') + actor = raw.get('actor') + + t1_level = classification_level_map.get(classification)(tag_count) + t1_namespace = 'GreyNoise' + t1_predicate = 'tags' + t1_value = tag_count + # print('{}:{} = {} ({})'.format(t1_namespace, t1_predicate, t1_value, t1_level)) + taxonomies.append(self.build_taxonomy(t1_level, t1_namespace, t1_predicate, t1_value)) + + t2_level = classification_level_map.get(classification)(None) + t2_namespace = 'GreyNoise' + t2_predicate = 'actor' if classification == 'benign' else 'classification' + t2_value = actor if classification == 'benign' else classification + # print('{}:{} = {} ({})'.format(t2_namespace, t2_predicate, t2_value, t2_level)) + taxonomies.append(self.build_taxonomy(t2_level, t2_namespace, t2_predicate, t2_value)) return {"taxonomies": taxonomies} diff --git a/analyzers/GreyNoise/requirements.txt b/analyzers/GreyNoise/requirements.txt index 6aabc3cfa..a494208ea 100644 --- a/analyzers/GreyNoise/requirements.txt +++ b/analyzers/GreyNoise/requirements.txt @@ -1,2 +1,3 @@ cortexutils requests +greynoise