Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GreyNoise analyzer v3 #1

Merged
merged 1 commit into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion analyzers/GreyNoise/GreyNoise.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "GreyNoise",
"version": "2.3",
"version": "3.0",
"author": "Nclose",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"license": "APLv2",
Expand Down
148 changes: 59 additions & 89 deletions analyzers/GreyNoise/greynoise.py
Original file line number Diff line number Diff line change
@@ -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}

Expand Down
1 change: 1 addition & 0 deletions analyzers/GreyNoise/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
cortexutils
requests
greynoise