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,249 @@
|
|
|
1
|
+
# Written by mohlcyber 13.08.2021
|
|
2
|
+
# MISP Module for McAfee MVISION Insights to query campaign details
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import requests
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from . import check_input_attribute, standard_error_message
|
|
10
|
+
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
|
11
|
+
|
|
12
|
+
misperrors = {'error': 'Error'}
|
|
13
|
+
mispattributes = {'input': ["md5", "sha1", "sha256"],
|
|
14
|
+
'format': 'misp_standard'}
|
|
15
|
+
|
|
16
|
+
# possible module-types: 'expansion', 'hover' or both
|
|
17
|
+
moduleinfo = {
|
|
18
|
+
'version': '1',
|
|
19
|
+
'author': 'Martin Ohl',
|
|
20
|
+
'description': 'Lookup McAfee MVISION Insights Details',
|
|
21
|
+
'module-type': ['hover'],
|
|
22
|
+
'name': 'McAfee MVISION Insights Lookup',
|
|
23
|
+
'logo': '',
|
|
24
|
+
'requirements': [],
|
|
25
|
+
'features': '',
|
|
26
|
+
'references': [],
|
|
27
|
+
'input': '',
|
|
28
|
+
'output': '',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# config fields that your code expects from the site admin
|
|
32
|
+
moduleconfig = ['api_key', 'client_id', 'client_secret']
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class MVAPI():
|
|
36
|
+
def __init__(self, attribute, api_key, client_id, client_secret):
|
|
37
|
+
self.misp_event = MISPEvent()
|
|
38
|
+
self.attribute = MISPAttribute()
|
|
39
|
+
self.attribute.from_dict(**attribute)
|
|
40
|
+
self.misp_event.add_attribute(**self.attribute)
|
|
41
|
+
|
|
42
|
+
self.base_url = 'https://api.mvision.mcafee.com'
|
|
43
|
+
self.session = requests.Session()
|
|
44
|
+
|
|
45
|
+
self.api_key = api_key
|
|
46
|
+
auth = (client_id, client_secret)
|
|
47
|
+
|
|
48
|
+
self.logging()
|
|
49
|
+
self.auth(auth)
|
|
50
|
+
|
|
51
|
+
def logging(self):
|
|
52
|
+
self.logger = logging.getLogger('logs')
|
|
53
|
+
self.logger.setLevel('INFO')
|
|
54
|
+
handler = logging.StreamHandler()
|
|
55
|
+
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")
|
|
56
|
+
handler.setFormatter(formatter)
|
|
57
|
+
self.logger.addHandler(handler)
|
|
58
|
+
|
|
59
|
+
def auth(self, auth):
|
|
60
|
+
iam_url = "https://iam.mcafee-cloud.com/iam/v1.1/token"
|
|
61
|
+
|
|
62
|
+
headers = {
|
|
63
|
+
'x-api-key': self.api_key,
|
|
64
|
+
'Content-Type': 'application/vnd.api+json'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
payload = {
|
|
68
|
+
"grant_type": "client_credentials",
|
|
69
|
+
"scope": "ins.user ins.suser ins.ms.r"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
res = self.session.post(iam_url, headers=headers, auth=auth, data=payload)
|
|
73
|
+
|
|
74
|
+
if res.status_code != 200:
|
|
75
|
+
self.logger.error('Could not authenticate to get the IAM token: {0} - {1}'.format(res.status_code, res.text))
|
|
76
|
+
sys.exit()
|
|
77
|
+
else:
|
|
78
|
+
self.logger.info('Successful authenticated.')
|
|
79
|
+
access_token = res.json()['access_token']
|
|
80
|
+
headers['Authorization'] = 'Bearer ' + access_token
|
|
81
|
+
self.session.headers = headers
|
|
82
|
+
|
|
83
|
+
def search_ioc(self):
|
|
84
|
+
filters = {
|
|
85
|
+
'filter[type][eq]': self.attribute.type,
|
|
86
|
+
'filter[value]': self.attribute.value,
|
|
87
|
+
'fields': 'id, type, value, coverage, uid, is_coat, is_sdb_dirty, category, comment, campaigns, threat, prevalence'
|
|
88
|
+
}
|
|
89
|
+
res = self.session.get(self.base_url + '/insights/v2/iocs', params=filters)
|
|
90
|
+
|
|
91
|
+
if res.ok:
|
|
92
|
+
if len(res.json()['data']) == 0:
|
|
93
|
+
self.logger.info('No Hash details in MVISION Insights found.')
|
|
94
|
+
else:
|
|
95
|
+
self.logger.info('Successfully retrieved MVISION Insights details.')
|
|
96
|
+
self.logger.debug(res.text)
|
|
97
|
+
return res.json()
|
|
98
|
+
else:
|
|
99
|
+
self.logger.error('Error in search_ioc. HTTP {0} - {1}'.format(str(res.status_code), res.text))
|
|
100
|
+
sys.exit()
|
|
101
|
+
|
|
102
|
+
def prep_result(self, ioc):
|
|
103
|
+
res = ioc['data'][0]
|
|
104
|
+
results = []
|
|
105
|
+
|
|
106
|
+
# Parse out Attribute Category
|
|
107
|
+
category_attr = {
|
|
108
|
+
'type': 'text',
|
|
109
|
+
'object_relation': 'text',
|
|
110
|
+
'value': 'Attribute Category: {0}'.format(res['attributes']['category'])
|
|
111
|
+
}
|
|
112
|
+
results.append(category_attr)
|
|
113
|
+
|
|
114
|
+
# Parse out Attribute Comment
|
|
115
|
+
comment_attr = {
|
|
116
|
+
'type': 'text',
|
|
117
|
+
'object_relation': 'text',
|
|
118
|
+
'value': 'Attribute Comment: {0}'.format(res['attributes']['comment'])
|
|
119
|
+
}
|
|
120
|
+
results.append(comment_attr)
|
|
121
|
+
|
|
122
|
+
# Parse out Attribute Dat Coverage
|
|
123
|
+
cover_attr = {
|
|
124
|
+
'type': 'text',
|
|
125
|
+
'object_relation': 'text',
|
|
126
|
+
'value': 'Dat Version Coverage: {0}'.format(res['attributes']['coverage']['dat_version']['min'])
|
|
127
|
+
}
|
|
128
|
+
results.append(cover_attr)
|
|
129
|
+
|
|
130
|
+
# Parse out if Dirty
|
|
131
|
+
cover_attr = {
|
|
132
|
+
'type': 'text',
|
|
133
|
+
'object_relation': 'text',
|
|
134
|
+
'value': 'Is Dirty: {0}'.format(res['attributes']['is-sdb-dirty'])
|
|
135
|
+
}
|
|
136
|
+
results.append(cover_attr)
|
|
137
|
+
|
|
138
|
+
# Parse our targeted countries
|
|
139
|
+
countries_dict = []
|
|
140
|
+
countries = res['attributes']['prevalence']['countries']
|
|
141
|
+
|
|
142
|
+
for country in countries:
|
|
143
|
+
countries_dict.append(country['iso_code'])
|
|
144
|
+
|
|
145
|
+
country_attr = {
|
|
146
|
+
'type': 'text',
|
|
147
|
+
'object_relation': 'text',
|
|
148
|
+
'value': 'Targeted Countries: {0}'.format(countries_dict)
|
|
149
|
+
}
|
|
150
|
+
results.append(country_attr)
|
|
151
|
+
|
|
152
|
+
# Parse out targeted sectors
|
|
153
|
+
sectors_dict = []
|
|
154
|
+
sectors = res['attributes']['prevalence']['sectors']
|
|
155
|
+
|
|
156
|
+
for sector in sectors:
|
|
157
|
+
sectors_dict.append(sector['sector'])
|
|
158
|
+
|
|
159
|
+
sector_attr = {
|
|
160
|
+
'type': 'text',
|
|
161
|
+
'object_relation': 'text',
|
|
162
|
+
'value': 'Targeted Sectors: {0}'.format(sectors_dict)
|
|
163
|
+
}
|
|
164
|
+
results.append(sector_attr)
|
|
165
|
+
|
|
166
|
+
# Parse out Threat Classification
|
|
167
|
+
threat_class_attr = {
|
|
168
|
+
'type': 'text',
|
|
169
|
+
'object_relation': 'text',
|
|
170
|
+
'value': 'Threat Classification: {0}'.format(res['attributes']['threat']['classification'])
|
|
171
|
+
}
|
|
172
|
+
results.append(threat_class_attr)
|
|
173
|
+
|
|
174
|
+
# Parse out Threat Name
|
|
175
|
+
threat_name_attr = {
|
|
176
|
+
'type': 'text',
|
|
177
|
+
'object_relation': 'text',
|
|
178
|
+
'value': 'Threat Name: {0}'.format(res['attributes']['threat']['name'])
|
|
179
|
+
}
|
|
180
|
+
results.append(threat_name_attr)
|
|
181
|
+
|
|
182
|
+
# Parse out Threat Severity
|
|
183
|
+
threat_sev_attr = {
|
|
184
|
+
'type': 'text',
|
|
185
|
+
'object_relation': 'text',
|
|
186
|
+
'value': 'Threat Severity: {0}'.format(res['attributes']['threat']['severity'])
|
|
187
|
+
}
|
|
188
|
+
results.append(threat_sev_attr)
|
|
189
|
+
|
|
190
|
+
# Parse out Attribute ID
|
|
191
|
+
attr_id = {
|
|
192
|
+
'type': 'text',
|
|
193
|
+
'object_relation': 'text',
|
|
194
|
+
'value': 'Attribute ID: {0}'.format(res['id'])
|
|
195
|
+
}
|
|
196
|
+
results.append(attr_id)
|
|
197
|
+
|
|
198
|
+
# Parse out Campaign Relationships
|
|
199
|
+
campaigns = ioc['included']
|
|
200
|
+
|
|
201
|
+
for campaign in campaigns:
|
|
202
|
+
campaign_attr = {
|
|
203
|
+
'type': 'campaign-name',
|
|
204
|
+
'object_relation': 'campaign-name',
|
|
205
|
+
'value': campaign['attributes']['name']
|
|
206
|
+
}
|
|
207
|
+
results.append(campaign_attr)
|
|
208
|
+
|
|
209
|
+
mv_insights_obj = MISPObject(name='MVISION Insights Details')
|
|
210
|
+
for mvi_res in results:
|
|
211
|
+
mv_insights_obj.add_attribute(**mvi_res)
|
|
212
|
+
mv_insights_obj.add_reference(self.attribute.uuid, 'mvision-insights-details')
|
|
213
|
+
|
|
214
|
+
self.misp_event.add_object(mv_insights_obj)
|
|
215
|
+
|
|
216
|
+
event = json.loads(self.misp_event.to_json())
|
|
217
|
+
results_mvi = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
|
|
218
|
+
|
|
219
|
+
return {'results': results_mvi}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def handler(q=False):
|
|
223
|
+
if q is False:
|
|
224
|
+
return False
|
|
225
|
+
request = json.loads(q)
|
|
226
|
+
|
|
227
|
+
if not request.get('config') or not request['config'].get('api_key') or not request['config'].get('client_id') or not request['config'].get('client_secret'):
|
|
228
|
+
misperrors['error'] = "Please provide MVISION API Key, Client ID and Client Secret."
|
|
229
|
+
return misperrors
|
|
230
|
+
if request['attribute']['type'] not in mispattributes['input']:
|
|
231
|
+
return {'error': 'Unsupported attribute type. Please use {0}'.format(mispattributes['input'])}
|
|
232
|
+
|
|
233
|
+
api_key = request['config']['api_key']
|
|
234
|
+
client_id = request['config']['client_id']
|
|
235
|
+
client_secret = request['config']['client_secret']
|
|
236
|
+
attribute = request['attribute']
|
|
237
|
+
|
|
238
|
+
mvi = MVAPI(attribute, api_key, client_id, client_secret)
|
|
239
|
+
res = mvi.search_ioc()
|
|
240
|
+
return mvi.prep_result(res)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def introspection():
|
|
244
|
+
return mispattributes
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def version():
|
|
248
|
+
moduleinfo['config'] = moduleconfig
|
|
249
|
+
return moduleinfo
|
|
@@ -0,0 +1,138 @@
|
|
|
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': ['ip-src', 'ip-src|port', 'ip-dst', 'ip-dst|port'], 'format': 'misp_standard'}
|
|
8
|
+
moduleinfo = {
|
|
9
|
+
'version': '1',
|
|
10
|
+
'author': 'Jeroen Pinoy',
|
|
11
|
+
'description': "A hover and expansion module to enrich an ip with geolocation and ASN information from an mmdb server instance, such as CIRCL's ip.circl.lu.",
|
|
12
|
+
'module-type': ['expansion', 'hover'],
|
|
13
|
+
'name': 'GeoIP Enrichment',
|
|
14
|
+
'logo': 'circl.png',
|
|
15
|
+
'requirements': [],
|
|
16
|
+
'features': 'The module takes an IP address related attribute as input.\n It queries the public CIRCL.lu mmdb-server instance, available at ip.circl.lu, by default. The module can be configured with a custom mmdb server url if required.\n It is also possible to filter results on 1 db_source by configuring db_source_filter.',
|
|
17
|
+
'references': ['https://data.public.lu/fr/datasets/geo-open-ip-address-geolocation-per-country-in-mmdb-format/', 'https://github.com/adulau/mmdb-server'],
|
|
18
|
+
'input': 'An IP address attribute (for example ip-src or ip-src|port).',
|
|
19
|
+
'output': 'Geolocation and asn objects.',
|
|
20
|
+
}
|
|
21
|
+
moduleconfig = ["custom_API", "db_source_filter"]
|
|
22
|
+
mmdblookup_url = 'https://ip.circl.lu/'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MmdbLookupParser():
|
|
26
|
+
def __init__(self, attribute, mmdblookupresult, api_url):
|
|
27
|
+
self.attribute = attribute
|
|
28
|
+
self.mmdblookupresult = mmdblookupresult
|
|
29
|
+
self.api_url = api_url
|
|
30
|
+
self.misp_event = MISPEvent()
|
|
31
|
+
self.misp_event.add_attribute(**attribute)
|
|
32
|
+
|
|
33
|
+
def get_result(self):
|
|
34
|
+
event = json.loads(self.misp_event.to_json())
|
|
35
|
+
results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
|
|
36
|
+
return {'results': results}
|
|
37
|
+
|
|
38
|
+
def parse_mmdblookup_information(self):
|
|
39
|
+
# There is a chance some db's have a hit while others don't so we have to check if entry is empty each time
|
|
40
|
+
for result_entry in self.mmdblookupresult:
|
|
41
|
+
if result_entry['country_info']:
|
|
42
|
+
mmdblookup_object = MISPObject('geolocation')
|
|
43
|
+
mmdblookup_object.add_attribute('country',
|
|
44
|
+
**{'type': 'text', 'value': result_entry['country_info']['Country']})
|
|
45
|
+
mmdblookup_object.add_attribute('countrycode',
|
|
46
|
+
**{'type': 'text', 'value': result_entry['country']['iso_code']})
|
|
47
|
+
mmdblookup_object.add_attribute('latitude',
|
|
48
|
+
**{'type': 'float',
|
|
49
|
+
'value': result_entry['country_info']['Latitude (average)']})
|
|
50
|
+
mmdblookup_object.add_attribute('longitude',
|
|
51
|
+
**{'type': 'float',
|
|
52
|
+
'value': result_entry['country_info']['Longitude (average)']})
|
|
53
|
+
mmdblookup_object.add_attribute('text',
|
|
54
|
+
**{'type': 'text',
|
|
55
|
+
'value': 'db_source: {}. build_db: {}. Latitude and longitude are country average.'.format(
|
|
56
|
+
result_entry['meta']['db_source'],
|
|
57
|
+
result_entry['meta']['build_db'])})
|
|
58
|
+
mmdblookup_object.add_reference(self.attribute['uuid'], 'related-to')
|
|
59
|
+
self.misp_event.add_object(mmdblookup_object)
|
|
60
|
+
if 'AutonomousSystemNumber' in result_entry['country']:
|
|
61
|
+
mmdblookup_object_asn = MISPObject('asn')
|
|
62
|
+
mmdblookup_object_asn.add_attribute('asn',
|
|
63
|
+
**{'type': 'text',
|
|
64
|
+
'value': result_entry['country'][
|
|
65
|
+
'AutonomousSystemNumber']})
|
|
66
|
+
mmdblookup_object_asn.add_attribute('description',
|
|
67
|
+
**{'type': 'text',
|
|
68
|
+
'value': 'ASNOrganization: {}. db_source: {}. build_db: {}.'.format(
|
|
69
|
+
result_entry['country'][
|
|
70
|
+
'AutonomousSystemOrganization'],
|
|
71
|
+
result_entry['meta']['db_source'],
|
|
72
|
+
result_entry['meta']['build_db'])})
|
|
73
|
+
mmdblookup_object_asn.add_reference(self.attribute['uuid'], 'related-to')
|
|
74
|
+
self.misp_event.add_object(mmdblookup_object_asn)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def check_url(url):
|
|
78
|
+
return "{}/".format(url) if not url.endswith('/') else url
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def handler(q=False):
|
|
82
|
+
if q is False:
|
|
83
|
+
return False
|
|
84
|
+
request = json.loads(q)
|
|
85
|
+
if not request.get('attribute') or not check_input_attribute(request['attribute']):
|
|
86
|
+
return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
|
|
87
|
+
attribute = request['attribute']
|
|
88
|
+
if attribute.get('type') == 'ip-src':
|
|
89
|
+
toquery = attribute['value']
|
|
90
|
+
elif attribute.get('type') == 'ip-src|port':
|
|
91
|
+
toquery = attribute['value'].split('|')[0]
|
|
92
|
+
elif attribute.get('type') == 'ip-dst':
|
|
93
|
+
toquery = attribute['value']
|
|
94
|
+
elif attribute.get('type') == 'ip-dst|port':
|
|
95
|
+
toquery = attribute['value'].split('|')[0]
|
|
96
|
+
else:
|
|
97
|
+
misperrors['error'] = 'There is no attribute of type ip-src or ip-dst provided as input'
|
|
98
|
+
return misperrors
|
|
99
|
+
api_url = check_url(request['config']['custom_API']) if 'config' in request and request['config'].get(
|
|
100
|
+
'custom_API') else mmdblookup_url
|
|
101
|
+
r = requests.get("{}/geolookup/{}".format(api_url, toquery))
|
|
102
|
+
if r.status_code == 200:
|
|
103
|
+
mmdblookupresult = r.json()
|
|
104
|
+
if not mmdblookupresult or len(mmdblookupresult) == 0:
|
|
105
|
+
misperrors['error'] = 'Empty result returned by server'
|
|
106
|
+
return misperrors
|
|
107
|
+
if 'config' in request and request['config'].get('db_source_filter'):
|
|
108
|
+
db_source_filter = request['config'].get('db_source_filter')
|
|
109
|
+
mmdblookupresult = [entry for entry in mmdblookupresult if entry['meta']['db_source'] == db_source_filter]
|
|
110
|
+
if not mmdblookupresult or len(mmdblookupresult) == 0:
|
|
111
|
+
misperrors['error'] = 'There was no result with the selected db_source'
|
|
112
|
+
return misperrors
|
|
113
|
+
# Server might return one or multiple entries which could all be empty, we check if there is at least one
|
|
114
|
+
# non-empty result below
|
|
115
|
+
empty_result = True
|
|
116
|
+
for lookup_result_entry in mmdblookupresult:
|
|
117
|
+
if lookup_result_entry['country_info']:
|
|
118
|
+
empty_result = False
|
|
119
|
+
break
|
|
120
|
+
if empty_result:
|
|
121
|
+
misperrors['error'] = 'Empty result returned by server'
|
|
122
|
+
return misperrors
|
|
123
|
+
else:
|
|
124
|
+
misperrors['error'] = 'API not accessible - http status code {} was returned'.format(r.status_code)
|
|
125
|
+
return misperrors
|
|
126
|
+
parser = MmdbLookupParser(attribute, mmdblookupresult, api_url)
|
|
127
|
+
parser.parse_mmdblookup_information()
|
|
128
|
+
result = parser.get_result()
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def introspection():
|
|
133
|
+
return mispattributes
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def version():
|
|
137
|
+
moduleinfo['config'] = moduleconfig
|
|
138
|
+
return moduleinfo
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
misperrors = {'error': 'Error'}
|
|
4
|
+
mispattributes = {'input': ['hostname', 'domain'], 'output': ['ip-src',
|
|
5
|
+
'ip-dst']}
|
|
6
|
+
|
|
7
|
+
# possible module-types: 'expansion', 'hover' or both
|
|
8
|
+
moduleinfo = {'version': '1', 'author': 'YOUR_NAME_HERE',
|
|
9
|
+
'description': 'MODULE_DESCRIPTION',
|
|
10
|
+
'module-type': ['expansion', 'hover']}
|
|
11
|
+
|
|
12
|
+
# config fields that your code expects from the site admin
|
|
13
|
+
moduleconfig = []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handler(q=False):
|
|
17
|
+
if q is False:
|
|
18
|
+
return False
|
|
19
|
+
request = json.loads(q)
|
|
20
|
+
# You code that converts the data passed along in request
|
|
21
|
+
# input will be the query values
|
|
22
|
+
# output should be a list of dictionaries with types and values
|
|
23
|
+
# The types will be the types that the user can choose between
|
|
24
|
+
# for a given value
|
|
25
|
+
# An example would be a network indicator enrichment that returns
|
|
26
|
+
# a list of IP addresses and domain names.
|
|
27
|
+
# IP addresses would allow for attributes type ip-src and ip-dst,
|
|
28
|
+
# either of which the user can choose
|
|
29
|
+
# So the resulting list would look like this:
|
|
30
|
+
# r = {'results': [{'types': ['ip-src', 'ip-dst'], 'values': ips},
|
|
31
|
+
# {'types': ['domain'], 'values': domains}]}
|
|
32
|
+
r = {'results': [{'types': types1, 'values': values1},
|
|
33
|
+
{'types': types2, 'values': values2}]}
|
|
34
|
+
return r
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def introspection():
|
|
38
|
+
return mispattributes
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def version():
|
|
42
|
+
moduleinfo['config'] = moduleconfig
|
|
43
|
+
return moduleinfo
|
|
44
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import base64
|
|
4
|
+
#from distutils.util import strtobool
|
|
5
|
+
|
|
6
|
+
import io
|
|
7
|
+
import zipfile
|
|
8
|
+
|
|
9
|
+
from pymisp import PyMISP
|
|
10
|
+
from mwdblib import MWDB
|
|
11
|
+
|
|
12
|
+
misperrors = {'error': 'Error'}
|
|
13
|
+
mispattributes = {'input': ['attachment', 'malware-sample'], 'output': ['link']}
|
|
14
|
+
moduleinfo = {
|
|
15
|
+
'version': '1',
|
|
16
|
+
'author': 'Koen Van Impe',
|
|
17
|
+
'description': 'Module to push malware samples to a MWDB instance',
|
|
18
|
+
'module-type': ['expansion'],
|
|
19
|
+
'name': 'MWDB Submit',
|
|
20
|
+
'logo': '',
|
|
21
|
+
'requirements': ['* mwdblib installed (pip install mwdblib) ; * (optional) keys.py file to add tags of events/attributes to MWDB * (optional) MWDB attribute created for the link back to MISP (defined in mwdb_misp_attribute)'],
|
|
22
|
+
'features': 'An expansion module to push malware samples to a MWDB (https://github.com/CERT-Polska/mwdb-core) instance. This module does not push samples to a sandbox. This can be achieved via Karton (connected to the MWDB). Does: * Upload of attachment or malware sample to MWDB * Tags of events and/or attributes are added to MWDB. * Comment of the MISP attribute is added to MWDB. * A link back to the MISP event is added to MWDB via the MWDB attribute. * A link to the MWDB attribute is added as an enrichted attribute to the MISP event.',
|
|
23
|
+
'references': [],
|
|
24
|
+
'input': 'Attachment or malware sample',
|
|
25
|
+
'output': 'Link attribute that points to the sample at the MWDB instane',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
moduleconfig = ['mwdb_apikey', 'mwdb_url', 'mwdb_misp_attribute', 'mwdb_public', 'include_tags_event', 'include_tags_attribute']
|
|
29
|
+
|
|
30
|
+
pymisp_keys_file = "/var/www/MISP/PyMISP/"
|
|
31
|
+
mwdb_public_default = True
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
An expansion module to push malware samples to a MWDB (https://github.com/CERT-Polska/mwdb-core) instance.
|
|
35
|
+
This module does not push samples to a sandbox. This can be achieved via Karton (connected to the MWDB)
|
|
36
|
+
|
|
37
|
+
Does:
|
|
38
|
+
- Upload of attachment or malware sample to MWDB
|
|
39
|
+
- Tags of events and/or attributes are added to MWDB.
|
|
40
|
+
- Comment of the MISP attribute is added to MWDB.
|
|
41
|
+
- A link back to the MISP event is added to MWDB via the MWDB attribute.
|
|
42
|
+
- A link to the MWDB attribute is added as an enriched attribute to the MISP event.
|
|
43
|
+
|
|
44
|
+
Requires
|
|
45
|
+
- mwdblib installed (pip install mwdblib)
|
|
46
|
+
- (optional) keys.py file to add tags of events/attributes to MWDB
|
|
47
|
+
- (optional) MWDB "attribute" created for the link back to MISP (defined in mwdb_misp_attribute)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def handler(q=False):
|
|
52
|
+
if q is False:
|
|
53
|
+
return False
|
|
54
|
+
request = json.loads(q)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
data = request.get("data")
|
|
58
|
+
if 'malware-sample' in request:
|
|
59
|
+
# malicious samples are encrypted with zip (password infected) and then base64 encoded
|
|
60
|
+
sample_filename = request.get("malware-sample").split("|", 1)[0]
|
|
61
|
+
data = base64.b64decode(data)
|
|
62
|
+
fl = io.BytesIO(data)
|
|
63
|
+
zf = zipfile.ZipFile(fl)
|
|
64
|
+
sample_hashname = zf.namelist()[0]
|
|
65
|
+
data = zf.read(sample_hashname, b"infected")
|
|
66
|
+
zf.close()
|
|
67
|
+
elif 'attachment' in request:
|
|
68
|
+
# All attachments get base64 encoded
|
|
69
|
+
sample_filename = request.get("attachment")
|
|
70
|
+
data = base64.b64decode(data)
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
misperrors['error'] = "No malware sample or attachment supplied"
|
|
74
|
+
return misperrors
|
|
75
|
+
except Exception:
|
|
76
|
+
misperrors['error'] = "Unable to process submited sample data"
|
|
77
|
+
return misperrors
|
|
78
|
+
|
|
79
|
+
if (request["config"].get("mwdb_apikey") is None) or (request["config"].get("mwdb_url") is None):
|
|
80
|
+
misperrors["error"] = "Missing MWDB API key or server URL"
|
|
81
|
+
return misperrors
|
|
82
|
+
|
|
83
|
+
mwdb_misp_attribute = request["config"].get("mwdb_misp_attribute")
|
|
84
|
+
mwdb_public = request["config"].get("mwdb_public", mwdb_public_default)
|
|
85
|
+
|
|
86
|
+
include_tags_event = request["config"].get("include_tags_event")
|
|
87
|
+
include_tags_attribute = request["config"].get("include_tags_attribute")
|
|
88
|
+
misp_event_id = request.get("event_id")
|
|
89
|
+
misp_attribute_uuid = request.get("attribute_uuid")
|
|
90
|
+
misp_attribute_comment = ""
|
|
91
|
+
mwdb_tags = []
|
|
92
|
+
misp_info = ""
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
if include_tags_event:
|
|
96
|
+
sys.path.append(pymisp_keys_file)
|
|
97
|
+
from keys import misp_url, misp_key, misp_verifycert
|
|
98
|
+
misp = PyMISP(misp_url, misp_key, misp_verifycert, False)
|
|
99
|
+
misp_event = misp.get_event(misp_event_id)
|
|
100
|
+
if "Event" in misp_event:
|
|
101
|
+
misp_info = misp_event["Event"]["info"]
|
|
102
|
+
if "Tag" in misp_event["Event"]:
|
|
103
|
+
tags = misp_event["Event"]["Tag"]
|
|
104
|
+
for tag in tags:
|
|
105
|
+
if "misp-galaxy" not in tag["name"]:
|
|
106
|
+
mwdb_tags.append(tag["name"])
|
|
107
|
+
if include_tags_attribute:
|
|
108
|
+
sys.path.append(pymisp_keys_file)
|
|
109
|
+
from keys import misp_url, misp_key, misp_verifycert
|
|
110
|
+
misp = PyMISP(misp_url, misp_key, misp_verifycert, False)
|
|
111
|
+
misp_attribute = misp.get_attribute(misp_attribute_uuid)
|
|
112
|
+
if "Attribute" in misp_attribute:
|
|
113
|
+
if "Tag" in misp_attribute["Attribute"]:
|
|
114
|
+
tags = misp_attribute["Attribute"]["Tag"]
|
|
115
|
+
for tag in tags:
|
|
116
|
+
if "misp-galaxy" not in tag["name"]:
|
|
117
|
+
mwdb_tags.append(tag["name"])
|
|
118
|
+
misp_attribute_comment = misp_attribute["Attribute"]["comment"]
|
|
119
|
+
except Exception:
|
|
120
|
+
misperrors['error'] = "Unable to read PyMISP (keys.py) configuration file"
|
|
121
|
+
return misperrors
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
mwdb = MWDB(api_key=request["config"].get("mwdb_apikey"), api_url=request["config"].get("mwdb_url"))
|
|
125
|
+
if mwdb_misp_attribute and len(mwdb_misp_attribute) > 0:
|
|
126
|
+
metakeys = {mwdb_misp_attribute: misp_event_id}
|
|
127
|
+
else:
|
|
128
|
+
metakeys = False
|
|
129
|
+
file_object = mwdb.upload_file(sample_filename, data, metakeys=metakeys, public=mwdb_public)
|
|
130
|
+
for tag in mwdb_tags:
|
|
131
|
+
file_object.add_tag(tag)
|
|
132
|
+
if len(misp_attribute_comment) < 1:
|
|
133
|
+
misp_attribute_comment = "MISP attribute {}".format(misp_attribute_uuid)
|
|
134
|
+
file_object.add_comment(misp_attribute_comment)
|
|
135
|
+
if len(misp_event) > 0:
|
|
136
|
+
file_object.add_comment("Fetched from event {} - {}".format(misp_event_id, misp_info))
|
|
137
|
+
mwdb_link = request["config"].get("mwdb_url").replace("/api", "/file/") + "{}".format(file_object.md5)
|
|
138
|
+
except Exception:
|
|
139
|
+
misperrors['error'] = "Unable to send sample to MWDB instance"
|
|
140
|
+
return misperrors
|
|
141
|
+
|
|
142
|
+
r = {'results': [{'types': 'link', 'values': mwdb_link, 'comment': 'Link to MWDB sample'}]}
|
|
143
|
+
return r
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def introspection():
|
|
147
|
+
return mispattributes
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def version():
|
|
151
|
+
moduleinfo['config'] = moduleconfig
|
|
152
|
+
return moduleinfo
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import binascii
|
|
3
|
+
import cv2
|
|
4
|
+
import np
|
|
5
|
+
import pytesseract
|
|
6
|
+
|
|
7
|
+
misperrors = {'error': 'Error'}
|
|
8
|
+
mispattributes = {'input': ['attachment'],
|
|
9
|
+
'output': ['freetext']}
|
|
10
|
+
moduleinfo = {
|
|
11
|
+
'version': '0.2',
|
|
12
|
+
'author': 'Sascha Rommelfangen',
|
|
13
|
+
'description': 'Module to process some optical character recognition on pictures.',
|
|
14
|
+
'module-type': ['expansion'],
|
|
15
|
+
'name': 'OCR Enrich',
|
|
16
|
+
'logo': '',
|
|
17
|
+
'requirements': ['cv2: The OpenCV python library.'],
|
|
18
|
+
'features': 'The module takes an attachment attributes as input and process some optical character recognition on it. The text found is then passed to the Freetext importer to extract potential IoCs.',
|
|
19
|
+
'references': [],
|
|
20
|
+
'input': 'A picture attachment.',
|
|
21
|
+
'output': 'Text and freetext fetched from the input picture.',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
moduleconfig = []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def filter_decoded(decoded):
|
|
28
|
+
for line in decoded.split('\n'):
|
|
29
|
+
decoded_line = line.strip('\t\x0b\x0c\r ')
|
|
30
|
+
if decoded_line:
|
|
31
|
+
yield decoded_line
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def handler(q=False):
|
|
35
|
+
if q is False:
|
|
36
|
+
return False
|
|
37
|
+
q = json.loads(q)
|
|
38
|
+
filename = q['attachment']
|
|
39
|
+
try:
|
|
40
|
+
img_array = np.frombuffer(binascii.a2b_base64(q['data']), np.uint8)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(e)
|
|
43
|
+
err = "Couldn't fetch attachment (JSON 'data' is empty). Are you using the 'Query enrichment' action?"
|
|
44
|
+
misperrors['error'] = err
|
|
45
|
+
print(err)
|
|
46
|
+
return misperrors
|
|
47
|
+
|
|
48
|
+
image = img_array
|
|
49
|
+
image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
|
|
50
|
+
try:
|
|
51
|
+
decoded = pytesseract.image_to_string(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
|
52
|
+
return {
|
|
53
|
+
'results': [
|
|
54
|
+
{
|
|
55
|
+
'types': ['freetext'],
|
|
56
|
+
'values': list(filter_decoded(decoded)),
|
|
57
|
+
'comment': f"OCR from file {filename}"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(e)
|
|
63
|
+
err = "Couldn't analyze file type. Only images are supported right now."
|
|
64
|
+
misperrors['error'] = err
|
|
65
|
+
return misperrors
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def introspection():
|
|
69
|
+
return mispattributes
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def version():
|
|
73
|
+
moduleinfo['config'] = moduleconfig
|
|
74
|
+
return moduleinfo
|