updates for greynoise community api

pull/484/head
Brad Chiappetta 2021-03-23 13:39:36 -04:00
parent 458e432bb7
commit 2855f7ff5f
2 changed files with 89 additions and 37 deletions

View File

@ -611,16 +611,17 @@ Module to query a local copy of Maxmind's Geolite database.
Module to access GreyNoise.io API Module to access GreyNoise.io API
- **features**: - **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**: - **input**:
>An IP address. >An IP address.
- **output**: - **output**:
>Additional information about the IP fetched from Greynoise API. >Additional information about the IP fetched from GreyNoise API.
- **references**: - **references**:
> - https://greynoise.io/ > - https://greynoise.io/
> - https://github.com/GreyNoise-Intelligence/api.greynoise.io > - https://developer.greynoise.io/
- **requirements**: - **requirements**:
>A Greynoise API key. > - A GreyNoise API key.
> - A GreyNoise API Type selection of "community" or "enterprise" based API key type
----- -----

View File

@ -1,61 +1,112 @@
import requests import requests
import json import json
misperrors = {'error': 'Error'} misperrors = {"error": "Error"}
mispattributes = {'input': ['ip-dst', 'ip-src'], 'output': ['text']} mispattributes = {"input": ["ip-dst", "ip-src"], "output": ["text"]}
moduleinfo = { moduleinfo = {
'version': '0.2', "version": "0.2",
'author': 'Aurélien Schwab <aurelien.schwab+dev@gmail.com>', "author": "Aurélien Schwab <aurelien.schwab+dev@gmail.com>",
'description': 'Module to access GreyNoise.io API.', "description": "Module to access GreyNoise.io API.",
'module-type': ['hover'] "module-type": ["hover"],
} }
moduleconfig = ['api_key'] moduleconfig = ["api_key", "api_type"]
greynoise_api_url = 'https://api.greynoise.io/v2/noise/quick/'
codes_mapping = { codes_mapping = {
'0x00': 'The IP has never been observed scanning the Internet', "0x00": "The IP has never been observed scanning the Internet",
'0x01': 'The IP has been observed by the GreyNoise sensor network', "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', "0x02": "The IP has been observed scanning the GreyNoise sensor network, "
'0x03': 'The IP is adjacent to another host that has been directly observed by the GreyNoise sensor network', "but has not completed a full connection, meaning this can be spoofed",
'0x04': 'Reserved', "0x03": "The IP is adjacent to another host that has been directly observed by "
'0x05': 'This IP is commonly spoofed in Internet-scan activity', "the GreyNoise sensor network",
'0x06': 'This IP has been observed as noise, but this host belongs to a cloud provider where IPs can be cycled frequently', "0x04": "Reserved",
'0x07': 'This IP is invalid', "0x05": "This IP is commonly spoofed in Internet-scan activity",
'0x08': 'This IP was classified as noise, but has not been observed engaging in Internet-wide scans or attacks in over 60 days' "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: if q is False:
return False return False
request = json.loads(q) request = json.loads(q)
if not request.get('config') or not request['config'].get('api_key'): if not request.get("config") or not request["config"].get("api_key"):
return {'error': 'Missing Greynoise 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 = { headers = {
'Accept': 'application/json', "Accept": "application/json",
'key': request['config']['api_key'] "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: if input_type in request:
ip = request[input_type] ip = request[input_type]
break break
else: else:
misperrors['error'] = "Unsupported attributes type." misperrors["error"] = "Unsupported attributes type."
return misperrors return misperrors
response = requests.get(f'{greynoise_api_url}{ip}', headers=headers) # Real request response = requests.get(f"{greynoise_api_url}{ip}", headers=headers) # Real request
if response.status_code == 200: # OK (record found) if response.status_code == 200:
return {'results': [{'types': mispattributes['output'], 'values': codes_mapping[response.json()['code']]}]} 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 # There is an error
errors = { errors = {
400: "Bad request.", 400: "Bad request.",
404: "IP not observed scanning the internet or contained in RIOT data set.",
401: "Unauthorized. Please check your API key.", 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: try:
misperrors['error'] = errors[response.status_code] misperrors["error"] = errors[response.status_code]
except KeyError: except KeyError:
misperrors['error'] = f'GreyNoise API not accessible (HTTP {response.status_code})' misperrors[
return misperrors['error'] "error"
] = f"GreyNoise API not accessible (HTTP {response.status_code})"
return misperrors
def introspection(): def introspection():
@ -63,5 +114,5 @@ def introspection():
def version(): def version():
moduleinfo['config'] = moduleconfig moduleinfo["config"] = moduleconfig
return moduleinfo return moduleinfo