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,133 @@
1
+ import json
2
+ import requests
3
+ from . import check_input_attribute, standard_error_message
4
+ from ._vulnerability_parser.vulnerability_parser import (
5
+ VulnerabilityMapping, VulnerabilityParser)
6
+ from pymisp import MISPObject
7
+
8
+ misperrors = {'error': 'Error'}
9
+ mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'}
10
+ moduleinfo = {
11
+ 'version': '1',
12
+ 'author': 'Christian Studer',
13
+ 'description': 'An expansion module to query the VARIoT db API for more information about a vulnerability.',
14
+ 'module-type': ['expansion', 'hover'],
15
+ 'name': 'VARIoT db Lookup',
16
+ 'logo': 'variot.png',
17
+ 'requirements': ['A VARIoT db API key (if you do not want to be limited to 100 queries / day)'],
18
+ 'features': 'The module takes a vulnerability attribute as input and queries que VARIoT db API to gather additional information.\n\nThe `vuln` endpoint is queried first to look for additional information about the vulnerability itself.\n\nThe `exploits` endpoint is also queried then to look for the information of the potential related exploits, which are parsed and added to the results using the `exploit` object template.',
19
+ 'references': ['https://www.variotdbs.pl/'],
20
+ 'input': 'Vulnerability attribute.',
21
+ 'output': 'Additional information about the vulnerability, as it is stored on the VARIoT db, about the vulnerability itself, and the potential related exploits.',
22
+ }
23
+ moduleconfig = ['API_key']
24
+ variotdbs_url = 'https://www.variotdbs.pl/api'
25
+
26
+
27
+ class VariotMapping(VulnerabilityMapping):
28
+ __exploit_mapping = {
29
+ 'credits': 'credit',
30
+ 'description': 'description',
31
+ 'exploit': 'exploit',
32
+ 'title': 'title'
33
+ }
34
+ __exploit_multiple_mapping = {
35
+ 'cve': {
36
+ 'feature': 'cve_id',
37
+ 'relation': 'cve-id'
38
+ },
39
+ 'references': {
40
+ 'feature': 'url',
41
+ 'relation': 'reference'
42
+ }
43
+ }
44
+
45
+ @classmethod
46
+ def exploit_mapping(cls) -> dict:
47
+ return cls.__exploit_mapping
48
+
49
+ @classmethod
50
+ def exploit_multiple_mapping(cls) -> dict:
51
+ return cls.__exploit_multiple_mapping
52
+
53
+
54
+ class VariotdbsParser(VulnerabilityParser):
55
+ def __init__(self, attribute):
56
+ super().__init__(attribute)
57
+ self.__mapping = VulnerabilityMapping
58
+
59
+ @property
60
+ def mapping(self) -> VulnerabilityMapping:
61
+ return self.__mapping
62
+
63
+ def parse_exploit_information(self, query_results):
64
+ for exploit in query_results:
65
+ exploit_object = MISPObject('exploit')
66
+ exploit_object.add_attribute('exploitdb-id', exploit['edb_id'])
67
+ for field, relation in self.mapping.exploit_mapping().items():
68
+ if exploit.get(field):
69
+ exploit_object.add_attribute(
70
+ relation, exploit[field]['data']
71
+ )
72
+ for field, relation in self.mapping.exploit_multiple_mapping().items():
73
+ if exploit.get(field):
74
+ for value in exploit[field]['data']:
75
+ exploit_object.add_attribute(
76
+ relation['relation'], value[relation['feature']]
77
+ )
78
+ exploit_object.add_reference(self.misp_attribute.uuid, 'related-to')
79
+ self.misp_event.add_object(exploit_object)
80
+
81
+
82
+ def handler(q=False):
83
+ if q is False:
84
+ return False
85
+ request = json.loads(q)
86
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
87
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
88
+ attribute = request['attribute']
89
+ if attribute.get('type') != 'vulnerability':
90
+ return {'error': 'Vulnerability id missing.'}
91
+ headers = {'Content-Type': 'application/json'}
92
+ if request.get('config', {}).get('API_key'):
93
+ headers['Authorization'] = f"Token {request['config']['API_key']}"
94
+ empty = True
95
+ parser = VariotdbsParser(attribute)
96
+ r = requests.get(f"{variotdbs_url}/vuln/{attribute['value']}/", headers=headers)
97
+ if r.status_code == 200:
98
+ vulnerability_results = r.json()
99
+ if vulnerability_results:
100
+ parser._parse_variot_description(vulnerability_results)
101
+ empty = False
102
+ else:
103
+ if r.reason != 'Not Found':
104
+ return {'error': 'Error while querying the variotdbs API.'}
105
+ r = requests.get(f"{variotdbs_url}/exploits/?cve={attribute['value']}", headers=headers)
106
+ if r.status_code == 200:
107
+ exploit_results = r.json()
108
+ if exploit_results:
109
+ parser.parse_exploit_information(exploit_results['results'])
110
+ empty = False
111
+ if exploit_results['next'] is not None:
112
+ while(1):
113
+ exploit_results = requests.get(exploit_results['next'], headers=headers)
114
+ if exploit_results.status_code != 200:
115
+ break
116
+ exploit_results = exploit_results.json()
117
+ parser.parse_exploit_information(exploit_results['results'])
118
+ if exploit_results['next'] is None:
119
+ break
120
+ else:
121
+ return {'error': 'Error while querying the variotdbs API.'}
122
+ if empty:
123
+ return {'error': 'Empty results'}
124
+ return parser.get_results()
125
+
126
+
127
+ def introspection():
128
+ return mispattributes
129
+
130
+
131
+ def version():
132
+ moduleinfo['config'] = moduleconfig
133
+ return moduleinfo
@@ -0,0 +1,311 @@
1
+ from urllib.parse import urlparse
2
+ import vt
3
+ from . import check_input_attribute, standard_error_message
4
+ from pymisp import MISPAttribute, MISPEvent, MISPObject
5
+
6
+ misperrors = {'error': 'Error'}
7
+ mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "url", "ip-src|port", "ip-dst|port"],
8
+ 'format': 'misp_standard'}
9
+
10
+ # possible module-types: 'expansion', 'hover' or both
11
+ moduleinfo = {
12
+ 'version': '6',
13
+ 'author': 'Hannah Ward',
14
+ 'description': 'Enrich observables with the VirusTotal v3 API',
15
+ 'module-type': ['expansion'],
16
+ 'name': 'VirusTotal v3 Lookup',
17
+ 'logo': 'virustotal.png',
18
+ 'requirements': ['An access to the VirusTotal API (apikey), with a high request rate limit.'],
19
+ 'features': 'New format of modules able to return attributes and objects.\n\nA module to take a MISP attribute as input and query the VirusTotal API to get additional data about it.\n\nCompared to the [standard VirusTotal expansion module](https://github.com/MISP/misp-modules/blob/main/misp_modules/modules/expansion/virustotal_public.py), this module is made for advanced parsing of VirusTotal report, with a recursive analysis of the elements found after the first request.\n\nThus, it requires a higher request rate limit to avoid the API to return a 204 error (Request rate limit exceeded), and the data parsed from the different requests are returned as MISP attributes and objects, with the corresponding relations between each one of them.',
20
+ 'references': ['https://www.virustotal.com/', 'https://docs.virustotal.com/reference/overview'],
21
+ 'input': 'A domain, hash (md5, sha1, sha256 or sha512), hostname or IP address attribute.',
22
+ 'output': 'MISP attributes and objects resulting from the parsing of the VirusTotal report concerning the input attribute.',
23
+ }
24
+
25
+ # config fields that your code expects from the site admin
26
+ moduleconfig = ["apikey", "event_limit", 'proxy_host', 'proxy_port', 'proxy_username', 'proxy_password']
27
+
28
+
29
+ DEFAULT_RESULTS_LIMIT = 10
30
+
31
+
32
+ class VirusTotalParser:
33
+ def __init__(self, client: vt.Client, limit: int) -> None:
34
+ self.client = client
35
+ self.limit = limit or DEFAULT_RESULTS_LIMIT
36
+ self.misp_event = MISPEvent()
37
+ self.attribute = MISPAttribute()
38
+ self.parsed_objects = {}
39
+ self.input_types_mapping = {'ip-src': self.parse_ip, 'ip-dst': self.parse_ip,
40
+ 'domain': self.parse_domain, 'hostname': self.parse_domain,
41
+ 'md5': self.parse_hash, 'sha1': self.parse_hash,
42
+ 'sha256': self.parse_hash, 'url': self.parse_url,
43
+ 'ip-src|port': self.parse_ip_port, 'ip-dst|port': self.parse_ip_port}
44
+ self.proxies = None
45
+
46
+ @staticmethod
47
+ def get_total_analysis(analysis: dict, known_distributors: dict = None) -> int:
48
+ if not analysis:
49
+ return 0
50
+ count = sum([analysis['undetected'], analysis['suspicious'], analysis['harmless']])
51
+ return count if known_distributors else count + analysis['malicious']
52
+
53
+ def query_api(self, attribute: dict) -> None:
54
+ self.attribute.from_dict(**attribute)
55
+ self.input_types_mapping[self.attribute.type](self.attribute.value)
56
+
57
+ def get_result(self) -> dict:
58
+ event = self.misp_event.to_dict()
59
+ results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
60
+ return {'results': results}
61
+
62
+ def add_vt_report(self, report: vt.Object) -> str:
63
+ analysis = report.get('last_analysis_stats')
64
+ total = self.get_total_analysis(analysis, report.get('known_distributors'))
65
+ if report.type == 'ip_address':
66
+ rtype = 'ip-address'
67
+ else:
68
+ rtype = report.type
69
+ permalink = f'https://www.virustotal.com/gui/{rtype}/{report.id}'
70
+
71
+ vt_object = MISPObject('virustotal-report')
72
+ vt_object.add_attribute('permalink', type='link', value=permalink)
73
+ detection_ratio = f"{analysis['malicious']}/{total}" if analysis else '-/-'
74
+ vt_object.add_attribute('detection-ratio', type='text', value=detection_ratio, disable_correlation=True)
75
+ self.misp_event.add_object(**vt_object)
76
+ return vt_object.uuid
77
+
78
+ def create_misp_object(self, report: vt.Object) -> MISPObject:
79
+ misp_object = None
80
+ vt_uuid = self.add_vt_report(report)
81
+
82
+ if report.type == 'file':
83
+ misp_object = MISPObject('file')
84
+ for hash_type in ('md5', 'sha1', 'sha256', 'tlsh',
85
+ 'vhash', 'ssdeep', 'imphash'):
86
+ misp_object.add_attribute(hash_type,
87
+ **{'type': hash_type,
88
+ 'value': report.get(hash_type)})
89
+ elif report.type == 'domain':
90
+ misp_object = MISPObject('domain-ip')
91
+ misp_object.add_attribute('domain', type='domain', value=report.id)
92
+ elif report.type == 'ip_address':
93
+ misp_object = MISPObject('domain-ip')
94
+ misp_object.add_attribute('ip', type='ip-dst', value=report.id)
95
+ elif report.type == 'url':
96
+ misp_object = MISPObject('url')
97
+ misp_object.add_attribute('url', type='url', value=report.id)
98
+ misp_object.add_reference(vt_uuid, 'analyzed-with')
99
+ return misp_object
100
+
101
+ ################################################################################
102
+ #### Main parsing functions #### # noqa
103
+ ################################################################################
104
+
105
+ def parse_domain(self, domain: str) -> str:
106
+ domain_report = self.client.get_object(f'/domains/{domain}')
107
+
108
+ # DOMAIN
109
+ domain_object = self.create_misp_object(domain_report)
110
+
111
+ # WHOIS
112
+ if domain_report.whois:
113
+ whois_object = MISPObject('whois')
114
+ whois_object.add_attribute('text', type='text', value=domain_report.whois)
115
+ self.misp_event.add_object(**whois_object)
116
+
117
+ # SIBLINGS AND SUBDOMAINS
118
+ for relationship_name, misp_name in [('siblings', 'sibling-of'), ('subdomains', 'subdomain')]:
119
+ rel_iterator = self.client.iterator(f'/domains/{domain_report.id}/{relationship_name}', limit=self.limit)
120
+ for item in rel_iterator:
121
+ attr = MISPAttribute()
122
+ attr.from_dict(**dict(type='domain', value=item.id))
123
+ self.misp_event.add_attribute(**attr)
124
+ domain_object.add_reference(attr.uuid, misp_name)
125
+
126
+ # RESOLUTIONS
127
+ resolutions_iterator = self.client.iterator(f'/domains/{domain_report.id}/resolutions', limit=self.limit)
128
+ for resolution in resolutions_iterator:
129
+ domain_object.add_attribute('ip', type='ip-dst', value=resolution.ip_address)
130
+
131
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
132
+ for relationship_name, misp_name in [
133
+ ('communicating_files', 'communicates-with'),
134
+ ('downloaded_files', 'downloaded-from'),
135
+ ('referrer_files', 'referring')
136
+ ]:
137
+ files_iterator = self.client.iterator(f'/domains/{domain_report.id}/{relationship_name}', limit=self.limit)
138
+ for file in files_iterator:
139
+ file_object = self.create_misp_object(file)
140
+ file_object.add_reference(domain_object.uuid, misp_name)
141
+ self.misp_event.add_object(**file_object)
142
+
143
+ # URLS
144
+ urls_iterator = self.client.iterator(f'/domains/{domain_report.id}/urls', limit=self.limit)
145
+ for url in urls_iterator:
146
+ url_object = self.create_misp_object(url)
147
+ url_object.add_reference(domain_object.uuid, 'hosted-in')
148
+ self.misp_event.add_object(**url_object)
149
+
150
+ self.misp_event.add_object(**domain_object)
151
+ return domain_object.uuid
152
+
153
+ def parse_hash(self, file_hash: str) -> str:
154
+ file_report = self.client.get_object(f'/files/{file_hash}')
155
+ file_object = self.create_misp_object(file_report)
156
+
157
+ # ITW URLS
158
+ urls_iterator = self.client.iterator(f'/files/{file_report.id}/itw_urls', limit=self.limit)
159
+ for url in urls_iterator:
160
+ url_object = self.create_misp_object(url)
161
+ url_object.add_reference(file_object.uuid, 'downloaded')
162
+ self.misp_event.add_object(**url_object)
163
+
164
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
165
+ for relationship_name, misp_name in [
166
+ ('contacted_urls', 'communicates-with'),
167
+ ('contacted_domains', 'communicates-with'),
168
+ ('contacted_ips', 'communicates-with')
169
+ ]:
170
+ related_files_iterator = self.client.iterator(f'/files/{file_report.id}/{relationship_name}', limit=self.limit)
171
+ for related_file in related_files_iterator:
172
+ related_file_object = self.create_misp_object(related_file)
173
+ related_file_object.add_reference(file_object.uuid, misp_name)
174
+ self.misp_event.add_object(**related_file_object)
175
+
176
+ self.misp_event.add_object(**file_object)
177
+ return file_object.uuid
178
+ def parse_ip_port(self, ipport: str) -> str:
179
+ ip = ipport.split('|')[0]
180
+ self.parse_ip(ip)
181
+
182
+ def parse_ip(self, ip: str) -> str:
183
+ ip_report = self.client.get_object(f'/ip_addresses/{ip}')
184
+
185
+ # IP
186
+ ip_object = self.create_misp_object(ip_report)
187
+
188
+ # ASN
189
+ asn_object = MISPObject('asn')
190
+ asn_object.add_attribute('asn', type='AS', value=ip_report.asn)
191
+ asn_object.add_attribute('subnet-announced', type='ip-src', value=ip_report.network)
192
+ asn_object.add_attribute('country', type='text', value=ip_report.country)
193
+ self.misp_event.add_object(**asn_object)
194
+
195
+ # RESOLUTIONS
196
+ resolutions_iterator = self.client.iterator(f'/ip_addresses/{ip_report.id}/resolutions', limit=self.limit)
197
+ for resolution in resolutions_iterator:
198
+ ip_object.add_attribute('domain', type='domain', value=resolution.host_name)
199
+
200
+ # URLS
201
+ urls_iterator = self.client.iterator(f'/ip_addresses/{ip_report.id}/urls', limit=self.limit)
202
+ for url in urls_iterator:
203
+ url_object = self.create_misp_object(url)
204
+ url_object.add_reference(ip_object.uuid, 'hosted-in')
205
+ self.misp_event.add_object(**url_object)
206
+
207
+ self.misp_event.add_object(**ip_object)
208
+ return ip_object.uuid
209
+
210
+ def parse_url(self, url: str) -> str:
211
+ url_id = vt.url_id(url)
212
+ url_report = self.client.get_object(f'/urls/{url_id}')
213
+ url_object = self.create_misp_object(url_report)
214
+
215
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
216
+ for relationship_name, misp_name in [
217
+ ('communicating_files', 'communicates-with'),
218
+ ('downloaded_files', 'downloaded-from'),
219
+ ('referrer_files', 'referring')
220
+ ]:
221
+ files_iterator = self.client.iterator(f'/urls/{url_report.id}/{relationship_name}', limit=self.limit)
222
+ for file in files_iterator:
223
+ file_object = self.create_misp_object(file)
224
+ file_object.add_reference(url_object.uuid, misp_name)
225
+ self.misp_event.add_object(**file_object)
226
+
227
+ self.misp_event.add_object(**url_object)
228
+ return url_object.uuid
229
+
230
+
231
+ def get_proxy_settings(config: dict) -> dict:
232
+ """Returns proxy settings in the requests format.
233
+ If no proxy settings are set, return None."""
234
+ proxies = None
235
+ host = config.get('proxy_host')
236
+ port = config.get('proxy_port')
237
+ username = config.get('proxy_username')
238
+ password = config.get('proxy_password')
239
+
240
+ if host:
241
+ if not port:
242
+ misperrors['error'] = 'The virustotal_proxy_host config is set, ' \
243
+ 'please also set the virustotal_proxy_port.'
244
+ raise KeyError
245
+ parsed = urlparse(host)
246
+ if 'http' in parsed.scheme:
247
+ scheme = 'http'
248
+ else:
249
+ scheme = parsed.scheme
250
+ netloc = parsed.netloc
251
+ host = f'{netloc}:{port}'
252
+
253
+ if username:
254
+ if not password:
255
+ misperrors['error'] = 'The virustotal_proxy_username config is set, ' \
256
+ 'please also set the virustotal_proxy_password.'
257
+ raise KeyError
258
+ auth = f'{username}:{password}'
259
+ host = auth + '@' + host
260
+
261
+ proxies = {
262
+ 'http': f'{scheme}://{host}',
263
+ 'https': f'{scheme}://{host}'
264
+ }
265
+ return proxies
266
+
267
+
268
+ def parse_error(status_code: int) -> str:
269
+ status_mapping = {204: 'VirusTotal request rate limit exceeded.',
270
+ 400: 'Incorrect request, please check the arguments.',
271
+ 403: 'You don\'t have enough privileges to make the request.'}
272
+ if status_code in status_mapping:
273
+ return status_mapping[status_code]
274
+ return "VirusTotal may not be accessible."
275
+
276
+
277
+ def dict_handler(request: dict):
278
+ if not request.get('config') or not request['config'].get('apikey'):
279
+ misperrors['error'] = 'A VirusTotal api key is required for this module.'
280
+ return misperrors
281
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
282
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
283
+ if request['attribute']['type'] not in mispattributes['input']:
284
+ return {'error': 'Unsupported attribute type.'}
285
+
286
+ event_limit = request['config'].get('event_limit')
287
+ attribute = request['attribute']
288
+ proxy_settings = get_proxy_settings(request.get('config'))
289
+
290
+ try:
291
+ client = vt.Client(request['config']['apikey'],
292
+ headers={
293
+ 'x-tool': 'MISPModuleVirusTotalExpansion',
294
+ },
295
+ proxy=proxy_settings['http'] if proxy_settings else None)
296
+ parser = VirusTotalParser(client, int(event_limit) if event_limit else None)
297
+ parser.query_api(attribute)
298
+ except vt.APIError as ex:
299
+ misperrors['error'] = ex.message
300
+ return misperrors
301
+
302
+ return parser.get_result()
303
+
304
+
305
+ def introspection():
306
+ return mispattributes
307
+
308
+
309
+ def version():
310
+ moduleinfo['config'] = moduleconfig
311
+ return moduleinfo