2024-08-23 10:04:07 +02:00
import ipaddress
2022-07-12 09:09:30 +02:00
import json
2024-08-22 04:23:30 +02:00
2022-07-12 09:09:30 +02:00
import pycountry
import requests
2023-05-11 16:12:07 +02:00
from pymisp import MISPEvent , MISPObject
2022-07-12 09:09:30 +02:00
2024-08-22 04:23:30 +02:00
from . import check_input_attribute , standard_error_message
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 = {
2024-08-22 04:23:30 +02:00
" version " : " 2.1 " ,
" author " : " Shivam Sandbhor <shivam@crowdsec.net> " ,
" description " : " Module to access CrowdSec CTI API. " ,
" module-type " : [ " hover " , " expansion " ] ,
2024-08-22 07:48:30 +02:00
" name " : " CrowdSec CTI " ,
" logo " : " crowdsec.png " ,
2024-08-23 10:04:07 +02:00
" requirements " : [
" A CrowdSec CTI API key. Get yours by following https://docs.crowdsec.net/docs/cti_api/getting_started/#getting-an-api-key "
] ,
2024-08-22 07:48:30 +02:00
" features " : " This module enables IP lookup from CrowdSec CTI API. It provides information about the IP, such as what kind of attacks it has been participant of as seen by CrowdSec ' s network. It also includes enrichment by CrowdSec like background noise score, aggressivity over time etc. " ,
2024-08-23 10:04:07 +02:00
" references " : [
" https://www.crowdsec.net/ " ,
" https://docs.crowdsec.net/docs/cti_api/getting_started " ,
" https://app.crowdsec.net/ " ,
] ,
2024-08-22 07:48:30 +02:00
" input " : " An IP address. " ,
" output " : " IP Lookup information from CrowdSec CTI API " ,
2024-08-22 04:23:30 +02:00
}
moduleconfig = [
" api_key " ,
" add_reputation_tag " ,
" add_behavior_tag " ,
" add_classification_tag " ,
" add_mitre_technique_tag " ,
" add_cve_tag " ,
]
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 " }
2024-08-22 04:23:30 +02:00
if not request . get ( " attribute " ) or not check_input_attribute ( request [ " attribute " ] ) :
return {
" error " : f " { standard_error_message } , which should contain at least a type, a value and an uuid. "
}
2023-05-11 16:12:07 +02:00
2024-08-22 04:23:30 +02:00
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
2024-08-22 04:23:30 +02:00
def _get_boolean_config ( request_data , config : str , default_config : bool ) :
if request_data [ " config " ] . get ( config ) is None :
return default_config
raw_config = request_data [ " config " ] . get ( config ) . lower ( )
# falsy values, return False
if raw_config in [ " false " , " 0 " , " no " , " off " ] :
return False
# truthy values, return True
if raw_config in [ " true " , " 1 " , " yes " , " on " ] :
return True
return default_config
2022-07-12 09:09:30 +02:00
def _handler_v2 ( request_data ) :
2024-08-22 04:23:30 +02:00
attribute = request_data [ " attribute " ]
ip = attribute [ " value " ]
2024-08-23 10:04:07 +02:00
# Validate IP
try :
ipaddress . ip_address ( ip )
except ValueError :
return {
" error " : f " IP ( { ip } ) is not valid for calling CrowdSec CTI. Please provide a valid IP address. "
}
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 " ] ,
2024-08-23 10:04:07 +02:00
" User-Agent " : " crowdsec-misp/v2.1.1 " ,
2024-08-22 04:23:30 +02:00
} ,
2022-07-12 09:09:30 +02:00
)
crowdsec_cti . raise_for_status ( )
crowdsec_cti = crowdsec_cti . json ( )
2024-08-22 04:23:30 +02:00
add_reputation_tag = _get_boolean_config ( request_data , " add_reputation_tag " , True )
add_behavior_tag = _get_boolean_config ( request_data , " add_behavior_tag " , True )
add_classification_tag = _get_boolean_config (
request_data , " add_classification_tag " , True
)
add_mitre_technique_tag = _get_boolean_config (
request_data , " add_mitre_technique_tag " , True
)
add_cve_tag = _get_boolean_config ( request_data , " add_cve_tag " , True )
2022-07-12 09:09:30 +02:00
misp_event = MISPEvent ( )
2023-05-12 12:16:22 +02:00
misp_attribute = misp_event . add_attribute ( * * attribute )
2024-08-23 10:04:07 +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 " ] ,
2024-08-22 04:23:30 +02:00
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 " ] )
2024-08-22 04:23:30 +02:00
reputation = crowdsec_cti [ " reputation " ] or " unknown "
crowdsec_context_object . add_attribute ( " reputation " , reputation )
if add_reputation_tag :
tag = f ' crowdsec:reputation= " { reputation } " '
ip_attribute . add_tag ( tag )
2023-05-12 12:16:22 +02:00
crowdsec_context_object . add_attribute ( " ip-range " , crowdsec_cti [ " ip_range " ] )
2024-08-22 04:23:30 +02:00
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
)
2024-08-22 04:23:30 +02:00
crowdsec_context_object . add_attribute (
" country-code " , crowdsec_cti [ " location " ] [ " country " ]
)
2023-05-12 12:16:22 +02:00
if crowdsec_cti [ " location " ] . get ( " city " ) :
2024-08-22 04:23:30 +02:00
crowdsec_context_object . add_attribute ( " 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 " ]
)
2023-05-12 12:16:22 +02:00
crowdsec_context_object . add_attribute ( " as-name " , crowdsec_cti [ " as_name " ] )
crowdsec_context_object . add_attribute ( " as-num " , crowdsec_cti [ " as_num " ] )
2024-08-22 04:23:30 +02:00
if crowdsec_cti . get ( " reverse_dns " ) is not None :
crowdsec_context_object . add_attribute (
" reverse-dns " , crowdsec_cti [ " reverse_dns " ]
)
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 (
2024-08-22 04:23:30 +02:00
" behaviors " , behavior [ " label " ] , comment = behavior [ " description " ]
2022-07-12 09:09:30 +02:00
)
2024-08-22 04:23:30 +02:00
if add_behavior_tag :
tag = f ' crowdsec:behavior= " { behavior [ " name " ] } " '
ip_attribute . add_tag ( tag )
for technique in crowdsec_cti [ " mitre_techniques " ] :
technique_name = technique [ " name " ]
mitre_url = (
f " https://attack.mitre.org/tactics/ { technique_name } "
if technique_name . startswith ( " TA " )
else f " https://attack.mitre.org/techniques/ { technique_name } "
)
crowdsec_context_object . add_attribute (
" mitre-techniques " ,
technique [ " label " ] ,
comment = f ' { technique [ " description " ] } ( { mitre_url } ) ' ,
)
if add_mitre_technique_tag :
tag = f ' crowdsec:mitre-technique= " { technique_name } " '
ip_attribute . add_tag ( tag )
for cve in crowdsec_cti [ " cves " ] :
cve_url = f " https://nvd.nist.gov/vuln/detail/ { cve } "
crowdsec_context_object . add_attribute ( " cves " , cve , comment = cve_url )
if add_cve_tag :
tag = f ' crowdsec:cve= " { cve } " '
ip_attribute . add_tag ( tag )
for feature , values in crowdsec_cti [ " classifications " ] . items ( ) :
2023-05-26 14:26:26 +02:00
field = feature [ : - 1 ]
for value in values :
crowdsec_context_object . add_attribute (
2024-08-22 04:23:30 +02:00
feature , value [ " label " ] , comment = value [ " description " ]
2023-05-26 14:26:26 +02:00
)
2024-08-22 04:23:30 +02:00
if add_classification_tag :
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 " ]
2024-08-22 04:23:30 +02:00
) ,
2022-07-12 09:09:30 +02:00
)
crowdsec_context_object . add_attribute (
2023-05-12 12:16:22 +02:00
" target-countries " ,
" , " . join (
2024-08-22 04:23:30 +02:00
map ( get_country_name_from_alpha_2 , crowdsec_cti [ " target_countries " ] . keys ( ) )
) ,
)
crowdsec_context_object . add_attribute (
" trust " , crowdsec_cti [ " scores " ] [ " overall " ] [ " trust " ]
2022-07-12 09:09:30 +02:00
)
2023-05-12 12:16:22 +02:00
scores = [ ]
2022-07-12 09:09:30 +02:00
for time_period , indicators in crowdsec_cti [ " scores " ] . items ( ) :
2024-08-22 04:23:30 +02:00
tp = " " . join ( map ( str . capitalize , time_period . split ( " _ " ) ) )
2023-05-12 12:16:22 +02:00
indicator = (
2024-08-22 04:23:30 +02:00
f " { indicator_type . capitalize ( ) } : { indicator_value } "
2023-05-12 12:16:22 +02:00
for indicator_type , indicator_value in indicators . items ( )
)
scores . append ( f " { tp } : { ' - ' . join ( indicator ) } " )
2024-08-22 04:23:30 +02:00
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 ( ) )
2024-08-22 04:23:30 +02:00
results = {
key : event [ key ]
for key in ( " Attribute " , " Object " )
if ( key in event and event [ key ] )
}
2022-07-12 09:09:30 +02:00
return { " results " : results }
def get_country_name_from_alpha_2 ( alpha_2 ) :
country_info = pycountry . countries . get ( alpha_2 = alpha_2 )
2024-08-22 04:23:30 +02:00
return country_info . name if country_info else None
2022-07-12 09:09:30 +02:00
def introspection ( ) :
return mispattributes
def version ( ) :
moduleinfo [ " config " ] = moduleconfig
return moduleinfo