misp-modules 2.4.196__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. misp_modules/__init__.py +379 -0
  2. misp_modules/helpers/__init__.py +1 -0
  3. misp_modules/helpers/cache.py +84 -0
  4. misp_modules/lib/__init__.py +4 -0
  5. misp_modules/lib/_vmray/__init__.py +0 -0
  6. misp_modules/lib/_vmray/parser.py +1417 -0
  7. misp_modules/lib/_vmray/rest_api.py +148 -0
  8. misp_modules/lib/cof2misp/LICENSE-2.0.txt +202 -0
  9. misp_modules/lib/cof2misp/__init__.py +0 -0
  10. misp_modules/lib/cof2misp/cof.py +165 -0
  11. misp_modules/lib/joe_mapping.py +114 -0
  12. misp_modules/lib/joe_parser.py +573 -0
  13. misp_modules/lib/lastline_api.py +841 -0
  14. misp_modules/lib/qintel_helper.py +263 -0
  15. misp_modules/lib/stix2misp.py +2080 -0
  16. misp_modules/lib/stix2misp_mapping.py +460 -0
  17. misp_modules/lib/synonymsToTagNames.json +1 -0
  18. misp_modules/lib/vt_graph_parser/__init__.py +8 -0
  19. misp_modules/lib/vt_graph_parser/errors.py +20 -0
  20. misp_modules/lib/vt_graph_parser/helpers/__init__.py +7 -0
  21. misp_modules/lib/vt_graph_parser/helpers/parsers.py +88 -0
  22. misp_modules/lib/vt_graph_parser/helpers/rules.py +304 -0
  23. misp_modules/lib/vt_graph_parser/helpers/wrappers.py +58 -0
  24. misp_modules/lib/vt_graph_parser/importers/__init__.py +7 -0
  25. misp_modules/lib/vt_graph_parser/importers/base.py +98 -0
  26. misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +73 -0
  27. misp_modules/modules/__init__.py +4 -0
  28. misp_modules/modules/action_mod/__init__.py +1 -0
  29. misp_modules/modules/action_mod/_utils/__init__.py +1 -0
  30. misp_modules/modules/action_mod/_utils/utils.py +70 -0
  31. misp_modules/modules/action_mod/mattermost.py +113 -0
  32. misp_modules/modules/action_mod/slack.py +96 -0
  33. misp_modules/modules/action_mod/testaction.py +68 -0
  34. misp_modules/modules/expansion/__init__.py +36 -0
  35. misp_modules/modules/expansion/_dnsdb_query/COPYRIGHT +27 -0
  36. misp_modules/modules/expansion/_dnsdb_query/LICENSE +202 -0
  37. misp_modules/modules/expansion/_dnsdb_query/README.md +162 -0
  38. misp_modules/modules/expansion/_dnsdb_query/__init__.py +0 -0
  39. misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py +327 -0
  40. misp_modules/modules/expansion/_ransomcoindb/__init__.py +0 -0
  41. misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +96 -0
  42. misp_modules/modules/expansion/_vulnerability_parser/__init__.py +0 -0
  43. misp_modules/modules/expansion/_vulnerability_parser/vulnerability_parser.py +118 -0
  44. misp_modules/modules/expansion/abuseipdb.py +146 -0
  45. misp_modules/modules/expansion/apiosintds.py +365 -0
  46. misp_modules/modules/expansion/apivoid.py +133 -0
  47. misp_modules/modules/expansion/assemblyline_query.py +179 -0
  48. misp_modules/modules/expansion/assemblyline_submit.py +100 -0
  49. misp_modules/modules/expansion/backscatter_io.py +84 -0
  50. misp_modules/modules/expansion/btc_scam_check.py +54 -0
  51. misp_modules/modules/expansion/btc_steroids.py +238 -0
  52. misp_modules/modules/expansion/censys_enrich.py +287 -0
  53. misp_modules/modules/expansion/circl_passivedns.py +85 -0
  54. misp_modules/modules/expansion/circl_passivessl.py +113 -0
  55. misp_modules/modules/expansion/clamav.py +129 -0
  56. misp_modules/modules/expansion/cluster25_expand.py +239 -0
  57. misp_modules/modules/expansion/countrycode.py +69 -0
  58. misp_modules/modules/expansion/cpe.py +140 -0
  59. misp_modules/modules/expansion/crowdsec.py +235 -0
  60. misp_modules/modules/expansion/crowdstrike_falcon.py +148 -0
  61. misp_modules/modules/expansion/cuckoo_submit.py +161 -0
  62. misp_modules/modules/expansion/cve.py +57 -0
  63. misp_modules/modules/expansion/cve_advanced.py +185 -0
  64. misp_modules/modules/expansion/cytomic_orion.py +196 -0
  65. misp_modules/modules/expansion/dbl_spamhaus.py +78 -0
  66. misp_modules/modules/expansion/dns.py +71 -0
  67. misp_modules/modules/expansion/docx_enrich.py +71 -0
  68. misp_modules/modules/expansion/domaintools.py +290 -0
  69. misp_modules/modules/expansion/eql.py +91 -0
  70. misp_modules/modules/expansion/eupi.py +86 -0
  71. misp_modules/modules/expansion/extract_url_components.py +83 -0
  72. misp_modules/modules/expansion/farsight_passivedns.py +243 -0
  73. misp_modules/modules/expansion/geoip_asn.py +75 -0
  74. misp_modules/modules/expansion/geoip_city.py +75 -0
  75. misp_modules/modules/expansion/geoip_country.py +73 -0
  76. misp_modules/modules/expansion/google_safe_browsing.py +86 -0
  77. misp_modules/modules/expansion/google_search.py +57 -0
  78. misp_modules/modules/expansion/google_threat_intelligence.py +453 -0
  79. misp_modules/modules/expansion/greynoise.py +346 -0
  80. misp_modules/modules/expansion/hashdd.py +53 -0
  81. misp_modules/modules/expansion/hashlookup.py +118 -0
  82. misp_modules/modules/expansion/hibp.py +62 -0
  83. misp_modules/modules/expansion/html_to_markdown.py +63 -0
  84. misp_modules/modules/expansion/hyasinsight.py +881 -0
  85. misp_modules/modules/expansion/intel471.py +73 -0
  86. misp_modules/modules/expansion/intelmq_eventdb.py.experimental +67 -0
  87. misp_modules/modules/expansion/ip2locationio.py +90 -0
  88. misp_modules/modules/expansion/ipasn.py +70 -0
  89. misp_modules/modules/expansion/ipinfo.py +112 -0
  90. misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +633 -0
  91. misp_modules/modules/expansion/iprep.py +98 -0
  92. misp_modules/modules/expansion/jinja_template_rendering.py +54 -0
  93. misp_modules/modules/expansion/joesandbox_query.py +91 -0
  94. misp_modules/modules/expansion/joesandbox_submit.py +147 -0
  95. misp_modules/modules/expansion/lastline_query.py +148 -0
  96. misp_modules/modules/expansion/lastline_submit.py +180 -0
  97. misp_modules/modules/expansion/macaddress_io.py +130 -0
  98. misp_modules/modules/expansion/macvendors.py +55 -0
  99. misp_modules/modules/expansion/malshare_upload.py +107 -0
  100. misp_modules/modules/expansion/malwarebazaar.py +67 -0
  101. misp_modules/modules/expansion/mcafee_insights_enrich.py +249 -0
  102. misp_modules/modules/expansion/mmdb_lookup.py +138 -0
  103. misp_modules/modules/expansion/module.py.skeleton +44 -0
  104. misp_modules/modules/expansion/mwdb.py +152 -0
  105. misp_modules/modules/expansion/ocr_enrich.py +74 -0
  106. misp_modules/modules/expansion/ods_enrich.py +71 -0
  107. misp_modules/modules/expansion/odt_enrich.py +61 -0
  108. misp_modules/modules/expansion/onyphe.py +243 -0
  109. misp_modules/modules/expansion/onyphe_full.py +387 -0
  110. misp_modules/modules/expansion/otx.py +167 -0
  111. misp_modules/modules/expansion/passive_ssh.py +150 -0
  112. misp_modules/modules/expansion/passivetotal.py +358 -0
  113. misp_modules/modules/expansion/pdf_enrich.py +58 -0
  114. misp_modules/modules/expansion/pptx_enrich.py +65 -0
  115. misp_modules/modules/expansion/qintel_qsentry.py +229 -0
  116. misp_modules/modules/expansion/qrcode.py +99 -0
  117. misp_modules/modules/expansion/ransomcoindb.py +82 -0
  118. misp_modules/modules/expansion/rbl.py +126 -0
  119. misp_modules/modules/expansion/recordedfuture.py +544 -0
  120. misp_modules/modules/expansion/reversedns.py +77 -0
  121. misp_modules/modules/expansion/securitytrails.py +571 -0
  122. misp_modules/modules/expansion/shodan.py +244 -0
  123. misp_modules/modules/expansion/sigma_queries.py +61 -0
  124. misp_modules/modules/expansion/sigma_syntax_validator.py +49 -0
  125. misp_modules/modules/expansion/sigmf_expand.py +303 -0
  126. misp_modules/modules/expansion/socialscan.py +108 -0
  127. misp_modules/modules/expansion/sophoslabs_intelix.py +146 -0
  128. misp_modules/modules/expansion/sourcecache.py +57 -0
  129. misp_modules/modules/expansion/stairwell.py +156 -0
  130. misp_modules/modules/expansion/stix2_pattern_syntax_validator.py +56 -0
  131. misp_modules/modules/expansion/threatcrowd.py +170 -0
  132. misp_modules/modules/expansion/threatfox.py +75 -0
  133. misp_modules/modules/expansion/threatminer.py +152 -0
  134. misp_modules/modules/expansion/triage_submit.py +119 -0
  135. misp_modules/modules/expansion/trustar_enrich.py +235 -0
  136. misp_modules/modules/expansion/urlhaus.py +164 -0
  137. misp_modules/modules/expansion/urlscan.py +269 -0
  138. misp_modules/modules/expansion/variotdbs.py +133 -0
  139. misp_modules/modules/expansion/virustotal.py +311 -0
  140. misp_modules/modules/expansion/virustotal_public.py +272 -0
  141. misp_modules/modules/expansion/virustotal_upload.py +91 -0
  142. misp_modules/modules/expansion/vmray_submit.py +161 -0
  143. misp_modules/modules/expansion/vmware_nsx.py +628 -0
  144. misp_modules/modules/expansion/vulndb.py +292 -0
  145. misp_modules/modules/expansion/vulnerability_lookup.py +324 -0
  146. misp_modules/modules/expansion/vulners.py +81 -0
  147. misp_modules/modules/expansion/vysion.py +221 -0
  148. misp_modules/modules/expansion/whois.py +72 -0
  149. misp_modules/modules/expansion/whoisfreaks.py +232 -0
  150. misp_modules/modules/expansion/wiki.py +58 -0
  151. misp_modules/modules/expansion/xforceexchange.py +189 -0
  152. misp_modules/modules/expansion/xlsx_enrich.py +63 -0
  153. misp_modules/modules/expansion/yara_query.py +70 -0
  154. misp_modules/modules/expansion/yara_syntax_validator.py +49 -0
  155. misp_modules/modules/expansion/yeti.py +196 -0
  156. misp_modules/modules/export_mod/__init__.py +3 -0
  157. misp_modules/modules/export_mod/cef_export.py +93 -0
  158. misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py +150 -0
  159. misp_modules/modules/export_mod/defender_endpoint_export.py +141 -0
  160. misp_modules/modules/export_mod/goamlexport.py +255 -0
  161. misp_modules/modules/export_mod/liteexport.py +97 -0
  162. misp_modules/modules/export_mod/mass_eql_export.py +106 -0
  163. misp_modules/modules/export_mod/nexthinkexport.py +131 -0
  164. misp_modules/modules/export_mod/osqueryexport.py +125 -0
  165. misp_modules/modules/export_mod/pdfexport.py +107 -0
  166. misp_modules/modules/export_mod/testexport.py +73 -0
  167. misp_modules/modules/export_mod/threatStream_misp_export.py +114 -0
  168. misp_modules/modules/export_mod/threat_connect_export.py +125 -0
  169. misp_modules/modules/export_mod/virustotal_collections.py +141 -0
  170. misp_modules/modules/export_mod/vt_graph.py +120 -0
  171. misp_modules/modules/export_mod/yara_export.py +290 -0
  172. misp_modules/modules/import_mod/__init__.py +22 -0
  173. misp_modules/modules/import_mod/cof2misp.py +264 -0
  174. misp_modules/modules/import_mod/csvimport.py +319 -0
  175. misp_modules/modules/import_mod/cuckooimport.py +749 -0
  176. misp_modules/modules/import_mod/email_import.py +294 -0
  177. misp_modules/modules/import_mod/goamlimport.py +188 -0
  178. misp_modules/modules/import_mod/import_blueprint.py +93 -0
  179. misp_modules/modules/import_mod/joe_import.py +74 -0
  180. misp_modules/modules/import_mod/lastline_import.py +160 -0
  181. misp_modules/modules/import_mod/mispjson.py +73 -0
  182. misp_modules/modules/import_mod/ocr.py +122 -0
  183. misp_modules/modules/import_mod/openiocimport.py +99 -0
  184. misp_modules/modules/import_mod/taxii21.py +383 -0
  185. misp_modules/modules/import_mod/testimport.py +73 -0
  186. misp_modules/modules/import_mod/threatanalyzer_import.py +552 -0
  187. misp_modules/modules/import_mod/url_import.py +97 -0
  188. misp_modules/modules/import_mod/vmray_import.py +96 -0
  189. misp_modules/modules/import_mod/vmray_summary_json_import.py +87 -0
  190. misp_modules-2.4.196.dist-info/LICENSE +661 -0
  191. misp_modules-2.4.196.dist-info/METADATA +281 -0
  192. misp_modules-2.4.196.dist-info/RECORD +194 -0
  193. misp_modules-2.4.196.dist-info/WHEEL +4 -0
  194. misp_modules-2.4.196.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,140 @@
1
+ import json
2
+ import requests
3
+ from . import check_input_attribute, standard_error_message
4
+ from pymisp import MISPEvent, MISPObject
5
+
6
+ misperrors = {'error': 'Error'}
7
+ mispattributes = {'input': ['cpe'], 'format': 'misp_standard'}
8
+ moduleinfo = {
9
+ 'version': '2',
10
+ 'author': 'Christian Studer',
11
+ 'description': 'An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities.',
12
+ 'module-type': ['expansion', 'hover'],
13
+ 'name': 'CPE Lookup',
14
+ 'logo': 'cve.png',
15
+ 'requirements': [],
16
+ 'features': 'The module takes a cpe attribute as input and queries the CVE search API to get its related vulnerabilities. \nThe list of vulnerabilities is then parsed and returned as vulnerability objects.\n\nUsers can use their own CVE search API url by defining a value to the custom_API_URL parameter. If no custom API url is given, the default vulnerability.circl.lu api url is used.\n\nIn order to limit the amount of data returned by CVE serach, users can also the limit parameter. With the limit set, the API returns only the requested number of vulnerabilities, sorted from the highest cvss score to the lowest one.',
17
+ 'references': ['https://vulnerability.circl.lu/api/'],
18
+ 'input': 'CPE attribute.',
19
+ 'output': 'The vulnerabilities related to the CPE.',
20
+ }
21
+ moduleconfig = ["custom_API_URL", "limit"]
22
+ cveapi_url = 'https://cvepremium.circl.lu/api/query'
23
+ DEFAULT_LIMIT = 10
24
+
25
+
26
+ class VulnerabilitiesParser():
27
+ def __init__(self, attribute):
28
+ self.attribute = attribute
29
+ self.misp_event = MISPEvent()
30
+ self.misp_event.add_attribute(**attribute)
31
+ self.vulnerability_mapping = {
32
+ 'id': {
33
+ 'type': 'vulnerability',
34
+ 'object_relation': 'id'
35
+ },
36
+ 'summary': {
37
+ 'type': 'text',
38
+ 'object_relation': 'summary'
39
+ },
40
+ 'vulnerable_configuration': {
41
+ 'type': 'cpe',
42
+ 'object_relation': 'vulnerable-configuration'
43
+ },
44
+ 'vulnerable_configuration_cpe_2_2': {
45
+ 'type': 'cpe',
46
+ 'object_relation': 'vulnerable-configuration'
47
+ },
48
+ 'Modified': {
49
+ 'type': 'datetime',
50
+ 'object_relation': 'modified'
51
+ },
52
+ 'Published': {
53
+ 'type': 'datetime',
54
+ 'object_relation': 'published'
55
+ },
56
+ 'references': {
57
+ 'type': 'link',
58
+ 'object_relation': 'references'
59
+ },
60
+ 'cvss': {
61
+ 'type': 'float',
62
+ 'object_relation': 'cvss-score'
63
+ }
64
+ }
65
+
66
+ def parse_vulnerabilities(self, vulnerabilities):
67
+ for vulnerability in vulnerabilities:
68
+ vulnerability_object = MISPObject('vulnerability')
69
+ for feature in ('id', 'summary', 'Modified', 'Published', 'cvss'):
70
+ if vulnerability.get(feature):
71
+ attribute = {'value': vulnerability[feature]}
72
+ attribute.update(self.vulnerability_mapping[feature])
73
+ vulnerability_object.add_attribute(**attribute)
74
+ if vulnerability.get('Published'):
75
+ vulnerability_object.add_attribute(**{
76
+ 'type': 'text',
77
+ 'object_relation': 'state',
78
+ 'value': 'Published'
79
+ })
80
+ for feature in ('references', 'vulnerable_configuration', 'vulnerable_configuration_cpe_2_2'):
81
+ if vulnerability.get(feature):
82
+ for value in vulnerability[feature]:
83
+ if isinstance(value, dict):
84
+ value = value['title']
85
+ attribute = {'value': value}
86
+ attribute.update(self.vulnerability_mapping[feature])
87
+ vulnerability_object.add_attribute(**attribute)
88
+ vulnerability_object.add_reference(self.attribute['uuid'], 'related-to')
89
+ self.misp_event.add_object(vulnerability_object)
90
+
91
+ def get_result(self):
92
+ event = json.loads(self.misp_event.to_json())
93
+ results = {key: event[key] for key in ('Attribute', 'Object')}
94
+ return {'results': results}
95
+
96
+
97
+ def check_url(url):
98
+ return url if url.endswith('/') else f"{url}/"
99
+
100
+
101
+ def handler(q=False):
102
+ if q is False:
103
+ return False
104
+ request = json.loads(q)
105
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
106
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
107
+ attribute = request['attribute']
108
+ if attribute.get('type') != 'cpe':
109
+ return {'error': 'Wrong input attribute type.'}
110
+ config = request['config']
111
+ url = check_url(config['custom_API_URL']) if config.get('custom_API_URL') else cveapi_url
112
+ limit = int(config['limit']) if config.get('limit') else DEFAULT_LIMIT
113
+ params = {
114
+ "retrieve": "cves",
115
+ "dict_filter": {
116
+ "vulnerable_configuration": attribute['value']
117
+ },
118
+ "limit": limit,
119
+ "sort": "cvss",
120
+ "sort_dir": "DESC"
121
+ }
122
+ response = requests.post(url, json=params)
123
+ if response.status_code == 200:
124
+ vulnerabilities = response.json()['data']
125
+ if not vulnerabilities:
126
+ return {'error': 'No related vulnerability for this CPE.'}
127
+ else:
128
+ return {'error': 'API not accessible.'}
129
+ parser = VulnerabilitiesParser(attribute)
130
+ parser.parse_vulnerabilities(vulnerabilities)
131
+ return parser.get_result()
132
+
133
+
134
+ def introspection():
135
+ return mispattributes
136
+
137
+
138
+ def version():
139
+ moduleinfo['config'] = moduleconfig
140
+ return moduleinfo
@@ -0,0 +1,235 @@
1
+ import ipaddress
2
+ import json
3
+
4
+ import pycountry
5
+ import requests
6
+ from pymisp import MISPEvent, MISPObject
7
+
8
+ from . import check_input_attribute, standard_error_message
9
+
10
+ mispattributes = {"input": ["ip-dst", "ip-src"], "format": "misp_standard"}
11
+ moduleinfo = {
12
+ "version": "2.1",
13
+ "author": "Shivam Sandbhor <shivam@crowdsec.net>",
14
+ "description": "Module to access CrowdSec CTI API.",
15
+ "module-type": ["hover", "expansion"],
16
+ "name": "CrowdSec CTI",
17
+ "logo": "crowdsec.png",
18
+ "requirements": [
19
+ "A CrowdSec CTI API key. Get yours by following https://docs.crowdsec.net/docs/cti_api/getting_started/#getting-an-api-key"
20
+ ],
21
+ "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.",
22
+ "references": [
23
+ "https://www.crowdsec.net/",
24
+ "https://docs.crowdsec.net/docs/cti_api/getting_started",
25
+ "https://app.crowdsec.net/",
26
+ ],
27
+ "input": "An IP address.",
28
+ "output": "IP Lookup information from CrowdSec CTI API",
29
+ }
30
+ moduleconfig = [
31
+ "api_key",
32
+ "add_reputation_tag",
33
+ "add_behavior_tag",
34
+ "add_classification_tag",
35
+ "add_mitre_technique_tag",
36
+ "add_cve_tag",
37
+ ]
38
+
39
+
40
+ def handler(q=False):
41
+ if q is False:
42
+ return False
43
+
44
+ request = json.loads(q)
45
+ if not request.get("config"):
46
+ return {"error": "Missing CrowdSec Config"}
47
+
48
+ if not request["config"].get("api_key"):
49
+ return {"error": "Missing CrowdSec API key"}
50
+
51
+ if not request.get("attribute") or not check_input_attribute(request["attribute"]):
52
+ return {
53
+ "error": f"{standard_error_message}, which should contain at least a type, a value and an uuid."
54
+ }
55
+
56
+ if request["attribute"].get("type") not in mispattributes["input"]:
57
+ return {
58
+ "error": f"Wrong input type. Please choose on of the following: {', '.join(mispattributes['input'])}"
59
+ }
60
+
61
+ return _handler_v2(request)
62
+
63
+
64
+ def _get_boolean_config(request_data, config: str, default_config: bool):
65
+ if request_data["config"].get(config) is None:
66
+ return default_config
67
+ raw_config = request_data["config"].get(config).lower()
68
+ # falsy values, return False
69
+ if raw_config in ["false", "0", "no", "off"]:
70
+ return False
71
+ # truthy values, return True
72
+ if raw_config in ["true", "1", "yes", "on"]:
73
+ return True
74
+ return default_config
75
+
76
+
77
+ def _handler_v2(request_data):
78
+ attribute = request_data["attribute"]
79
+ ip = attribute["value"]
80
+ # Validate IP
81
+ try:
82
+ ipaddress.ip_address(ip)
83
+ except ValueError:
84
+ return {
85
+ "error": f"IP ({ip}) is not valid for calling CrowdSec CTI. Please provide a valid IP address."
86
+ }
87
+
88
+ crowdsec_cti = requests.get(
89
+ f"https://cti.api.crowdsec.net/v2/smoke/{ip}",
90
+ headers={
91
+ "x-api-key": request_data["config"]["api_key"],
92
+ "User-Agent": "crowdsec-misp/v2.1.1",
93
+ },
94
+ )
95
+ crowdsec_cti.raise_for_status()
96
+ crowdsec_cti = crowdsec_cti.json()
97
+
98
+ add_reputation_tag = _get_boolean_config(request_data, "add_reputation_tag", True)
99
+ add_behavior_tag = _get_boolean_config(request_data, "add_behavior_tag", True)
100
+ add_classification_tag = _get_boolean_config(
101
+ request_data, "add_classification_tag", True
102
+ )
103
+ add_mitre_technique_tag = _get_boolean_config(
104
+ request_data, "add_mitre_technique_tag", True
105
+ )
106
+ add_cve_tag = _get_boolean_config(request_data, "add_cve_tag", True)
107
+
108
+ misp_event = MISPEvent()
109
+ misp_attribute = misp_event.add_attribute(**attribute)
110
+ crowdsec_context_object = MISPObject("crowdsec-ip-context")
111
+ crowdsec_context_object.from_dict(
112
+ first_seen=crowdsec_cti["history"]["first_seen"],
113
+ last_seen=crowdsec_cti["history"]["last_seen"],
114
+ )
115
+ ip_attribute = crowdsec_context_object.add_attribute("ip", crowdsec_cti["ip"])
116
+ reputation = crowdsec_cti["reputation"] or "unknown"
117
+ crowdsec_context_object.add_attribute("reputation", reputation)
118
+ if add_reputation_tag:
119
+ tag = f'crowdsec:reputation="{reputation}"'
120
+ ip_attribute.add_tag(tag)
121
+ crowdsec_context_object.add_attribute("ip-range", crowdsec_cti["ip_range"])
122
+ crowdsec_context_object.add_attribute(
123
+ "ip-range-score", crowdsec_cti["ip_range_score"]
124
+ )
125
+ crowdsec_context_object.add_attribute(
126
+ "country", get_country_name_from_alpha_2(crowdsec_cti["location"]["country"])
127
+ )
128
+ crowdsec_context_object.add_attribute(
129
+ "country-code", crowdsec_cti["location"]["country"]
130
+ )
131
+ if crowdsec_cti["location"].get("city"):
132
+ crowdsec_context_object.add_attribute("city", crowdsec_cti["location"]["city"])
133
+ crowdsec_context_object.add_attribute(
134
+ "latitude", crowdsec_cti["location"]["latitude"]
135
+ )
136
+ crowdsec_context_object.add_attribute(
137
+ "longitude", crowdsec_cti["location"]["longitude"]
138
+ )
139
+ crowdsec_context_object.add_attribute("as-name", crowdsec_cti["as_name"])
140
+ crowdsec_context_object.add_attribute("as-num", crowdsec_cti["as_num"])
141
+ if crowdsec_cti.get("reverse_dns") is not None:
142
+ crowdsec_context_object.add_attribute(
143
+ "reverse-dns", crowdsec_cti["reverse_dns"]
144
+ )
145
+ crowdsec_context_object.add_attribute(
146
+ "background-noise", crowdsec_cti["background_noise_score"]
147
+ )
148
+ for behavior in crowdsec_cti["behaviors"]:
149
+ crowdsec_context_object.add_attribute(
150
+ "behaviors", behavior["label"], comment=behavior["description"]
151
+ )
152
+ if add_behavior_tag:
153
+ tag = f'crowdsec:behavior="{behavior["name"]}"'
154
+ ip_attribute.add_tag(tag)
155
+ for technique in crowdsec_cti["mitre_techniques"]:
156
+ technique_name = technique["name"]
157
+ mitre_url = (
158
+ f"https://attack.mitre.org/tactics/{technique_name}"
159
+ if technique_name.startswith("TA")
160
+ else f"https://attack.mitre.org/techniques/{technique_name}"
161
+ )
162
+ crowdsec_context_object.add_attribute(
163
+ "mitre-techniques",
164
+ technique["label"],
165
+ comment=f'{technique["description"]} ({mitre_url})',
166
+ )
167
+ if add_mitre_technique_tag:
168
+ tag = f'crowdsec:mitre-technique="{technique_name}"'
169
+ ip_attribute.add_tag(tag)
170
+
171
+ for cve in crowdsec_cti["cves"]:
172
+ cve_url = f"https://nvd.nist.gov/vuln/detail/{cve}"
173
+ crowdsec_context_object.add_attribute("cves", cve, comment=cve_url)
174
+ if add_cve_tag:
175
+ tag = f'crowdsec:cve="{cve}"'
176
+ ip_attribute.add_tag(tag)
177
+
178
+ for feature, values in crowdsec_cti["classifications"].items():
179
+ field = feature[:-1]
180
+ for value in values:
181
+ crowdsec_context_object.add_attribute(
182
+ feature, value["label"], comment=value["description"]
183
+ )
184
+ if add_classification_tag:
185
+ tag = f'crowdsec:{field}="{value["name"]}"'
186
+ ip_attribute.add_tag(tag)
187
+ crowdsec_context_object.add_attribute(
188
+ "attack-details",
189
+ ", ".join(
190
+ f"{scenario['name']} - {scenario['label']} ({scenario['description']})"
191
+ for scenario in crowdsec_cti["attack_details"]
192
+ ),
193
+ )
194
+ crowdsec_context_object.add_attribute(
195
+ "target-countries",
196
+ ", ".join(
197
+ map(get_country_name_from_alpha_2, crowdsec_cti["target_countries"].keys())
198
+ ),
199
+ )
200
+ crowdsec_context_object.add_attribute(
201
+ "trust", crowdsec_cti["scores"]["overall"]["trust"]
202
+ )
203
+ scores = []
204
+ for time_period, indicators in crowdsec_cti["scores"].items():
205
+ tp = " ".join(map(str.capitalize, time_period.split("_")))
206
+ indicator = (
207
+ f"{indicator_type.capitalize()}: {indicator_value}"
208
+ for indicator_type, indicator_value in indicators.items()
209
+ )
210
+ scores.append(f"{tp}: {' - '.join(indicator)}")
211
+ crowdsec_context_object.add_attribute("scores", ", ".join(scores))
212
+ crowdsec_context_object.add_reference(misp_attribute.uuid, "related-to")
213
+ misp_event.add_object(crowdsec_context_object)
214
+
215
+ event = json.loads(misp_event.to_json())
216
+ results = {
217
+ key: event[key]
218
+ for key in ("Attribute", "Object")
219
+ if (key in event and event[key])
220
+ }
221
+ return {"results": results}
222
+
223
+
224
+ def get_country_name_from_alpha_2(alpha_2):
225
+ country_info = pycountry.countries.get(alpha_2=alpha_2)
226
+ return country_info.name if country_info else None
227
+
228
+
229
+ def introspection():
230
+ return mispattributes
231
+
232
+
233
+ def version():
234
+ moduleinfo["config"] = moduleconfig
235
+ return moduleinfo
@@ -0,0 +1,148 @@
1
+ import json
2
+ from . import check_input_attribute, standard_error_message
3
+ from falconpy import Intel
4
+ from pymisp import MISPAttribute, MISPEvent
5
+
6
+ moduleinfo = {
7
+ 'version': '0.2',
8
+ 'author': 'Christophe Vandeplas',
9
+ 'description': 'Module to query CrowdStrike Falcon.',
10
+ 'module-type': ['expansion', 'hover'],
11
+ 'name': 'CrowdStrike Falcon',
12
+ 'logo': 'crowdstrike.png',
13
+ 'requirements': ['A CrowdStrike API access (API id & key)'],
14
+ 'features': 'This module takes a MISP attribute as input to query a CrowdStrike Falcon API. The API returns then the result of the query with some types we map into compatible types we add as MISP attributes.\n\nPlease note that composite attributes composed by at least one of the input types mentionned below (domains, IPs, hostnames) are also supported.',
15
+ 'references': ['https://www.crowdstrike.com/products/crowdstrike-falcon-faq/'],
16
+ 'input': 'A MISP attribute included in the following list:\n- domain\n- email-attachment\n- email-dst\n- email-reply-to\n- email-src\n- email-subject\n- filename\n- hostname\n- ip-src\n- ip-dst\n- md5\n- mutex\n- regkey\n- sha1\n- sha256\n- uri\n- url\n- user-agent\n- whois-registrant-email\n- x509-fingerprint-md5',
17
+ 'output': 'MISP attributes mapped after the CrowdStrike API has been queried, included in the following list:\n- hostname\n- email-src\n- email-subject\n- filename\n- md5\n- sha1\n- sha256\n- ip-dst\n- ip-dst\n- mutex\n- regkey\n- url\n- user-agent\n- x509-fingerprint-md5',
18
+ }
19
+ moduleconfig = ['api_id', 'apikey']
20
+ misperrors = {'error': 'Error'}
21
+ misp_type_in = ['domain', 'email-attachment', 'email-dst', 'email-reply-to', 'email-src', 'email-subject',
22
+ 'filename', 'hostname', 'ip', 'ip-src', 'ip-dst', 'md5', 'mutex', 'regkey', 'sha1', 'sha256', 'uri', 'url',
23
+ 'user-agent', 'whois-registrant-email', 'x509-fingerprint-md5']
24
+ mapping_out = { # mapping between the MISP attributes type and the compatible CrowdStrike indicator types.
25
+ 'domain': {'type': 'hostname', 'to_ids': True},
26
+ 'email_address': {'type': 'email-src', 'to_ids': True},
27
+ 'email_subject': {'type': 'email-subject', 'to_ids': True},
28
+ 'file_name': {'type': 'filename', 'to_ids': True},
29
+ 'hash_md5': {'type': 'md5', 'to_ids': True},
30
+ 'hash_sha1': {'type': 'sha1', 'to_ids': True},
31
+ 'hash_sha256': {'type': 'sha256', 'to_ids': True},
32
+ 'ip_address': {'type': 'ip-dst', 'to_ids': True},
33
+ 'ip_address_block': {'type': 'ip-dst', 'to_ids': True},
34
+ 'mutex_name': {'type': 'mutex', 'to_ids': True},
35
+ 'registry': {'type': 'regkey', 'to_ids': True},
36
+ 'url': {'type': 'url', 'to_ids': True},
37
+ 'user_agent': {'type': 'user-agent', 'to_ids': True},
38
+ 'x509_serial': {'type': 'x509-fingerprint-md5', 'to_ids': True},
39
+
40
+ 'actors': {'type': 'threat-actor', 'category': 'Attribution'},
41
+ 'malware_families': {'type': 'text', 'category': 'Attribution'}
42
+ }
43
+ misp_type_out = [item['type'] for item in mapping_out.values()]
44
+ mispattributes = {'input': misp_type_in, 'format': 'misp_standard'}
45
+
46
+ def handler(q=False):
47
+ if q is False:
48
+ return False
49
+ request = json.loads(q)
50
+ #validate CrowdStrike params
51
+ if (request.get('config')):
52
+ if (request['config'].get('apikey') is None):
53
+ misperrors['error'] = 'CrowdStrike apikey is missing'
54
+ return misperrors
55
+ if (request['config'].get('api_id') is None):
56
+ misperrors['error'] = 'CrowdStrike api_id is missing'
57
+ return misperrors
58
+
59
+ #validate attribute
60
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
61
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
62
+ attribute = request.get('attribute')
63
+ if not any(input_type == attribute.get('type') for input_type in misp_type_in):
64
+ return {'error': 'Unsupported attribute type.'}
65
+
66
+ client = CSIntelAPI(request['config']['api_id'], request['config']['apikey'])
67
+
68
+ attribute = MISPAttribute()
69
+ attribute.from_dict(**request.get('attribute') )
70
+ r = {"results": []}
71
+ valid_type = False
72
+
73
+ try:
74
+ for k in misp_type_in:
75
+ if attribute.type == k:
76
+ # map the MISP type to the CrowdStrike type
77
+ r['results'].append(lookup_indicator(client, attribute))
78
+ valid_type = True
79
+ except Exception as e:
80
+ return {'error': f"{e}"}
81
+
82
+ if not valid_type:
83
+ misperrors['error'] = "Unsupported attributes type"
84
+ return misperrors
85
+ return {'results': r.get('results').pop()}
86
+
87
+
88
+ def lookup_indicator(client, ref_attribute):
89
+ result = client.search_indicator(ref_attribute.value)
90
+ misp_event = MISPEvent()
91
+ misp_event.add_attribute(**ref_attribute)
92
+
93
+ for item in result.get('resources', []):
94
+ for relation in item.get('relations'):
95
+ if mapping_out.get(relation.get('type')):
96
+ r = mapping_out[relation.get('type')].copy()
97
+ r['value'] = relation.get('indicator')
98
+ attribute = MISPAttribute()
99
+ attribute.from_dict(**r)
100
+ misp_event.add_attribute(**attribute)
101
+ for actor in item.get('actors'):
102
+ r = mapping_out.get('actors').copy()
103
+ r['value'] = actor
104
+ attribute = MISPAttribute()
105
+ attribute.from_dict(**r)
106
+ misp_event.add_attribute(**attribute)
107
+ if item.get('malware_families'):
108
+ r = mapping_out.get('malware_families').copy()
109
+ r['value'] = f"malware_families: {' | '.join(item.get('malware_families'))}"
110
+ attribute = MISPAttribute()
111
+ attribute.from_dict(**r)
112
+ misp_event.add_attribute(**attribute)
113
+
114
+ event = json.loads(misp_event.to_json())
115
+ return {'Object': event.get('Object', []), 'Attribute': event.get('Attribute', [])}
116
+
117
+ def introspection():
118
+ return mispattributes
119
+
120
+
121
+ def version():
122
+ moduleinfo['config'] = moduleconfig
123
+ return moduleinfo
124
+
125
+
126
+ class CSIntelAPI():
127
+ def __init__(self, custid=None, custkey=None):
128
+ # customer id and key should be passed when obj is created
129
+ self.falcon = Intel(client_id=custid, client_secret=custkey)
130
+
131
+ def search_indicator(self, query):
132
+ r = self.falcon.query_indicator_entities(q=query)
133
+ # 400 - bad request
134
+ if r.get('status_code') == 400:
135
+ raise Exception('HTTP Error 400 - Bad request.')
136
+
137
+ # 404 - oh shit
138
+ if r.get('status_code') == 404:
139
+ raise Exception('HTTP Error 404 - awww snap.')
140
+
141
+ # catch all?
142
+ if r.get('status_code') != 200:
143
+ raise Exception('HTTP Error: ' + str(r.get('status_code')))
144
+
145
+ if len(r.get('body').get('errors')):
146
+ raise Exception('API Error: ' + ' | '.join(r.get('body').get('errors')))
147
+
148
+ return r.get('body', {})
@@ -0,0 +1,161 @@
1
+ import base64
2
+ import io
3
+ import json
4
+ import logging
5
+ import requests
6
+ import sys
7
+ import urllib.parse
8
+ import zipfile
9
+
10
+ from requests.exceptions import RequestException
11
+
12
+ log = logging.getLogger("cuckoo_submit")
13
+ log.setLevel(logging.DEBUG)
14
+ sh = logging.StreamHandler(sys.stdout)
15
+ sh.setLevel(logging.DEBUG)
16
+ fmt = logging.Formatter(
17
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
18
+ )
19
+ sh.setFormatter(fmt)
20
+ log.addHandler(sh)
21
+
22
+ moduleinfo = {
23
+ 'version': '0.1',
24
+ 'author': 'Evert Kors',
25
+ 'description': 'Submit files and URLs to Cuckoo Sandbox',
26
+ 'module-type': ['expansion', 'hover'],
27
+ 'name': 'Cuckoo Submit',
28
+ 'logo': 'cuckoo.png',
29
+ 'requirements': ['Access to a Cuckoo Sandbox API and an API key if the API requires it. (api_url and api_key)'],
30
+ 'features': 'The module takes a malware-sample, attachment, url or domain and submits it to Cuckoo Sandbox.\n The returned task id can be used to retrieve results when the analysis completed.',
31
+ 'references': ['https://cuckoosandbox.org/', 'https://cuckoo.sh/docs/'],
32
+ 'input': 'A malware-sample or attachment for files. A url or domain for URLs.',
33
+ 'output': "A text field containing 'Cuckoo task id: <id>'",
34
+ }
35
+ misperrors = {"error": "Error"}
36
+ moduleconfig = ["api_url", "api_key"]
37
+ mispattributes = {
38
+ "input": ["attachment", "malware-sample", "url", "domain"],
39
+ "output": ["text"]
40
+ }
41
+
42
+
43
+ class APIKeyError(RequestException):
44
+ """Raised if the Cuckoo API returns a 401. This means no or an invalid
45
+ bearer token was supplied."""
46
+ pass
47
+
48
+
49
+ class CuckooAPI(object):
50
+
51
+ def __init__(self, api_url, api_key=""):
52
+ self.api_key = api_key
53
+ if not api_url.startswith("http"):
54
+ api_url = "https://{}".format(api_url)
55
+
56
+ self.api_url = api_url
57
+
58
+ def _post_api(self, endpoint, files=None, data={}):
59
+ data.update({
60
+ "owner": "MISP"
61
+ })
62
+
63
+ try:
64
+ response = requests.post(
65
+ urllib.parse.urljoin(self.api_url, endpoint),
66
+ files=files, data=data,
67
+ headers={"Authorization": "Bearer {}".format(self.api_key)}
68
+ )
69
+ except RequestException as e:
70
+ log.error("Failed to submit sample to Cuckoo Sandbox. %s", e)
71
+ return None
72
+
73
+ if response.status_code == 401:
74
+ raise APIKeyError("Invalid or no Cuckoo Sandbox API key provided")
75
+
76
+ if response.status_code != 200:
77
+ log.error("Invalid Cuckoo API response")
78
+ return None
79
+
80
+ return response.json()
81
+
82
+ def create_task(self, filename, fp):
83
+ response = self._post_api(
84
+ "/tasks/create/file", files={"file": (filename, fp)}
85
+ )
86
+ if not response:
87
+ return False
88
+
89
+ return response["task_id"]
90
+
91
+ def create_url(self, url):
92
+ response = self._post_api(
93
+ "/tasks/create/url", data={"url": url}
94
+ )
95
+ if not response:
96
+ return False
97
+
98
+ return response["task_id"]
99
+
100
+
101
+ def handler(q=False):
102
+ if q is False:
103
+ return False
104
+
105
+ request = json.loads(q)
106
+
107
+ # See if the API URL was provided. The API key is optional, as it can
108
+ # be disabled in the Cuckoo API settings.
109
+ api_url = request["config"].get("api_url")
110
+ api_key = request["config"].get("api_key", "")
111
+ if not api_url:
112
+ misperrors["error"] = "No Cuckoo API URL provided"
113
+ return misperrors
114
+
115
+ url = request.get("url") or request.get("domain")
116
+ data = request.get("data")
117
+ filename = None
118
+ if data:
119
+ data = base64.b64decode(data)
120
+
121
+ if "malware-sample" in request:
122
+ filename = request.get("malware-sample").split("|", 1)[0]
123
+ with zipfile.ZipFile(io.BytesIO(data)) as zipf:
124
+ data = zipf.read(zipf.namelist()[0], pwd=b"infected")
125
+
126
+ elif "attachment" in request:
127
+ filename = request.get("attachment")
128
+
129
+ cuckoo_api = CuckooAPI(api_url=api_url, api_key=api_key)
130
+ task_id = None
131
+ try:
132
+ if url:
133
+ log.debug("Submitting URL to Cuckoo Sandbox %s", api_url)
134
+ task_id = cuckoo_api.create_url(url)
135
+ elif data and filename:
136
+ log.debug("Submitting file to Cuckoo Sandbox %s", api_url)
137
+ task_id = cuckoo_api.create_task(
138
+ filename=filename, fp=io.BytesIO(data)
139
+ )
140
+ except APIKeyError as e:
141
+ misperrors["error"] = "Failed to submit to Cuckoo: {}".format(e)
142
+ return misperrors
143
+
144
+ if not task_id:
145
+ misperrors["error"] = "File or URL submission failed"
146
+ return misperrors
147
+
148
+ return {
149
+ "results": [
150
+ {"types": "text", "values": "Cuckoo task id: {}".format(task_id)}
151
+ ]
152
+ }
153
+
154
+
155
+ def introspection():
156
+ return mispattributes
157
+
158
+
159
+ def version():
160
+ moduleinfo["config"] = moduleconfig
161
+ return moduleinfo