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 " ,
" requirements " : [ " A CrowdSec CTI API key. Get yours by following https://docs.crowdsec.net/docs/cti_api/getting_started/#getting-an-api-key " ] ,
" 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. " ,
" references " : [ " https://www.crowdsec.net/ " , " https://docs.crowdsec.net/docs/cti_api/getting_started " , " https://app.crowdsec.net/ " ] ,
" 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 " ,
]
crowdsec_template = {
" attributes " : {
" as-name " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " Autonomous system name " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 0 ,
} ,
" as-num " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " Autonomous system number " ,
" disable_correlation " : True ,
" misp-attribute " : " AS " ,
" multiple " : True ,
" ui-priority " : 0 ,
} ,
" attack-details " : {
" description " : " Triggered scenarios " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" background-noise " : {
" description " : " High background noise scores highlight untargeted, mild threat mass-attacks " ,
" disable_correlation " : True ,
" misp-attribute " : " float " ,
" ui-priority " : 1 ,
} ,
" behaviors " : {
" description " : " Attack categories " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" city " : {
" description " : " City of origin " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" classifications " : {
" description " : " Classification category of the IP address " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" country " : {
" description " : " Country of origin " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" country-code " : {
" description " : " Country Code " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 0 ,
} ,
" cves " : {
" description " : " CVEs " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" dst-port " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " Destination port " ,
" disable_correlation " : True ,
" misp-attribute " : " port " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" false-positives " : {
" description " : " False positive category of the IP address " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" ip " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " IP Address " ,
" misp-attribute " : " ip-src " ,
" ui-priority " : 1 ,
} ,
" ip-range " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " destination IP address " ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" ip-range-score " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " destination IP address " ,
" disable_correlation " : True ,
" misp-attribute " : " float " ,
" ui-priority " : 1 ,
} ,
" latitude " : {
" description " : " Latitude of origin " ,
" disable_correlation " : True ,
" misp-attribute " : " float " ,
" ui-priority " : 1 ,
} ,
" longitude " : {
" description " : " Longitude of origin " ,
" disable_correlation " : True ,
" misp-attribute " : " float " ,
" ui-priority " : 1 ,
} ,
" mitre-techniques " : {
" description " : " MITRE ATT&CK techniques " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : True ,
" ui-priority " : 1 ,
} ,
" reputation " : {
" description " : " IP reputation " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" multiple " : False ,
" ui-priority " : 1 ,
} ,
" reverse-dns " : {
" categories " : [ " Network activity " , " External analysis " ] ,
" description " : " Reverse DNS name " ,
" misp-attribute " : " hostname " ,
" ui-priority " : 1 ,
} ,
" scores " : {
" description " : " Scores " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" target-countries " : {
" description " : " Target countries (top 10) " ,
" disable_correlation " : True ,
" misp-attribute " : " text " ,
" ui-priority " : 1 ,
} ,
" trust " : {
" description " : " Trust level " ,
" disable_correlation " : True ,
" misp-attribute " : " float " ,
" ui-priority " : 1 ,
} ,
} ,
" description " : " CrowdSec Threat Intelligence - IP CTI search " ,
" meta-category " : " network " ,
" name " : " crowdsec-ip-context " ,
" requiredOneOf " : [ " ip " ] ,
" uuid " : " 0f0a6def-a351-4d3b-9868-d732f6f4666f " ,
" version " : 4 ,
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 " ]
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-22 04:23:30 +02:00
" User-Agent " : " crowdsec-misp/v2.1.0 " ,
} ,
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-22 04:23:30 +02:00
crowdsec_context_object = MISPObject (
" crowdsec-ip-context " , misp_objects_template_custom = crowdsec_template
)
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