From 2855f7ff5ff63f34e4bd2ca68302cfd271851d64 Mon Sep 17 00:00:00 2001 From: Brad Chiappetta Date: Tue, 23 Mar 2021 13:39:36 -0400 Subject: [PATCH] updates for greynoise community api --- documentation/README.md | 9 +- misp_modules/modules/expansion/greynoise.py | 117 ++++++++++++++------ 2 files changed, 89 insertions(+), 37 deletions(-) diff --git a/documentation/README.md b/documentation/README.md index 78d051b..5891766 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -611,16 +611,17 @@ Module to query a local copy of Maxmind's Geolite database. Module to access GreyNoise.io API - **features**: ->The module takes an IP address as input and queries Greynoise for some additional information about it: basically it checks whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. The result is returned as text. +>The module takes an IP address as input and queries GreyNoise for some additional information about it: basically it checks whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. The result is returned as text. - **input**: >An IP address. - **output**: ->Additional information about the IP fetched from Greynoise API. +>Additional information about the IP fetched from GreyNoise API. - **references**: > - https://greynoise.io/ -> - https://github.com/GreyNoise-Intelligence/api.greynoise.io +> - https://developer.greynoise.io/ - **requirements**: ->A Greynoise API key. +> - A GreyNoise API key. +> - A GreyNoise API Type selection of "community" or "enterprise" based API key type ----- diff --git a/misp_modules/modules/expansion/greynoise.py b/misp_modules/modules/expansion/greynoise.py index 4cd89d5..9d8f803 100644 --- a/misp_modules/modules/expansion/greynoise.py +++ b/misp_modules/modules/expansion/greynoise.py @@ -1,61 +1,112 @@ import requests import json -misperrors = {'error': 'Error'} -mispattributes = {'input': ['ip-dst', 'ip-src'], 'output': ['text']} +misperrors = {"error": "Error"} +mispattributes = {"input": ["ip-dst", "ip-src"], "output": ["text"]} moduleinfo = { - 'version': '0.2', - 'author': 'Aurélien Schwab ', - 'description': 'Module to access GreyNoise.io API.', - 'module-type': ['hover'] + "version": "0.2", + "author": "Aurélien Schwab ", + "description": "Module to access GreyNoise.io API.", + "module-type": ["hover"], } -moduleconfig = ['api_key'] - -greynoise_api_url = 'https://api.greynoise.io/v2/noise/quick/' +moduleconfig = ["api_key", "api_type"] codes_mapping = { - '0x00': 'The IP has never been observed scanning the Internet', - '0x01': 'The IP has been observed by the GreyNoise sensor network', - '0x02': 'The IP has been observed scanning the GreyNoise sensor network, but has not completed a full connection, meaning this can be spoofed', - '0x03': 'The IP is adjacent to another host that has been directly observed by the GreyNoise sensor network', - '0x04': 'Reserved', - '0x05': 'This IP is commonly spoofed in Internet-scan activity', - '0x06': 'This IP has been observed as noise, but this host belongs to a cloud provider where IPs can be cycled frequently', - '0x07': 'This IP is invalid', - '0x08': 'This IP was classified as noise, but has not been observed engaging in Internet-wide scans or attacks in over 60 days' + "0x00": "The IP has never been observed scanning the Internet", + "0x01": "The IP has been observed by the GreyNoise sensor network", + "0x02": "The IP has been observed scanning the GreyNoise sensor network, " + "but has not completed a full connection, meaning this can be spoofed", + "0x03": "The IP is adjacent to another host that has been directly observed by " + "the GreyNoise sensor network", + "0x04": "Reserved", + "0x05": "This IP is commonly spoofed in Internet-scan activity", + "0x06": "This IP has been observed as noise, but this host belongs to a cloud " + "provider where IPs can be cycled frequently", + "0x07": "This IP is invalid", + "0x08": "This IP was classified as noise, but has not been observed engaging in " + "Internet-wide scans or attacks in over 60 days", } -def handler(q=False): +def handler(q=False): # noqa: C901 if q is False: return False request = json.loads(q) - if not request.get('config') or not request['config'].get('api_key'): - return {'error': 'Missing Greynoise API key.'} + if not request.get("config") or not request["config"].get("api_key"): + return {"error": "Missing Greynoise API key."} + if request["config"]["api_type"] and request["config"]["api_type"] == "enterprise": + greynoise_api_url = "https://api.greynoise.io/v2/noise/quick/" + else: + greynoise_api_url = "https://api.dev.greynoise.io/v3/community/" + headers = { - 'Accept': 'application/json', - 'key': request['config']['api_key'] + "Accept": "application/json", + "key": request["config"]["api_key"], + "User-Agent": "greynoise-misp-module-{}".format(moduleinfo["version"]), } - for input_type in mispattributes['input']: + for input_type in mispattributes["input"]: if input_type in request: ip = request[input_type] break else: - misperrors['error'] = "Unsupported attributes type." + misperrors["error"] = "Unsupported attributes type." return misperrors - response = requests.get(f'{greynoise_api_url}{ip}', headers=headers) # Real request - if response.status_code == 200: # OK (record found) - return {'results': [{'types': mispattributes['output'], 'values': codes_mapping[response.json()['code']]}]} + response = requests.get(f"{greynoise_api_url}{ip}", headers=headers) # Real request + if response.status_code == 200: + if request["config"]["api_type"] == "enterprise": + return { + "results": [ + { + "types": ["text"], + "values": codes_mapping[response.json()["code"]], + } + ] + } + elif response.json()["noise"]: + return { + "results": [ + { + "types": ["text"], + "values": "IP Address ({}) has been observed by GreyNoise " + "scanning the internet in the last 90 days. GreyNoise has " + "classified it as {} and it was last seen on {}. For more " + "information visit {}".format( + response.json()["ip"], + response.json()["classification"], + response.json()["last_seen"], + response.json()["link"], + ), + } + ] + } + elif response.json()["riot"]: + return { + "results": [ + { + "types": ["text"], + "values": "IP Address ({}) is part of GreyNoise Project RIOT " + "and likely belongs to a benign service from {}. For more " + "information visit {}".format( + response.json()["ip"], + response.json()["name"], + response.json()["link"], + ), + } + ] + } # There is an error errors = { 400: "Bad request.", + 404: "IP not observed scanning the internet or contained in RIOT data set.", 401: "Unauthorized. Please check your API key.", - 429: "Too many requests. You've hit the rate-limit." + 429: "Too many requests. You've hit the rate-limit.", } try: - misperrors['error'] = errors[response.status_code] + misperrors["error"] = errors[response.status_code] except KeyError: - misperrors['error'] = f'GreyNoise API not accessible (HTTP {response.status_code})' - return misperrors['error'] + misperrors[ + "error" + ] = f"GreyNoise API not accessible (HTTP {response.status_code})" + return misperrors def introspection(): @@ -63,5 +114,5 @@ def introspection(): def version(): - moduleinfo['config'] = moduleconfig + moduleinfo["config"] = moduleconfig return moduleinfo