diff --git a/analyzers/NERD/nerd_analyzer.py b/analyzers/NERD/nerd_analyzer.py index 4ee11b46c21245b1f92492f30b2718cd3cb2ed44..73f744533b96213195b8665d07c6c34233f42d29 100644 --- a/analyzers/NERD/nerd_analyzer.py +++ b/analyzers/NERD/nerd_analyzer.py @@ -25,10 +25,10 @@ tag_map = { class NERDAnalyzer(Analyzer): def __init__(self): Analyzer.__init__(self) - self.base_url = self.get_param('config.url', None, 'Missing URL of NERD.') + self.base_url = self.get_param('config.url', None, 'Config error: Missing URL of NERD.') if not self.base_url.endswith('/'): self.base_url += '/' - self.api_key = self.get_param('config.key', None, 'Missing API key for NERD.') + self.api_key = self.get_param('config.key', None, 'Config error: Missing API key for NERD.') def summary(self, raw): """Returns a summary, needed for 'short.html' template, based on the full report""" @@ -48,11 +48,7 @@ class NERDAnalyzer(Analyzer): taxonomies.append(self.build_taxonomy('malicious', 'NERD', 'Blacklists', len(raw['blacklists']))) # Tags - for tag in raw['tags']: - try: - tag_name, level = tag_map[tag] - except KeyError: - continue + for tag_name,level in raw['translated_tags']: taxonomies.append(self.build_taxonomy(level, 'NERD', 'Tag', tag_name)) return {'taxonomies': taxonomies} @@ -65,12 +61,10 @@ class NERDAnalyzer(Analyzer): def run(self): """Main function run by Cortex to analyze an observable.""" - print("RUN") if self.data_type != 'ip': self.error("Invalid data type, only IP addresses are supported") return ip = self.get_data() - print("ip:", ip) # Get data from server url = '{}api/v1/ip/{}'.format(self.base_url, ip) @@ -85,26 +79,39 @@ class NERDAnalyzer(Analyzer): try: data = resp.json() except ValueError: - self.error("Invalid response received from server.") + self.error("Unexpected or invalid response received from server (can't parse as JSON). A possible reason can be wrong URL.") return if resp.status_code == 404: # IP not found in NERD's DB (i.e. it wasn't reported as malicious) self.report({ 'rep': 0.0, - 'message': '{} not found in NERD, i.e. there are no recent reports of malicious activity.'.format(ip) + 'message': '{} not found in NERD, i.e. there are no recent reports of malicious activity.'.format(ip), + 'nerd_url': '{}ip/{}'.format(self.base_url, ip), # Link to IP's page at NERD web }) return elif resp.status_code == 200: # Success, IP data received - format as output for Cortex try: + # Translate tags + translated_tags = [] + tag_ids = [t['n'] for t in data['tags'] if t.get('c', 1.0) >= 0.5] # List of tags with confidence >= 50% + for tag in tag_ids: + try: + tag_name, level = tag_map[tag] + except KeyError: + continue + translated_tags.append([tag_name, level]) + # Create report self.report({ 'rep': data['rep'], # reputation score (number between 0.0 to 1.0) 'hostname': data['hostname'], # result of DNS PTR qeury 'asn': data['asn'], # list of ASNs announcing the IP (usually just one) - 'country': data['geo']['ctry'], # Geolocation - two-letter country code + 'country': data['geo'].get('ctry', ''), # Geolocation - two-letter country code 'blacklists': data['bl'], # List of blacklists the IP is listed on - 'tags': [t['n'] for t in data['tags'] if t.get('c', 1.0) >= 0.5] # List of tags (with confidence >= 50%) + 'tags': tag_ids, # Original Tags as in NERD + 'translated_tags': translated_tags, # Tags filtered and translated to nicer names + 'nerd_url': '{}ip/{}'.format(self.base_url, ip), # Link to IP's page at NERD web }) except KeyError as e: self.error("Invalid response received from server, missing field: {}".format(e)) diff --git a/thehive-templates/NERD_1_0/long.html b/thehive-templates/NERD_1_0/long.html new file mode 100644 index 0000000000000000000000000000000000000000..787d7196c55aae3f720a09222655a69470c1e4da --- /dev/null +++ b/thehive-templates/NERD_1_0/long.html @@ -0,0 +1,52 @@ +<div class="panel panel-info" ng-if="success"> + <div class="panel-heading"> + NERD information for <strong>{{artifact.data}}</strong> + </div> + <div class="panel-body" ng-if="content.message"> + <p>{{content.message}}</p> + <p>Current info at: <a href="{{content.nerd_url}}" target="_blank">{{content.nerd_url}}</a></p> + </div> + <div class="panel-body" ng-if="!content.message"> + <dl class="dl-horizontal"> + <dt class="text-bold"> + Reputation score + <sup style="color: #999" title="A score computed from the number of recent alerts, their age, and the number of distinct sources (0.0 = no alerts, 1.0 = lots of alerts from multiple sources)">(?)</sup> + </dt> + <dd><span style="color: hsl({{ (100 - 100*content.rep | number: 2) }}, 85%, 50%); margin-right: 5px">⬤</span>{{content.rep | number: 3}}</dd> + </dl> + <dl class="dl-horizontal"> + <dt class="text-bold">Hostname</dt> + <dd>{{content.hostname || "—"}}</dd> + </dl> + <dl class="dl-horizontal"> + <dt class="text-bold">Blacklists</dt> + <dd>{{content.blacklists.join(", ") || "—"}}</dd> + </dl> + <dl class="dl-horizontal"> + <dt class="text-bold">Tags</dt> + <dd> + <span ng-repeat="t in content.translated_tags" class="label" style="margin-right: 5px" ng-class="{'info': 'label-info', 'safe': 'label-success', 'suspicious': 'label-warning', 'malicious':'label-danger'}[t[1]]">{{t[0]}}</span> + <span ng-if="!content.translated_tags.length">—</span> + </dd> + </dl> + <dl class="dl-horizontal"> + <dt class="text-bold">ASN</dt> + <dd>{{content.asn.join(", ") || "unknown"}}</dd> + </dl> + <dl class="dl-horizontal"> + <dt class="text-bold">Country</dt> + <dd>{{content.country || "unknown"}}</dd> + </dl> + <p>Full info at: <a href="{{content.nerd_url}}" target="_blank">{{content.nerd_url}}</a></p> + </div> +</div> + +<!-- General error --> +<div class="panel panel-danger" ng-if="!success"> + <div class="panel-heading"> + <strong>{{artifact.data}}</strong> + </div> + <div class="panel-body"> + {{content.errorMessage}} + </div> +</div> \ No newline at end of file diff --git a/thehive-templates/NERD_1_0/short.html b/thehive-templates/NERD_1_0/short.html new file mode 100644 index 0000000000000000000000000000000000000000..9fd48f9fa20b5dfb9dcb41bf64a4a977b59ad2b4 --- /dev/null +++ b/thehive-templates/NERD_1_0/short.html @@ -0,0 +1,3 @@ +<span class="label" ng-repeat="t in content.taxonomies" ng-class="{'info': 'label-info', 'safe': 'label-success', 'suspicious': 'label-warning', 'malicious':'label-danger'}[t.level]"> + {{t.namespace}}:{{t.predicate}}="{{t.value}}" +</span> \ No newline at end of file