2022-07-12 09:09:30 +02:00
|
|
|
import json
|
|
|
|
import pycountry
|
|
|
|
import requests
|
2023-05-11 16:12:07 +02:00
|
|
|
from . import check_input_attribute, standard_error_message
|
|
|
|
from pymisp import MISPEvent, MISPObject
|
2022-07-12 09:09:30 +02:00
|
|
|
|
2023-05-11 16:12:07 +02:00
|
|
|
mispattributes = {"input": ["ip-dst", "ip-src"], "format": "misp_standard"}
|
2022-07-12 09:09:30 +02:00
|
|
|
moduleinfo = {
|
2023-05-26 14:26:26 +02:00
|
|
|
"version": "2.0",
|
2022-07-12 09:09:30 +02:00
|
|
|
"author": "Shivam Sandbhor <shivam@crowdsec.net>",
|
|
|
|
"description": "Module to access CrowdSec CTI API.",
|
2023-05-11 15:18:26 +02:00
|
|
|
"module-type": ["hover", "expansion"],
|
2022-07-12 09:09:30 +02:00
|
|
|
}
|
2023-05-11 16:12:07 +02:00
|
|
|
moduleconfig = ["api_key"]
|
2022-07-12 09:09:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
def handler(q=False):
|
|
|
|
if q is False:
|
|
|
|
return False
|
|
|
|
|
|
|
|
request = json.loads(q)
|
|
|
|
if not request.get("config"):
|
|
|
|
return {"error": "Missing CrowdSec Config"}
|
|
|
|
|
|
|
|
if not request["config"].get("api_key"):
|
|
|
|
return {"error": "Missing CrowdSec API key"}
|
|
|
|
|
2023-05-11 16:12:07 +02:00
|
|
|
if not request.get('attribute') or not check_input_attribute(request['attribute']):
|
|
|
|
return {'error': f'{standard_error_message}, which shoul contain at least a type, a value and an uuid.'}
|
|
|
|
|
|
|
|
if request['attribute'].get('type') not in mispattributes['input']:
|
|
|
|
return {'error': f"Wrong input type. Please choose on of the following: {', '.join(mispattributes['input'])}"}
|
2022-07-12 09:09:30 +02:00
|
|
|
|
2023-05-11 16:12:07 +02:00
|
|
|
return _handler_v2(request)
|
2022-07-12 09:09:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
def _handler_v2(request_data):
|
2023-05-12 12:16:22 +02:00
|
|
|
attribute = request_data['attribute']
|
|
|
|
ip = attribute['value']
|
2022-07-12 09:09:30 +02:00
|
|
|
|
|
|
|
crowdsec_cti = requests.get(
|
|
|
|
f"https://cti.api.crowdsec.net/v2/smoke/{ip}",
|
2023-02-20 05:41:38 +01:00
|
|
|
headers={
|
2023-05-26 14:26:26 +02:00
|
|
|
"x-api-key": request_data["config"]["api_key"],
|
|
|
|
"User-Agent": "crowdsec-misp/v1.0.0",
|
|
|
|
}
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
|
|
|
crowdsec_cti.raise_for_status()
|
|
|
|
crowdsec_cti = crowdsec_cti.json()
|
|
|
|
|
|
|
|
misp_event = MISPEvent()
|
2023-05-12 12:16:22 +02:00
|
|
|
misp_attribute = misp_event.add_attribute(**attribute)
|
2022-07-12 09:09:30 +02:00
|
|
|
crowdsec_context_object = MISPObject("crowdsec-ip-context")
|
2023-05-12 12:16:22 +02:00
|
|
|
crowdsec_context_object.from_dict(
|
|
|
|
first_seen=crowdsec_cti["history"]["first_seen"],
|
|
|
|
last_seen=crowdsec_cti["history"]["last_seen"]
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
2023-05-26 14:26:26 +02:00
|
|
|
ip_attribute = crowdsec_context_object.add_attribute("ip", crowdsec_cti["ip"])
|
2023-05-12 12:16:22 +02:00
|
|
|
crowdsec_context_object.add_attribute("ip-range", crowdsec_cti["ip_range"])
|
|
|
|
crowdsec_context_object.add_attribute("ip-range-score", crowdsec_cti["ip_range_score"])
|
2022-07-12 09:09:30 +02:00
|
|
|
crowdsec_context_object.add_attribute(
|
2023-05-12 12:16:22 +02:00
|
|
|
"country", get_country_name_from_alpha_2(crowdsec_cti["location"]["country"])
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
2023-05-12 12:16:22 +02:00
|
|
|
crowdsec_context_object.add_attribute("country-code", crowdsec_cti["location"]["country"])
|
|
|
|
if crowdsec_cti["location"].get("city"):
|
2022-07-12 09:09:30 +02:00
|
|
|
crowdsec_context_object.add_attribute(
|
2023-05-12 12:16:22 +02:00
|
|
|
"city", crowdsec_cti["location"]["city"]
|
|
|
|
)
|
|
|
|
crowdsec_context_object.add_attribute("latitude", crowdsec_cti["location"]["latitude"])
|
|
|
|
crowdsec_context_object.add_attribute("longitude", crowdsec_cti["location"]["longitude"])
|
|
|
|
crowdsec_context_object.add_attribute("as-name", crowdsec_cti["as_name"])
|
|
|
|
crowdsec_context_object.add_attribute("as-num", crowdsec_cti["as_num"])
|
2023-05-23 13:20:52 +02:00
|
|
|
if crowdsec_cti.get('reverse_dns') is not None:
|
|
|
|
crowdsec_context_object.add_attribute("reverse-dns", crowdsec_cti["reverse_dns"])
|
2023-05-23 13:34:52 +02:00
|
|
|
crowdsec_context_object.add_attribute('background-noise', crowdsec_cti['background_noise_score'])
|
2023-05-12 12:16:22 +02:00
|
|
|
for behavior in crowdsec_cti["behaviors"]:
|
|
|
|
crowdsec_context_object.add_attribute(
|
|
|
|
"behaviors", behavior["label"],
|
|
|
|
comment=behavior['description']
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
2023-05-26 14:26:26 +02:00
|
|
|
tag = f'crowdsec:behavior="{behavior["name"]}"'
|
|
|
|
ip_attribute.add_tag(tag)
|
|
|
|
for feature, values in crowdsec_cti['classifications'].items():
|
|
|
|
field = feature[:-1]
|
|
|
|
for value in values:
|
|
|
|
crowdsec_context_object.add_attribute(
|
|
|
|
feature, value['label'], comment=value['description']
|
|
|
|
)
|
|
|
|
tag = f'crowdsec:{field}="{value["name"]}"'
|
|
|
|
ip_attribute.add_tag(tag)
|
2022-07-12 09:09:30 +02:00
|
|
|
crowdsec_context_object.add_attribute(
|
2023-05-12 12:16:22 +02:00
|
|
|
"attack-details",
|
|
|
|
", ".join(
|
|
|
|
f"{scenario['name']} - {scenario['label']} ({scenario['description']})"
|
|
|
|
for scenario in crowdsec_cti["attack_details"]
|
|
|
|
)
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
|
|
|
crowdsec_context_object.add_attribute(
|
2023-05-12 12:16:22 +02:00
|
|
|
"target-countries",
|
|
|
|
", ".join(
|
|
|
|
map(
|
|
|
|
get_country_name_from_alpha_2,
|
|
|
|
crowdsec_cti["target_countries"].keys()
|
|
|
|
)
|
|
|
|
)
|
2022-07-12 09:09:30 +02:00
|
|
|
)
|
2023-05-12 12:16:22 +02:00
|
|
|
crowdsec_context_object.add_attribute("trust", crowdsec_cti["scores"]["overall"]["trust"])
|
|
|
|
scores = []
|
2022-07-12 09:09:30 +02:00
|
|
|
for time_period, indicators in crowdsec_cti["scores"].items():
|
2023-05-12 12:16:22 +02:00
|
|
|
tp = ' '.join(map(str.capitalize, time_period.split('_')))
|
|
|
|
indicator = (
|
|
|
|
f'{indicator_type.capitalize()}: {indicator_value}'
|
|
|
|
for indicator_type, indicator_value in indicators.items()
|
|
|
|
)
|
|
|
|
scores.append(f"{tp}: {' - '.join(indicator)}")
|
|
|
|
crowdsec_context_object.add_attribute('scores', ', '.join(scores))
|
|
|
|
crowdsec_context_object.add_reference(misp_attribute.uuid, 'related-to')
|
2022-07-12 09:09:30 +02:00
|
|
|
misp_event.add_object(crowdsec_context_object)
|
|
|
|
|
|
|
|
event = json.loads(misp_event.to_json())
|
|
|
|
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
|
|
|
return {"results": results}
|
|
|
|
|
|
|
|
|
|
|
|
def get_country_name_from_alpha_2(alpha_2):
|
|
|
|
country_info = pycountry.countries.get(alpha_2=alpha_2)
|
|
|
|
return country_info.name
|
|
|
|
|
|
|
|
|
|
|
|
def introspection():
|
|
|
|
return mispattributes
|
|
|
|
|
|
|
|
|
|
|
|
def version():
|
|
|
|
moduleinfo["config"] = moduleconfig
|
|
|
|
return moduleinfo
|