mirror of https://github.com/MISP/misp-modules
Merge branch 'main' of github.com:MISP/misp-modules into new_features
commit
8935c4adc5
|
@ -132,7 +132,7 @@ sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev tesseract-ocr li
|
||||||
sudo -u www-data virtualenv -p python3 /var/www/MISP/venv
|
sudo -u www-data virtualenv -p python3 /var/www/MISP/venv
|
||||||
cd /usr/local/src/
|
cd /usr/local/src/
|
||||||
sudo chown -R www-data: .
|
sudo chown -R www-data: .
|
||||||
sudo git clone https://github.com/MISP/misp-modules.git
|
sudo -u www-data git clone https://github.com/MISP/misp-modules.git
|
||||||
cd misp-modules
|
cd misp-modules
|
||||||
sudo -u www-data /var/www/MISP/venv/bin/pip install -I -r REQUIREMENTS
|
sudo -u www-data /var/www/MISP/venv/bin/pip install -I -r REQUIREMENTS
|
||||||
sudo -u www-data /var/www/MISP/venv/bin/pip install .
|
sudo -u www-data /var/www/MISP/venv/bin/pip install .
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -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": "1.0",
|
||||||
'author': 'Aurélien Schwab <aurelien.schwab+dev@gmail.com>',
|
"author": "Brad Chiappetta <brad@greynoise.io>",
|
||||||
'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.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
|
||||||
|
|
Loading…
Reference in New Issue