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.
- misp_modules/__init__.py +379 -0
- misp_modules/helpers/__init__.py +1 -0
- misp_modules/helpers/cache.py +84 -0
- misp_modules/lib/__init__.py +4 -0
- misp_modules/lib/_vmray/__init__.py +0 -0
- misp_modules/lib/_vmray/parser.py +1417 -0
- misp_modules/lib/_vmray/rest_api.py +148 -0
- misp_modules/lib/cof2misp/LICENSE-2.0.txt +202 -0
- misp_modules/lib/cof2misp/__init__.py +0 -0
- misp_modules/lib/cof2misp/cof.py +165 -0
- misp_modules/lib/joe_mapping.py +114 -0
- misp_modules/lib/joe_parser.py +573 -0
- misp_modules/lib/lastline_api.py +841 -0
- misp_modules/lib/qintel_helper.py +263 -0
- misp_modules/lib/stix2misp.py +2080 -0
- misp_modules/lib/stix2misp_mapping.py +460 -0
- misp_modules/lib/synonymsToTagNames.json +1 -0
- misp_modules/lib/vt_graph_parser/__init__.py +8 -0
- misp_modules/lib/vt_graph_parser/errors.py +20 -0
- misp_modules/lib/vt_graph_parser/helpers/__init__.py +7 -0
- misp_modules/lib/vt_graph_parser/helpers/parsers.py +88 -0
- misp_modules/lib/vt_graph_parser/helpers/rules.py +304 -0
- misp_modules/lib/vt_graph_parser/helpers/wrappers.py +58 -0
- misp_modules/lib/vt_graph_parser/importers/__init__.py +7 -0
- misp_modules/lib/vt_graph_parser/importers/base.py +98 -0
- misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +73 -0
- misp_modules/modules/__init__.py +4 -0
- misp_modules/modules/action_mod/__init__.py +1 -0
- misp_modules/modules/action_mod/_utils/__init__.py +1 -0
- misp_modules/modules/action_mod/_utils/utils.py +70 -0
- misp_modules/modules/action_mod/mattermost.py +113 -0
- misp_modules/modules/action_mod/slack.py +96 -0
- misp_modules/modules/action_mod/testaction.py +68 -0
- misp_modules/modules/expansion/__init__.py +36 -0
- misp_modules/modules/expansion/_dnsdb_query/COPYRIGHT +27 -0
- misp_modules/modules/expansion/_dnsdb_query/LICENSE +202 -0
- misp_modules/modules/expansion/_dnsdb_query/README.md +162 -0
- misp_modules/modules/expansion/_dnsdb_query/__init__.py +0 -0
- misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py +327 -0
- misp_modules/modules/expansion/_ransomcoindb/__init__.py +0 -0
- misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +96 -0
- misp_modules/modules/expansion/_vulnerability_parser/__init__.py +0 -0
- misp_modules/modules/expansion/_vulnerability_parser/vulnerability_parser.py +118 -0
- misp_modules/modules/expansion/abuseipdb.py +146 -0
- misp_modules/modules/expansion/apiosintds.py +365 -0
- misp_modules/modules/expansion/apivoid.py +133 -0
- misp_modules/modules/expansion/assemblyline_query.py +179 -0
- misp_modules/modules/expansion/assemblyline_submit.py +100 -0
- misp_modules/modules/expansion/backscatter_io.py +84 -0
- misp_modules/modules/expansion/btc_scam_check.py +54 -0
- misp_modules/modules/expansion/btc_steroids.py +238 -0
- misp_modules/modules/expansion/censys_enrich.py +287 -0
- misp_modules/modules/expansion/circl_passivedns.py +85 -0
- misp_modules/modules/expansion/circl_passivessl.py +113 -0
- misp_modules/modules/expansion/clamav.py +129 -0
- misp_modules/modules/expansion/cluster25_expand.py +239 -0
- misp_modules/modules/expansion/countrycode.py +69 -0
- misp_modules/modules/expansion/cpe.py +140 -0
- misp_modules/modules/expansion/crowdsec.py +235 -0
- misp_modules/modules/expansion/crowdstrike_falcon.py +148 -0
- misp_modules/modules/expansion/cuckoo_submit.py +161 -0
- misp_modules/modules/expansion/cve.py +57 -0
- misp_modules/modules/expansion/cve_advanced.py +185 -0
- misp_modules/modules/expansion/cytomic_orion.py +196 -0
- misp_modules/modules/expansion/dbl_spamhaus.py +78 -0
- misp_modules/modules/expansion/dns.py +71 -0
- misp_modules/modules/expansion/docx_enrich.py +71 -0
- misp_modules/modules/expansion/domaintools.py +290 -0
- misp_modules/modules/expansion/eql.py +91 -0
- misp_modules/modules/expansion/eupi.py +86 -0
- misp_modules/modules/expansion/extract_url_components.py +83 -0
- misp_modules/modules/expansion/farsight_passivedns.py +243 -0
- misp_modules/modules/expansion/geoip_asn.py +75 -0
- misp_modules/modules/expansion/geoip_city.py +75 -0
- misp_modules/modules/expansion/geoip_country.py +73 -0
- misp_modules/modules/expansion/google_safe_browsing.py +86 -0
- misp_modules/modules/expansion/google_search.py +57 -0
- misp_modules/modules/expansion/google_threat_intelligence.py +453 -0
- misp_modules/modules/expansion/greynoise.py +346 -0
- misp_modules/modules/expansion/hashdd.py +53 -0
- misp_modules/modules/expansion/hashlookup.py +118 -0
- misp_modules/modules/expansion/hibp.py +62 -0
- misp_modules/modules/expansion/html_to_markdown.py +63 -0
- misp_modules/modules/expansion/hyasinsight.py +881 -0
- misp_modules/modules/expansion/intel471.py +73 -0
- misp_modules/modules/expansion/intelmq_eventdb.py.experimental +67 -0
- misp_modules/modules/expansion/ip2locationio.py +90 -0
- misp_modules/modules/expansion/ipasn.py +70 -0
- misp_modules/modules/expansion/ipinfo.py +112 -0
- misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +633 -0
- misp_modules/modules/expansion/iprep.py +98 -0
- misp_modules/modules/expansion/jinja_template_rendering.py +54 -0
- misp_modules/modules/expansion/joesandbox_query.py +91 -0
- misp_modules/modules/expansion/joesandbox_submit.py +147 -0
- misp_modules/modules/expansion/lastline_query.py +148 -0
- misp_modules/modules/expansion/lastline_submit.py +180 -0
- misp_modules/modules/expansion/macaddress_io.py +130 -0
- misp_modules/modules/expansion/macvendors.py +55 -0
- misp_modules/modules/expansion/malshare_upload.py +107 -0
- misp_modules/modules/expansion/malwarebazaar.py +67 -0
- misp_modules/modules/expansion/mcafee_insights_enrich.py +249 -0
- misp_modules/modules/expansion/mmdb_lookup.py +138 -0
- misp_modules/modules/expansion/module.py.skeleton +44 -0
- misp_modules/modules/expansion/mwdb.py +152 -0
- misp_modules/modules/expansion/ocr_enrich.py +74 -0
- misp_modules/modules/expansion/ods_enrich.py +71 -0
- misp_modules/modules/expansion/odt_enrich.py +61 -0
- misp_modules/modules/expansion/onyphe.py +243 -0
- misp_modules/modules/expansion/onyphe_full.py +387 -0
- misp_modules/modules/expansion/otx.py +167 -0
- misp_modules/modules/expansion/passive_ssh.py +150 -0
- misp_modules/modules/expansion/passivetotal.py +358 -0
- misp_modules/modules/expansion/pdf_enrich.py +58 -0
- misp_modules/modules/expansion/pptx_enrich.py +65 -0
- misp_modules/modules/expansion/qintel_qsentry.py +229 -0
- misp_modules/modules/expansion/qrcode.py +99 -0
- misp_modules/modules/expansion/ransomcoindb.py +82 -0
- misp_modules/modules/expansion/rbl.py +126 -0
- misp_modules/modules/expansion/recordedfuture.py +544 -0
- misp_modules/modules/expansion/reversedns.py +77 -0
- misp_modules/modules/expansion/securitytrails.py +571 -0
- misp_modules/modules/expansion/shodan.py +244 -0
- misp_modules/modules/expansion/sigma_queries.py +61 -0
- misp_modules/modules/expansion/sigma_syntax_validator.py +49 -0
- misp_modules/modules/expansion/sigmf_expand.py +303 -0
- misp_modules/modules/expansion/socialscan.py +108 -0
- misp_modules/modules/expansion/sophoslabs_intelix.py +146 -0
- misp_modules/modules/expansion/sourcecache.py +57 -0
- misp_modules/modules/expansion/stairwell.py +156 -0
- misp_modules/modules/expansion/stix2_pattern_syntax_validator.py +56 -0
- misp_modules/modules/expansion/threatcrowd.py +170 -0
- misp_modules/modules/expansion/threatfox.py +75 -0
- misp_modules/modules/expansion/threatminer.py +152 -0
- misp_modules/modules/expansion/triage_submit.py +119 -0
- misp_modules/modules/expansion/trustar_enrich.py +235 -0
- misp_modules/modules/expansion/urlhaus.py +164 -0
- misp_modules/modules/expansion/urlscan.py +269 -0
- misp_modules/modules/expansion/variotdbs.py +133 -0
- misp_modules/modules/expansion/virustotal.py +311 -0
- misp_modules/modules/expansion/virustotal_public.py +272 -0
- misp_modules/modules/expansion/virustotal_upload.py +91 -0
- misp_modules/modules/expansion/vmray_submit.py +161 -0
- misp_modules/modules/expansion/vmware_nsx.py +628 -0
- misp_modules/modules/expansion/vulndb.py +292 -0
- misp_modules/modules/expansion/vulnerability_lookup.py +324 -0
- misp_modules/modules/expansion/vulners.py +81 -0
- misp_modules/modules/expansion/vysion.py +221 -0
- misp_modules/modules/expansion/whois.py +72 -0
- misp_modules/modules/expansion/whoisfreaks.py +232 -0
- misp_modules/modules/expansion/wiki.py +58 -0
- misp_modules/modules/expansion/xforceexchange.py +189 -0
- misp_modules/modules/expansion/xlsx_enrich.py +63 -0
- misp_modules/modules/expansion/yara_query.py +70 -0
- misp_modules/modules/expansion/yara_syntax_validator.py +49 -0
- misp_modules/modules/expansion/yeti.py +196 -0
- misp_modules/modules/export_mod/__init__.py +3 -0
- misp_modules/modules/export_mod/cef_export.py +93 -0
- misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py +150 -0
- misp_modules/modules/export_mod/defender_endpoint_export.py +141 -0
- misp_modules/modules/export_mod/goamlexport.py +255 -0
- misp_modules/modules/export_mod/liteexport.py +97 -0
- misp_modules/modules/export_mod/mass_eql_export.py +106 -0
- misp_modules/modules/export_mod/nexthinkexport.py +131 -0
- misp_modules/modules/export_mod/osqueryexport.py +125 -0
- misp_modules/modules/export_mod/pdfexport.py +107 -0
- misp_modules/modules/export_mod/testexport.py +73 -0
- misp_modules/modules/export_mod/threatStream_misp_export.py +114 -0
- misp_modules/modules/export_mod/threat_connect_export.py +125 -0
- misp_modules/modules/export_mod/virustotal_collections.py +141 -0
- misp_modules/modules/export_mod/vt_graph.py +120 -0
- misp_modules/modules/export_mod/yara_export.py +290 -0
- misp_modules/modules/import_mod/__init__.py +22 -0
- misp_modules/modules/import_mod/cof2misp.py +264 -0
- misp_modules/modules/import_mod/csvimport.py +319 -0
- misp_modules/modules/import_mod/cuckooimport.py +749 -0
- misp_modules/modules/import_mod/email_import.py +294 -0
- misp_modules/modules/import_mod/goamlimport.py +188 -0
- misp_modules/modules/import_mod/import_blueprint.py +93 -0
- misp_modules/modules/import_mod/joe_import.py +74 -0
- misp_modules/modules/import_mod/lastline_import.py +160 -0
- misp_modules/modules/import_mod/mispjson.py +73 -0
- misp_modules/modules/import_mod/ocr.py +122 -0
- misp_modules/modules/import_mod/openiocimport.py +99 -0
- misp_modules/modules/import_mod/taxii21.py +383 -0
- misp_modules/modules/import_mod/testimport.py +73 -0
- misp_modules/modules/import_mod/threatanalyzer_import.py +552 -0
- misp_modules/modules/import_mod/url_import.py +97 -0
- misp_modules/modules/import_mod/vmray_import.py +96 -0
- misp_modules/modules/import_mod/vmray_summary_json_import.py +87 -0
- misp_modules-2.4.196.dist-info/LICENSE +661 -0
- misp_modules-2.4.196.dist-info/METADATA +281 -0
- misp_modules-2.4.196.dist-info/RECORD +194 -0
- misp_modules-2.4.196.dist-info/WHEEL +4 -0
- misp_modules-2.4.196.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
#!/usr/local/bin/python
|
|
2
|
+
# Copyright © 2024 The Google Threat Intelligence authors. All Rights Reserved.
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Google Threat Intelligence MISP expansion module."""
|
|
16
|
+
|
|
17
|
+
from urllib import parse
|
|
18
|
+
import vt
|
|
19
|
+
import pymisp
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
mispattributes = {
|
|
23
|
+
'input': [
|
|
24
|
+
'hostname',
|
|
25
|
+
'domain',
|
|
26
|
+
'ip-src',
|
|
27
|
+
'ip-dst',
|
|
28
|
+
'md5',
|
|
29
|
+
'sha1',
|
|
30
|
+
'sha256',
|
|
31
|
+
'url',
|
|
32
|
+
],
|
|
33
|
+
'format': 'misp_standard',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
moduleinfo = {
|
|
37
|
+
'version': '2',
|
|
38
|
+
'author': 'Google Threat Intelligence team',
|
|
39
|
+
'description': "An expansion module to have the observable's threat score assessed by Google Threat Intelligence.",
|
|
40
|
+
'module-type': ['expansion'],
|
|
41
|
+
'name': 'Google Threat Intelligence Lookup',
|
|
42
|
+
'config': ['apikey', 'event_limit', 'proxy_host', 'proxy_port', 'proxy_username', 'proxy_password'],
|
|
43
|
+
'logo': 'google_threat_intelligence.png',
|
|
44
|
+
'requirements': ['An access to the Google Threat Intelligence API (apikey), with a high request rate limit.'],
|
|
45
|
+
'features': 'GTI assessment for the given observable, this include information about level of severity, a clear verdict (malicious, suspicious, undetected and benign) and additional information provided by the Mandiant expertise combined with the VirusTotal database.\n\n[Output example screeshot](https://github.com/MISP/MISP/assets/4747608/e275db2f-bb1e-4413-8cc0-ec3cb05e0414)',
|
|
46
|
+
'references': ['https://www.virustotal.com/', 'https://gtidocs.virustotal.com/reference'],
|
|
47
|
+
'input': 'A domain, hash (md5, sha1, sha256 or sha512), hostname or IP address attribute.',
|
|
48
|
+
'output': 'Text fields containing the threat score, the severity, the verdict and the threat label of the observable inspected.',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
DEFAULT_RESULTS_LIMIT = 10
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class GoogleThreatIntelligenceParser:
|
|
55
|
+
"""Main parser class to create the MISP event."""
|
|
56
|
+
def __init__(self, client: vt.Client, limit: int) -> None:
|
|
57
|
+
self.client = client
|
|
58
|
+
self.limit = limit or DEFAULT_RESULTS_LIMIT
|
|
59
|
+
self.misp_event = pymisp.MISPEvent()
|
|
60
|
+
self.attribute = pymisp.MISPAttribute()
|
|
61
|
+
self.parsed_objects = {}
|
|
62
|
+
self.input_types_mapping = {
|
|
63
|
+
'ip-src': self.parse_ip,
|
|
64
|
+
'ip-dst': self.parse_ip,
|
|
65
|
+
'domain': self.parse_domain,
|
|
66
|
+
'hostname': self.parse_domain,
|
|
67
|
+
'md5': self.parse_hash,
|
|
68
|
+
'sha1': self.parse_hash,
|
|
69
|
+
'sha256': self.parse_hash,
|
|
70
|
+
'url': self.parse_url,
|
|
71
|
+
'ip-src|port': self.parse_ip_port,
|
|
72
|
+
'ip-dst|port': self.parse_ip_port,
|
|
73
|
+
}
|
|
74
|
+
self.proxies = None
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def get_total_analysis(analysis: dict,
|
|
78
|
+
known_distributors: dict = None) -> int:
|
|
79
|
+
"""Get total """
|
|
80
|
+
if not analysis:
|
|
81
|
+
return 0
|
|
82
|
+
count = sum([analysis['undetected'],
|
|
83
|
+
analysis['suspicious'],
|
|
84
|
+
analysis['harmless']])
|
|
85
|
+
return count if known_distributors else count + analysis['malicious']
|
|
86
|
+
|
|
87
|
+
def query_api(self, attribute: dict) -> None:
|
|
88
|
+
"""Get data from the API and parse it."""
|
|
89
|
+
self.attribute.from_dict(**attribute)
|
|
90
|
+
self.input_types_mapping[self.attribute.type](self.attribute.value)
|
|
91
|
+
|
|
92
|
+
def get_results(self) -> dict:
|
|
93
|
+
"""Serialize the MISP event."""
|
|
94
|
+
event = self.misp_event.to_dict()
|
|
95
|
+
results = {
|
|
96
|
+
key: event[key] for key in ('Attribute', 'Object') \
|
|
97
|
+
if (key in event and event[key])
|
|
98
|
+
}
|
|
99
|
+
return {'results': results}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def add_gti_report(self, report: vt.Object) -> str:
|
|
103
|
+
analysis = report.get('last_analysis_stats')
|
|
104
|
+
total = self.get_total_analysis(analysis,
|
|
105
|
+
report.get('known_distributors'))
|
|
106
|
+
if report.type == 'ip_address':
|
|
107
|
+
rtype = 'ip-address'
|
|
108
|
+
else:
|
|
109
|
+
rtype = report.type
|
|
110
|
+
permalink = f'https://www.virustotal.com/gui/{rtype}/{report.id}'
|
|
111
|
+
|
|
112
|
+
gti_object = pymisp.MISPObject('google-threat-intelligence-report')
|
|
113
|
+
gti_object.add_attribute('permalink', type='link', value=permalink)
|
|
114
|
+
ratio = f"{analysis['malicious']}/{total}" if analysis else '-/-'
|
|
115
|
+
gti_object.add_attribute('detection-ratio',
|
|
116
|
+
type='text',
|
|
117
|
+
value=ratio,
|
|
118
|
+
disable_correlation=True)
|
|
119
|
+
report_dict = report.to_dict()
|
|
120
|
+
gti_object.add_attribute(
|
|
121
|
+
'threat-score', type='text',
|
|
122
|
+
value=get_key(report_dict,
|
|
123
|
+
'attributes.gti_assessment.threat_score.value'))
|
|
124
|
+
gti_object.add_attribute(
|
|
125
|
+
'verdict', type='text',
|
|
126
|
+
value=get_key(report_dict,
|
|
127
|
+
'attributes.gti_assessment.verdict.value').replace(
|
|
128
|
+
'VERDICT_', ''))
|
|
129
|
+
gti_object.add_attribute(
|
|
130
|
+
'severity', type='text',
|
|
131
|
+
value=get_key(report_dict,
|
|
132
|
+
'attributes.gti_assessment.severity.value').replace(
|
|
133
|
+
'SEVERITY_', ''))
|
|
134
|
+
self.misp_event.add_object(**gti_object)
|
|
135
|
+
return gti_object.uuid
|
|
136
|
+
|
|
137
|
+
def create_misp_object(self, report: vt.Object) -> pymisp.MISPObject:
|
|
138
|
+
misp_object = None
|
|
139
|
+
gti_uuid = self.add_gti_report(report)
|
|
140
|
+
|
|
141
|
+
if report.type == 'file':
|
|
142
|
+
misp_object = pymisp.MISPObject('file')
|
|
143
|
+
for hash_type in ('md5', 'sha1', 'sha256', 'tlsh',
|
|
144
|
+
'vhash', 'ssdeep', 'imphash'):
|
|
145
|
+
misp_object.add_attribute(hash_type,
|
|
146
|
+
**{'type': hash_type,
|
|
147
|
+
'value': report.get(hash_type)})
|
|
148
|
+
elif report.type == 'domain':
|
|
149
|
+
misp_object = pymisp.MISPObject('domain-ip')
|
|
150
|
+
misp_object.add_attribute('domain', type='domain', value=report.id)
|
|
151
|
+
elif report.type == 'ip_address':
|
|
152
|
+
misp_object = pymisp.MISPObject('domain-ip')
|
|
153
|
+
misp_object.add_attribute('ip', type='ip-dst', value=report.id)
|
|
154
|
+
elif report.type == 'url':
|
|
155
|
+
misp_object = pymisp.MISPObject('url')
|
|
156
|
+
misp_object.add_attribute('url', type='url', value=report.id)
|
|
157
|
+
misp_object.add_reference(gti_uuid, 'analyzed-with')
|
|
158
|
+
return misp_object
|
|
159
|
+
|
|
160
|
+
def parse_domain(self, domain: str) -> str:
|
|
161
|
+
domain_report = self.client.get_object(f'/domains/{domain}')
|
|
162
|
+
|
|
163
|
+
# DOMAIN
|
|
164
|
+
domain_object = self.create_misp_object(domain_report)
|
|
165
|
+
|
|
166
|
+
# WHOIS
|
|
167
|
+
if domain_report.whois:
|
|
168
|
+
whois_object = pymisp.MISPObject('whois')
|
|
169
|
+
whois_object.add_attribute('text', type='text',
|
|
170
|
+
value=domain_report.whois)
|
|
171
|
+
self.misp_event.add_object(**whois_object)
|
|
172
|
+
|
|
173
|
+
# SIBLINGS AND SUBDOMAINS
|
|
174
|
+
for relationship_name, misp_name in [
|
|
175
|
+
('siblings', 'sibling-of'), ('subdomains', 'subdomain')]:
|
|
176
|
+
rel_iterator = self.client.iterator(
|
|
177
|
+
f'/domains/{domain_report.id}/{relationship_name}',
|
|
178
|
+
limit=self.limit)
|
|
179
|
+
for item in rel_iterator:
|
|
180
|
+
attr = pymisp.MISPAttribute()
|
|
181
|
+
attr.from_dict(**dict(type='domain', value=item.id))
|
|
182
|
+
self.misp_event.add_attribute(**attr)
|
|
183
|
+
domain_object.add_reference(attr.uuid, misp_name)
|
|
184
|
+
|
|
185
|
+
# RESOLUTIONS
|
|
186
|
+
resolutions_iterator = self.client.iterator(
|
|
187
|
+
f'/domains/{domain_report.id}/resolutions', limit=self.limit)
|
|
188
|
+
for resolution in resolutions_iterator:
|
|
189
|
+
domain_object.add_attribute('ip', type='ip-dst',
|
|
190
|
+
value=resolution.ip_address)
|
|
191
|
+
|
|
192
|
+
# COMMUNICATING, DOWNLOADED AND REFERRER FILES
|
|
193
|
+
for relationship_name, misp_name in [
|
|
194
|
+
('communicating_files', 'communicates-with'),
|
|
195
|
+
('downloaded_files', 'downloaded-from'),
|
|
196
|
+
('referrer_files', 'referring')
|
|
197
|
+
]:
|
|
198
|
+
files_iterator = self.client.iterator(
|
|
199
|
+
f'/domains/{domain_report.id}/{relationship_name}',
|
|
200
|
+
limit=self.limit)
|
|
201
|
+
for file in files_iterator:
|
|
202
|
+
file_object = self.create_misp_object(file)
|
|
203
|
+
file_object.add_reference(domain_object.uuid, misp_name)
|
|
204
|
+
self.misp_event.add_object(**file_object)
|
|
205
|
+
|
|
206
|
+
# URLS
|
|
207
|
+
urls_iterator = self.client.iterator(
|
|
208
|
+
f'/domains/{domain_report.id}/urls', limit=self.limit)
|
|
209
|
+
for url in urls_iterator:
|
|
210
|
+
url_object = self.create_misp_object(url)
|
|
211
|
+
url_object.add_reference(domain_object.uuid, 'hosted-in')
|
|
212
|
+
self.misp_event.add_object(**url_object)
|
|
213
|
+
|
|
214
|
+
self.misp_event.add_object(**domain_object)
|
|
215
|
+
return domain_object.uuid
|
|
216
|
+
|
|
217
|
+
def parse_hash(self, file_hash: str) -> str:
|
|
218
|
+
file_report = self.client.get_object(f'/files/{file_hash}')
|
|
219
|
+
file_object = self.create_misp_object(file_report)
|
|
220
|
+
|
|
221
|
+
# ITW URLS
|
|
222
|
+
urls_iterator = self.client.iterator(
|
|
223
|
+
f'/files/{file_report.id}/itw_urls', limit=self.limit)
|
|
224
|
+
for url in urls_iterator:
|
|
225
|
+
url_object = self.create_misp_object(url)
|
|
226
|
+
url_object.add_reference(file_object.uuid, 'downloaded')
|
|
227
|
+
self.misp_event.add_object(**url_object)
|
|
228
|
+
|
|
229
|
+
# COMMUNICATING, DOWNLOADED AND REFERRER FILES
|
|
230
|
+
for relationship_name, misp_name in [
|
|
231
|
+
('contacted_urls', 'communicates-with'),
|
|
232
|
+
('contacted_domains', 'communicates-with'),
|
|
233
|
+
('contacted_ips', 'communicates-with')
|
|
234
|
+
]:
|
|
235
|
+
related_files_iterator = self.client.iterator(
|
|
236
|
+
f'/files/{file_report.id}/{relationship_name}', limit=self.limit)
|
|
237
|
+
for related_file in related_files_iterator:
|
|
238
|
+
related_file_object = self.create_misp_object(related_file)
|
|
239
|
+
related_file_object.add_reference(file_object.uuid, misp_name)
|
|
240
|
+
self.misp_event.add_object(**related_file_object)
|
|
241
|
+
|
|
242
|
+
self.misp_event.add_object(**file_object)
|
|
243
|
+
return file_object.uuid
|
|
244
|
+
|
|
245
|
+
def parse_ip_port(self, ipport: str) -> str:
|
|
246
|
+
ip = ipport.split('|')[0]
|
|
247
|
+
self.parse_ip(ip)
|
|
248
|
+
|
|
249
|
+
def parse_ip(self, ip: str) -> str:
|
|
250
|
+
ip_report = self.client.get_object(f'/ip_addresses/{ip}')
|
|
251
|
+
|
|
252
|
+
# IP
|
|
253
|
+
ip_object = self.create_misp_object(ip_report)
|
|
254
|
+
|
|
255
|
+
# ASN
|
|
256
|
+
asn_object = pymisp.MISPObject('asn')
|
|
257
|
+
asn_object.add_attribute('asn', type='AS', value=ip_report.asn)
|
|
258
|
+
asn_object.add_attribute('subnet-announced', type='ip-src',
|
|
259
|
+
value=ip_report.network)
|
|
260
|
+
asn_object.add_attribute('country', type='text',
|
|
261
|
+
value=ip_report.country)
|
|
262
|
+
self.misp_event.add_object(**asn_object)
|
|
263
|
+
|
|
264
|
+
# RESOLUTIONS
|
|
265
|
+
resolutions_iterator = self.client.iterator(
|
|
266
|
+
f'/ip_addresses/{ip_report.id}/resolutions', limit=self.limit)
|
|
267
|
+
for resolution in resolutions_iterator:
|
|
268
|
+
ip_object.add_attribute('domain', type='domain',
|
|
269
|
+
value=resolution.host_name)
|
|
270
|
+
|
|
271
|
+
# URLS
|
|
272
|
+
urls_iterator = self.client.iterator(
|
|
273
|
+
f'/ip_addresses/{ip_report.id}/urls', limit=self.limit)
|
|
274
|
+
for url in urls_iterator:
|
|
275
|
+
url_object = self.create_misp_object(url)
|
|
276
|
+
url_object.add_reference(ip_object.uuid, 'hosted-in')
|
|
277
|
+
self.misp_event.add_object(**url_object)
|
|
278
|
+
|
|
279
|
+
self.misp_event.add_object(**ip_object)
|
|
280
|
+
return ip_object.uuid
|
|
281
|
+
|
|
282
|
+
def parse_url(self, url: str) -> str:
|
|
283
|
+
url_id = vt.url_id(url)
|
|
284
|
+
url_report = self.client.get_object(f'/urls/{url_id}')
|
|
285
|
+
url_object = self.create_misp_object(url_report)
|
|
286
|
+
|
|
287
|
+
# COMMUNICATING, DOWNLOADED AND REFERRER FILES
|
|
288
|
+
for relationship_name, misp_name in [
|
|
289
|
+
('communicating_files', 'communicates-with'),
|
|
290
|
+
('downloaded_files', 'downloaded-from'),
|
|
291
|
+
('referrer_files', 'referring')
|
|
292
|
+
]:
|
|
293
|
+
files_iterator = self.client.iterator(
|
|
294
|
+
f'/urls/{url_report.id}/{relationship_name}', limit=self.limit)
|
|
295
|
+
for file in files_iterator:
|
|
296
|
+
file_object = self.create_misp_object(file)
|
|
297
|
+
file_object.add_reference(url_object.uuid, misp_name)
|
|
298
|
+
self.misp_event.add_object(**file_object)
|
|
299
|
+
|
|
300
|
+
self.misp_event.add_object(**url_object)
|
|
301
|
+
return url_object.uuid
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def get_key(dictionary, key, default_value=''):
|
|
305
|
+
"""Get value from nested dictionaries."""
|
|
306
|
+
dictionary = dictionary or {}
|
|
307
|
+
keys = key.split('.')
|
|
308
|
+
field_name = keys.pop()
|
|
309
|
+
for k in keys:
|
|
310
|
+
if k not in dictionary:
|
|
311
|
+
return default_value
|
|
312
|
+
dictionary = dictionary[k]
|
|
313
|
+
return dictionary.get(field_name, default_value)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def get_proxy_settings(config: dict) -> dict:
|
|
317
|
+
"""Returns proxy settings in the requests format or None if not set up."""
|
|
318
|
+
proxies = None
|
|
319
|
+
host = config.get('proxy_host')
|
|
320
|
+
port = config.get('proxy_port')
|
|
321
|
+
username = config.get('proxy_username')
|
|
322
|
+
password = config.get('proxy_password')
|
|
323
|
+
|
|
324
|
+
if host:
|
|
325
|
+
if not port:
|
|
326
|
+
raise KeyError(
|
|
327
|
+
('The google_threat_intelligence_proxy_host config is set, '
|
|
328
|
+
'please also set the virustotal_proxy_port.'))
|
|
329
|
+
parsed = parse.urlparse(host)
|
|
330
|
+
if 'http' in parsed.scheme:
|
|
331
|
+
scheme = 'http'
|
|
332
|
+
else:
|
|
333
|
+
scheme = parsed.scheme
|
|
334
|
+
netloc = parsed.netloc
|
|
335
|
+
host = f'{netloc}:{port}'
|
|
336
|
+
|
|
337
|
+
if username:
|
|
338
|
+
if not password:
|
|
339
|
+
raise KeyError(('The google_threat_intelligence_'
|
|
340
|
+
' proxy_host config is set, please also'
|
|
341
|
+
' set the virustotal_proxy_password.'))
|
|
342
|
+
auth = f'{username}:{password}'
|
|
343
|
+
host = auth + '@' + host
|
|
344
|
+
|
|
345
|
+
proxies = {
|
|
346
|
+
'http': f'{scheme}://{host}',
|
|
347
|
+
'https': f'{scheme}://{host}'
|
|
348
|
+
}
|
|
349
|
+
return proxies
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def dict_handler(request: dict):
|
|
353
|
+
"""MISP entry point fo the module."""
|
|
354
|
+
if not request.get('config') or not request['config'].get('apikey'):
|
|
355
|
+
return {
|
|
356
|
+
'error': ('A Google Threat Intelligence api '
|
|
357
|
+
'key is required for this module.')
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if not request.get('attribute'):
|
|
361
|
+
return {
|
|
362
|
+
'error': ('This module requires an "attribute" field as input,'
|
|
363
|
+
' which should contain at least a type, a value and an'
|
|
364
|
+
' uuid.')
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if request['attribute']['type'] not in mispattributes['input']:
|
|
368
|
+
return {'error': 'Unsupported attribute type.'}
|
|
369
|
+
|
|
370
|
+
event_limit = request['config'].get('event_limit')
|
|
371
|
+
attribute = request['attribute']
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
proxy_settings = get_proxy_settings(request.get('config'))
|
|
375
|
+
client = vt.Client(
|
|
376
|
+
request['config']['apikey'],
|
|
377
|
+
headers={
|
|
378
|
+
'x-tool': 'MISPModuleGTIExpansion',
|
|
379
|
+
},
|
|
380
|
+
proxy=proxy_settings['http'] if proxy_settings else None)
|
|
381
|
+
parser = GoogleThreatIntelligenceParser(
|
|
382
|
+
client, int(event_limit) if event_limit else None)
|
|
383
|
+
parser.query_api(attribute)
|
|
384
|
+
except vt.APIError as ex:
|
|
385
|
+
return {'error': ex.message}
|
|
386
|
+
except KeyError as ex:
|
|
387
|
+
return {'error': str(ex)}
|
|
388
|
+
|
|
389
|
+
return parser.get_results()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def introspection():
|
|
393
|
+
"""Returns the module input attributes required."""
|
|
394
|
+
return mispattributes
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def version():
|
|
398
|
+
"""Returns the module metadata."""
|
|
399
|
+
return moduleinfo
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if __name__ == '__main__':
|
|
403
|
+
# Testing/debug calls.
|
|
404
|
+
import os
|
|
405
|
+
api_key = os.getenv('GTI_API_KEY')
|
|
406
|
+
# File
|
|
407
|
+
request_data = {
|
|
408
|
+
'config': {'apikey': api_key},
|
|
409
|
+
'attribute': {
|
|
410
|
+
'type': 'sha256',
|
|
411
|
+
'value': ('ed01ebfbc9eb5bbea545af4d01bf5f10'
|
|
412
|
+
'71661840480439c6e5babe8e080e41aa')
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
response = dict_handler(request_data)
|
|
416
|
+
report_obj = response['results']['Object'][0]
|
|
417
|
+
print(report_obj.to_dict())
|
|
418
|
+
|
|
419
|
+
# URL
|
|
420
|
+
request_data = {
|
|
421
|
+
'config': {'apikey': api_key},
|
|
422
|
+
'attribute': {
|
|
423
|
+
'type': 'url',
|
|
424
|
+
'value': 'http://47.21.48.182:60813/Mozi.a'
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
response = dict_handler(request_data)
|
|
428
|
+
report_obj = response['results']['Object'][0]
|
|
429
|
+
print(report_obj.to_dict())
|
|
430
|
+
|
|
431
|
+
# Ip
|
|
432
|
+
request_data = {
|
|
433
|
+
'config': {'apikey': api_key},
|
|
434
|
+
'attribute': {
|
|
435
|
+
'type': 'ip-src',
|
|
436
|
+
'value': '180.72.148.38'
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
response = dict_handler(request_data)
|
|
440
|
+
report_obj = response['results']['Object'][0]
|
|
441
|
+
print(report_obj.to_dict())
|
|
442
|
+
|
|
443
|
+
# Domain
|
|
444
|
+
request_data = {
|
|
445
|
+
'config': {'apikey': api_key},
|
|
446
|
+
'attribute': {
|
|
447
|
+
'type': 'domain',
|
|
448
|
+
'value': 'qexyhuv.com'
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
response = dict_handler(request_data)
|
|
452
|
+
report_obj = response['results']['Object'][0]
|
|
453
|
+
print(report_obj.to_dict())
|