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,189 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from . import check_input_attribute, standard_error_message
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
|
7
|
+
from requests.auth import HTTPBasicAuth
|
|
8
|
+
|
|
9
|
+
sys.path.append('./')
|
|
10
|
+
|
|
11
|
+
misperrors = {'error': 'Error'}
|
|
12
|
+
mispattributes = {'input': ['ip-src', 'ip-dst', 'vulnerability', 'md5', 'sha1', 'sha256', 'domain', 'hostname', 'url'],
|
|
13
|
+
'output': ['ip-src', 'ip-dst', 'text', 'domain'],
|
|
14
|
+
'format': 'misp_standard'}
|
|
15
|
+
|
|
16
|
+
# possible module-types: 'expansion', 'hover' or both
|
|
17
|
+
moduleinfo = {
|
|
18
|
+
'version': '2',
|
|
19
|
+
'author': 'Joerg Stephan (@johest)',
|
|
20
|
+
'description': 'An expansion module for IBM X-Force Exchange.',
|
|
21
|
+
'module-type': ['expansion', 'hover'],
|
|
22
|
+
'name': 'IBM X-Force Exchange Lookup',
|
|
23
|
+
'logo': 'xforce.png',
|
|
24
|
+
'requirements': ['An access to the X-Force API (apikey)'],
|
|
25
|
+
'features': 'This module takes a MISP attribute as input to query the X-Force API. The API returns then additional information known in their threats data, that is mapped into MISP attributes.',
|
|
26
|
+
'references': ['https://exchange.xforce.ibmcloud.com/'],
|
|
27
|
+
'input': 'A MISP attribute included in the following list:\n- ip-src\n- ip-dst\n- vulnerability\n- md5\n- sha1\n- sha256',
|
|
28
|
+
'output': 'MISP attributes mapped from the result of the query on X-Force Exchange.',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# config fields that your code expects from the site admin
|
|
32
|
+
moduleconfig = ["apikey", "apipassword"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class XforceExchange():
|
|
36
|
+
def __init__(self, attribute, apikey, apipassword):
|
|
37
|
+
self.base_url = "https://api.xforce.ibmcloud.com"
|
|
38
|
+
self.misp_event = MISPEvent()
|
|
39
|
+
self.attribute = MISPAttribute()
|
|
40
|
+
self.attribute.from_dict(**attribute)
|
|
41
|
+
self._apikey = apikey
|
|
42
|
+
self._apipassword = apipassword
|
|
43
|
+
self.result = {}
|
|
44
|
+
self.objects = defaultdict(dict)
|
|
45
|
+
self.status_mapping = {403: "Access denied, please check if your authentication is valid and if you did not reach the limit of queries.",
|
|
46
|
+
404: "No result found for your query."}
|
|
47
|
+
|
|
48
|
+
def parse(self):
|
|
49
|
+
mapping = {'url': '_parse_url', 'vulnerability': '_parse_vulnerability'}
|
|
50
|
+
mapping.update(dict.fromkeys(('md5', 'sha1', 'sha256'), '_parse_hash'))
|
|
51
|
+
mapping.update(dict.fromkeys(('domain', 'hostname'), '_parse_dns'))
|
|
52
|
+
mapping.update(dict.fromkeys(('ip-src', 'ip-dst'), '_parse_ip'))
|
|
53
|
+
to_call = mapping[self.attribute.type]
|
|
54
|
+
getattr(self, to_call)(self.attribute.value)
|
|
55
|
+
|
|
56
|
+
def get_result(self):
|
|
57
|
+
if not self.misp_event.objects:
|
|
58
|
+
if 'error' not in self.result:
|
|
59
|
+
self.result['error'] = "No additional data found on Xforce Exchange."
|
|
60
|
+
return self.result
|
|
61
|
+
self.misp_event.add_attribute(**self.attribute)
|
|
62
|
+
event = json.loads(self.misp_event.to_json())
|
|
63
|
+
result = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
|
|
64
|
+
return {'results': result}
|
|
65
|
+
|
|
66
|
+
def _api_call(self, url):
|
|
67
|
+
try:
|
|
68
|
+
result = requests.get(url, auth=HTTPBasicAuth(self._apikey, self._apipassword))
|
|
69
|
+
except Exception as e:
|
|
70
|
+
self.result['error'] = e
|
|
71
|
+
return
|
|
72
|
+
status_code = result.status_code
|
|
73
|
+
if status_code != 200:
|
|
74
|
+
try:
|
|
75
|
+
self.result['error'] = self.status_mapping[status_code]
|
|
76
|
+
except KeyError:
|
|
77
|
+
self.result['error'] = 'An error with the API has occurred.'
|
|
78
|
+
return
|
|
79
|
+
return result.json()
|
|
80
|
+
|
|
81
|
+
def _create_file(self, malware, relationship):
|
|
82
|
+
file_object = MISPObject('file')
|
|
83
|
+
for key, relation in zip(('filepath', 'md5'), ('filename', 'md5')):
|
|
84
|
+
file_object.add_attribute(relation, malware[key])
|
|
85
|
+
file_object.add_reference(self.attribute.uuid, relationship)
|
|
86
|
+
return file_object
|
|
87
|
+
|
|
88
|
+
def _create_url(self, malware):
|
|
89
|
+
url_object = MISPObject('url')
|
|
90
|
+
for key, relation in zip(('uri', 'domain'), ('url', 'domain')):
|
|
91
|
+
url_object.add_attribute(relation, malware[key])
|
|
92
|
+
attributes = tuple(f'{attribute.object_relation}_{attribute.value}' for attribute in url_object.attributes)
|
|
93
|
+
if attributes in self.objects['url']:
|
|
94
|
+
del url_object
|
|
95
|
+
return self.objects['url'][attributes]
|
|
96
|
+
url_uuid = url_object.uuid
|
|
97
|
+
self.misp_event.add_object(**url_object)
|
|
98
|
+
self.objects['url'][attributes] = url_uuid
|
|
99
|
+
return url_uuid
|
|
100
|
+
|
|
101
|
+
def _fetch_types(self, value):
|
|
102
|
+
if self.attribute.type in ('ip-src', 'ip-dst'):
|
|
103
|
+
return 'ip', 'domain', self.attribute.value
|
|
104
|
+
return 'domain', 'ip', value
|
|
105
|
+
|
|
106
|
+
def _handle_file(self, malware, relationship):
|
|
107
|
+
file_object = self._create_file(malware, relationship)
|
|
108
|
+
attributes = tuple(f'{attribute.object_relation}_{attribute.value}' for attribute in file_object.attributes)
|
|
109
|
+
if attributes in self.objects['file']:
|
|
110
|
+
self.objects['file'][attributes].add_reference(self._create_url(malware), 'dropped-by')
|
|
111
|
+
del file_object
|
|
112
|
+
return
|
|
113
|
+
file_object.add_reference(self._create_url(malware), 'dropped-by')
|
|
114
|
+
self.objects['file'][attributes] = file_object
|
|
115
|
+
self.misp_event.add_object(**file_object)
|
|
116
|
+
|
|
117
|
+
def _parse_dns(self, value):
|
|
118
|
+
dns_result = self._api_call(f'{self.base_url}/resolve/{value}')
|
|
119
|
+
if dns_result.get('Passive') and dns_result['Passive'].get('records'):
|
|
120
|
+
itype, ftype, value = self._fetch_types(dns_result['Passive']['query'])
|
|
121
|
+
misp_object = MISPObject('domain-ip')
|
|
122
|
+
misp_object.add_attribute(itype, value)
|
|
123
|
+
for record in dns_result['Passive']['records']:
|
|
124
|
+
misp_object.add_attribute(ftype, record['value'])
|
|
125
|
+
misp_object.add_reference(self.attribute.uuid, 'related-to')
|
|
126
|
+
self.misp_event.add_object(**misp_object)
|
|
127
|
+
|
|
128
|
+
def _parse_hash(self, value):
|
|
129
|
+
malware_result = self._api_call(f'{self.base_url}/malware/{value}')
|
|
130
|
+
if malware_result and malware_result.get('malware'):
|
|
131
|
+
malware_report = malware_result['malware']
|
|
132
|
+
for malware in malware_report.get('origins', {}).get('CnCServers', {}).get('rows', []):
|
|
133
|
+
self._handle_file(malware, 'related-to')
|
|
134
|
+
|
|
135
|
+
def _parse_ip(self, value):
|
|
136
|
+
self._parse_dns(value)
|
|
137
|
+
self._parse_malware(value, 'ipr')
|
|
138
|
+
|
|
139
|
+
def _parse_malware(self, value, feature):
|
|
140
|
+
malware_result = self._api_call(f'{self.base_url}/{feature}/malware/{value}')
|
|
141
|
+
if malware_result and malware_result.get('malware'):
|
|
142
|
+
for malware in malware_result['malware']:
|
|
143
|
+
self._handle_file(malware, 'associated-with')
|
|
144
|
+
|
|
145
|
+
def _parse_url(self, value):
|
|
146
|
+
self._parse_dns(value)
|
|
147
|
+
self._parse_malware(value, 'url')
|
|
148
|
+
|
|
149
|
+
def _parse_vulnerability(self, value):
|
|
150
|
+
vulnerability_result = self._api_call(f'{self.base_url}/vulnerabilities/search/{value}')
|
|
151
|
+
if vulnerability_result:
|
|
152
|
+
for vulnerability in vulnerability_result:
|
|
153
|
+
misp_object = MISPObject('vulnerability')
|
|
154
|
+
for code in vulnerability['stdcode']:
|
|
155
|
+
misp_object.add_attribute('id', code)
|
|
156
|
+
for feature, relation in zip(('title', 'description', 'temporal_score'),
|
|
157
|
+
('summary', 'description', 'cvss-score')):
|
|
158
|
+
misp_object.add_attribute(relation, vulnerability[feature])
|
|
159
|
+
for reference in vulnerability['references']:
|
|
160
|
+
misp_object.add_attribute('references', reference['link_target'])
|
|
161
|
+
misp_object.add_reference(self.attribute.uuid, 'related-to')
|
|
162
|
+
self.misp_event.add_object(**misp_object)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def handler(q=False):
|
|
166
|
+
if q is False:
|
|
167
|
+
return False
|
|
168
|
+
request = json.loads(q)
|
|
169
|
+
if not request.get('config') or not (request['config'].get('apikey') and request['config'].get('apipassword')):
|
|
170
|
+
misperrors['error'] = 'An API authentication is required (key and password).'
|
|
171
|
+
return misperrors
|
|
172
|
+
key = request["config"]["apikey"]
|
|
173
|
+
password = request['config']['apipassword']
|
|
174
|
+
if not request.get('attribute') or not check_input_attribute(request['attribute']):
|
|
175
|
+
return {'error': f'{standard_error_message} which should contain at least a type, a value and an uuid.'}
|
|
176
|
+
if request['attribute']['type'] not in mispattributes['input']:
|
|
177
|
+
return {'error': 'Unsupported attribute type.'}
|
|
178
|
+
parser = XforceExchange(request['attribute'], key, password)
|
|
179
|
+
parser.parse()
|
|
180
|
+
return parser.get_result()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def introspection():
|
|
184
|
+
return mispattributes
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def version():
|
|
188
|
+
moduleinfo['config'] = moduleconfig
|
|
189
|
+
return moduleinfo
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import binascii
|
|
3
|
+
import np
|
|
4
|
+
import pandas
|
|
5
|
+
import io
|
|
6
|
+
|
|
7
|
+
misperrors = {'error': 'Error'}
|
|
8
|
+
mispattributes = {'input': ['attachment'],
|
|
9
|
+
'output': ['freetext', 'text']}
|
|
10
|
+
moduleinfo = {
|
|
11
|
+
'version': '0.1',
|
|
12
|
+
'author': 'Sascha Rommelfangen',
|
|
13
|
+
'description': 'Module to extract freetext from a .xlsx document.',
|
|
14
|
+
'module-type': ['expansion'],
|
|
15
|
+
'name': 'XLXS Enrich',
|
|
16
|
+
'logo': 'xlsx.png',
|
|
17
|
+
'requirements': ['pandas: Python library to perform data analysis, time series and statistics.'],
|
|
18
|
+
'features': 'The module reads the text contained in a .xlsx document. The result is passed to the freetext import parser so IoCs can be extracted out of it.',
|
|
19
|
+
'references': [],
|
|
20
|
+
'input': 'Attachment attribute containing a .xlsx document.',
|
|
21
|
+
'output': 'Text and freetext parsed from the document.',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
moduleconfig = []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def handler(q=False):
|
|
28
|
+
if q is False:
|
|
29
|
+
return False
|
|
30
|
+
q = json.loads(q)
|
|
31
|
+
filename = q['attachment']
|
|
32
|
+
try:
|
|
33
|
+
xlsx_array = np.frombuffer(binascii.a2b_base64(q['data']), np.uint8)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(e)
|
|
36
|
+
err = "Couldn't fetch attachment (JSON 'data' is empty). Are you using the 'Query enrichment' action?"
|
|
37
|
+
misperrors['error'] = err
|
|
38
|
+
print(err)
|
|
39
|
+
return misperrors
|
|
40
|
+
|
|
41
|
+
xls_content = ""
|
|
42
|
+
xls_file = io.BytesIO(xlsx_array)
|
|
43
|
+
pandas.set_option('display.max_colwidth', -1)
|
|
44
|
+
try:
|
|
45
|
+
xls = pandas.read_excel(xls_file)
|
|
46
|
+
xls_content = xls.to_string(max_rows=None)
|
|
47
|
+
print(xls_content)
|
|
48
|
+
return {'results': [{'types': ['freetext'], 'values': xls_content, 'comment': ".xlsx-to-text from file " + filename},
|
|
49
|
+
{'types': ['text'], 'values': xls_content, 'comment': ".xlsx-to-text from file " + filename}]}
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print(e)
|
|
52
|
+
err = "Couldn't analyze file as .xlsx. Error was: " + str(e)
|
|
53
|
+
misperrors['error'] = err
|
|
54
|
+
return misperrors
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def introspection():
|
|
58
|
+
return mispattributes
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def version():
|
|
62
|
+
moduleinfo['config'] = moduleconfig
|
|
63
|
+
return moduleinfo
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
try:
|
|
4
|
+
import yara
|
|
5
|
+
except (OSError, ImportError):
|
|
6
|
+
print("yara is missing, use 'pip3 install -I -r REQUIREMENTS' from the root of this repository to install it.")
|
|
7
|
+
|
|
8
|
+
misperrors = {'error': 'Error'}
|
|
9
|
+
moduleinfo = {
|
|
10
|
+
'version': '1',
|
|
11
|
+
'author': 'Christian STUDER',
|
|
12
|
+
'description': 'jj',
|
|
13
|
+
'module-type': ['expansion', 'hover'],
|
|
14
|
+
'name': 'YARA Rule Generator',
|
|
15
|
+
'require_standard_format': True,
|
|
16
|
+
'logo': 'yara.png',
|
|
17
|
+
'requirements': ['yara-python python library'],
|
|
18
|
+
'features': "The module takes a hash attribute (md5, sha1, sha256, imphash) as input, and is returning a YARA rule from it. This YARA rule is also validated using the same method as in 'yara_syntax_validator' module.\nBoth hover and expansion functionalities are supported with this module, where the hover part is displaying the resulting YARA rule and the expansion part allows you to add the rule as a new attribute, as usual with expansion modules.",
|
|
19
|
+
'references': ['https://virustotal.github.io/yara/', 'https://github.com/virustotal/yara-python'],
|
|
20
|
+
'input': 'MISP Hash attribute (md5, sha1, sha256, imphash, or any of the composite attribute with filename and one of the previous hash type).',
|
|
21
|
+
'output': 'YARA rule.',
|
|
22
|
+
}
|
|
23
|
+
moduleconfig = []
|
|
24
|
+
mispattributes = {'input': ['md5', 'sha1', 'sha256', 'filename|md5', 'filename|sha1', 'filename|sha256', 'imphash'], 'output': ['yara']}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def extract_input_attribute(request):
|
|
28
|
+
for input_type in mispattributes['input']:
|
|
29
|
+
if input_type in request:
|
|
30
|
+
return input_type, request[input_type]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_hash_condition(hashtype, hashvalue):
|
|
34
|
+
hashvalue = hashvalue.lower()
|
|
35
|
+
required_module, params = ('pe', '()') if hashtype == 'imphash' else ('hash', '(0, filesize)')
|
|
36
|
+
return '{}.{}{} == "{}"'.format(required_module, hashtype, params, hashvalue), required_module
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def handler(q=False):
|
|
40
|
+
if q is False:
|
|
41
|
+
return False
|
|
42
|
+
request = json.loads(q)
|
|
43
|
+
attribute = extract_input_attribute(request)
|
|
44
|
+
if attribute is None:
|
|
45
|
+
return {'error': f'Wrong input type, please choose in the following: {", ".join(mispattributes["input"])}'}
|
|
46
|
+
uuid = request.pop('attribute_uuid') if 'attribute_uuid' in request else None
|
|
47
|
+
attribute_type, value = attribute
|
|
48
|
+
if 'filename' in attribute_type:
|
|
49
|
+
_, attribute_type = attribute_type.split('|')
|
|
50
|
+
_, value = value.split('|')
|
|
51
|
+
condition, required_module = get_hash_condition(attribute_type, value)
|
|
52
|
+
import_section = 'import "{}"'.format(required_module)
|
|
53
|
+
rule_start = '%s\r\nrule %s_%s {' % (import_section, attribute_type.upper(), re.sub(r'\W+', '_', uuid)) if uuid else '%s\r\nrule %s {' % (import_section, attribute_type.upper())
|
|
54
|
+
condition = '\tcondition:\r\n\t\t{}'.format(condition)
|
|
55
|
+
rule = '\r\n'.join([rule_start, condition, '}'])
|
|
56
|
+
try:
|
|
57
|
+
yara.compile(source=rule)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
misperrors['error'] = 'Syntax error: {}'.format(e)
|
|
60
|
+
return misperrors
|
|
61
|
+
return {'results': [{'types': mispattributes['output'], 'values': rule}]}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def introspection():
|
|
65
|
+
return mispattributes
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def version():
|
|
69
|
+
moduleinfo['config'] = moduleconfig
|
|
70
|
+
return moduleinfo
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
try:
|
|
3
|
+
import yara
|
|
4
|
+
except (OSError, ImportError):
|
|
5
|
+
print("yara is missing, use 'pip3 install -I -r REQUIREMENTS' from the root of this repository to install it.")
|
|
6
|
+
|
|
7
|
+
misperrors = {'error': 'Error'}
|
|
8
|
+
mispattributes = {'input': ['yara'], 'output': ['text']}
|
|
9
|
+
moduleinfo = {
|
|
10
|
+
'version': '0.1',
|
|
11
|
+
'author': 'Dennis Rand',
|
|
12
|
+
'description': 'An expansion hover module to perform a syntax check on if yara rules are valid or not.',
|
|
13
|
+
'module-type': ['hover'],
|
|
14
|
+
'name': 'YARA Syntax Validator',
|
|
15
|
+
'logo': 'yara.png',
|
|
16
|
+
'requirements': ['yara_python python library'],
|
|
17
|
+
'features': 'This modules simply takes a YARA rule as input, and checks its syntax. It returns then a confirmation if the syntax is valid, otherwise the syntax error is displayed.',
|
|
18
|
+
'references': ['http://virustotal.github.io/yara/'],
|
|
19
|
+
'input': 'YARA rule attribute.',
|
|
20
|
+
'output': 'Text to inform users if their rule is valid.',
|
|
21
|
+
}
|
|
22
|
+
moduleconfig = []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def handler(q=False):
|
|
26
|
+
if q is False:
|
|
27
|
+
return False
|
|
28
|
+
request = json.loads(q)
|
|
29
|
+
if not request.get('yara'):
|
|
30
|
+
misperrors['error'] = 'Yara rule missing'
|
|
31
|
+
return misperrors
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
yara.compile(source=request.get('yara'))
|
|
35
|
+
summary = ("Syntax valid")
|
|
36
|
+
except Exception as e:
|
|
37
|
+
summary = ("Syntax error: " + str(e))
|
|
38
|
+
|
|
39
|
+
r = {'results': [{'types': mispattributes['output'], 'values': summary}]}
|
|
40
|
+
return r
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def introspection():
|
|
44
|
+
return mispattributes
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def version():
|
|
48
|
+
moduleinfo['config'] = moduleconfig
|
|
49
|
+
return moduleinfo
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import pyeti
|
|
6
|
+
except ImportError:
|
|
7
|
+
print("pyeti module not installed.")
|
|
8
|
+
|
|
9
|
+
from pymisp import MISPEvent, MISPObject
|
|
10
|
+
|
|
11
|
+
misperrors = {'error': 'Error'}
|
|
12
|
+
|
|
13
|
+
mispattributes = {'input': ['AS', 'ip-src', 'ip-dst', 'hostname', 'domain', 'sha256', 'sha1', 'md5', 'url'],
|
|
14
|
+
'format': 'misp_standard'
|
|
15
|
+
}
|
|
16
|
+
# possible module-types: 'expansion', 'hover' or both
|
|
17
|
+
moduleinfo = {
|
|
18
|
+
'version': '1',
|
|
19
|
+
'author': 'Sebastien Larinier @sebdraven',
|
|
20
|
+
'description': 'Module to process a query on Yeti.',
|
|
21
|
+
'module-type': ['expansion', 'hover'],
|
|
22
|
+
'name': 'Yeti Lookup',
|
|
23
|
+
'logo': 'yeti.png',
|
|
24
|
+
'requirements': ['pyeti', 'API key '],
|
|
25
|
+
'features': 'This module add context and links between observables using yeti',
|
|
26
|
+
'references': ['https://github.com/yeti-platform/yeti', 'https://github.com/sebdraven/pyeti'],
|
|
27
|
+
'input': 'A domain, hostname,IP, sha256,sha1, md5, url of MISP attribute.',
|
|
28
|
+
'output': 'MISP attributes and objects fetched from the Yeti instances.',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
moduleconfig = ['apikey', 'url']
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Yeti():
|
|
35
|
+
|
|
36
|
+
def __init__(self, url, key, attribute):
|
|
37
|
+
self.misp_mapping = {'Ip': 'ip-dst', 'Domain': 'domain', 'Hostname': 'hostname', 'Url': 'url',
|
|
38
|
+
'AutonomousSystem': 'AS', 'File': 'sha256'}
|
|
39
|
+
self.yeti_client = pyeti.YetiApi(url=url, api_key=key)
|
|
40
|
+
self.attribute = attribute
|
|
41
|
+
self.misp_event = MISPEvent()
|
|
42
|
+
self.misp_event.add_attribute(**attribute)
|
|
43
|
+
|
|
44
|
+
def search(self, value):
|
|
45
|
+
obs = self.yeti_client.observable_search(value=value)
|
|
46
|
+
if obs:
|
|
47
|
+
return obs[0]
|
|
48
|
+
|
|
49
|
+
def get_neighboors(self, obs_id):
|
|
50
|
+
neighboors = self.yeti_client.neighbors_observables(obs_id)
|
|
51
|
+
if neighboors and 'objs' in neighboors:
|
|
52
|
+
links_by_id = {link['dst']['id']: (link['description'], 'dst') for link in neighboors['links']
|
|
53
|
+
if link['dst']['id'] != obs_id}
|
|
54
|
+
links_by_id.update({link['src']['id']: (link['description'], 'src') for link in neighboors['links']
|
|
55
|
+
if link['src']['id'] != obs_id})
|
|
56
|
+
|
|
57
|
+
for n in neighboors['objs']:
|
|
58
|
+
yield n, links_by_id[n['id']]
|
|
59
|
+
|
|
60
|
+
def parse_yeti_result(self):
|
|
61
|
+
obs = self.search(self.attribute['value'])
|
|
62
|
+
|
|
63
|
+
for obs_to_add, link in self.get_neighboors(obs['id']):
|
|
64
|
+
object_misp_domain_ip = self.__get_object_domain_ip(obs_to_add)
|
|
65
|
+
if object_misp_domain_ip:
|
|
66
|
+
self.misp_event.add_object(object_misp_domain_ip)
|
|
67
|
+
continue
|
|
68
|
+
object_misp_url = self.__get_object_url(obs_to_add)
|
|
69
|
+
if object_misp_url:
|
|
70
|
+
self.misp_event.add_object(object_misp_url)
|
|
71
|
+
continue
|
|
72
|
+
if link[0] == 'NS record':
|
|
73
|
+
object_ns_record = self.__get_object_ns_record(obs_to_add, link[1])
|
|
74
|
+
if object_ns_record:
|
|
75
|
+
self.misp_event.add_object(object_ns_record)
|
|
76
|
+
continue
|
|
77
|
+
self.__get_attribute(obs_to_add, link[0])
|
|
78
|
+
|
|
79
|
+
def get_result(self):
|
|
80
|
+
event = json.loads(self.misp_event.to_json())
|
|
81
|
+
results = {key: event[key] for key in ('Attribute', 'Object') if key in event}
|
|
82
|
+
return results
|
|
83
|
+
|
|
84
|
+
def __get_attribute(self, obs_to_add, link):
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
type_attr = self.misp_mapping[obs_to_add['type']]
|
|
88
|
+
value = None
|
|
89
|
+
if obs_to_add['type'] == 'File':
|
|
90
|
+
value = obs_to_add['value'].split(':')[1]
|
|
91
|
+
else:
|
|
92
|
+
value = obs_to_add['value']
|
|
93
|
+
attr = self.misp_event.add_attribute(value=value, type=type_attr)
|
|
94
|
+
attr.comment = '%s: %s' % (link, self.attribute['value'])
|
|
95
|
+
except KeyError:
|
|
96
|
+
logging.error('type not found %s' % obs_to_add['type'])
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
for t in obs_to_add['tags']:
|
|
100
|
+
self.misp_event.add_attribute_tag(t['name'], attr['uuid'])
|
|
101
|
+
|
|
102
|
+
def __get_object_domain_ip(self, obj_to_add):
|
|
103
|
+
if (obj_to_add['type'] == 'Ip' and self.attribute['type'] in ['hostname', 'domain']) or \
|
|
104
|
+
(obj_to_add['type'] in ('Hostname', 'Domain') and self.attribute['type'] in ('ip-src', 'ip-dst')):
|
|
105
|
+
domain_ip_object = MISPObject('domain-ip')
|
|
106
|
+
domain_ip_object.add_attribute(self.__get_relation(obj_to_add),
|
|
107
|
+
obj_to_add['value'])
|
|
108
|
+
domain_ip_object.add_attribute(self.__get_relation(self.attribute, is_yeti_object=False),
|
|
109
|
+
self.attribute['value'])
|
|
110
|
+
domain_ip_object.add_reference(self.attribute['uuid'], 'related_to')
|
|
111
|
+
|
|
112
|
+
return domain_ip_object
|
|
113
|
+
|
|
114
|
+
def __get_object_url(self, obj_to_add):
|
|
115
|
+
if (obj_to_add['type'] == 'Url' and self.attribute['type'] in ['hostname', 'domain', 'ip-src', 'ip-dst']) or (
|
|
116
|
+
obj_to_add['type'] in ('Hostname', 'Domain', 'Ip') and self.attribute['type'] == 'url'
|
|
117
|
+
):
|
|
118
|
+
url_object = MISPObject('url')
|
|
119
|
+
obj_relation = self.__get_relation(obj_to_add)
|
|
120
|
+
if obj_relation:
|
|
121
|
+
url_object.add_attribute(obj_relation, obj_to_add['value'])
|
|
122
|
+
obj_relation = self.__get_relation(self.attribute, is_yeti_object=False)
|
|
123
|
+
if obj_relation:
|
|
124
|
+
url_object.add_attribute(obj_relation,
|
|
125
|
+
self.attribute['value'])
|
|
126
|
+
url_object.add_reference(self.attribute['uuid'], 'related_to')
|
|
127
|
+
|
|
128
|
+
return url_object
|
|
129
|
+
|
|
130
|
+
def __get_object_ns_record(self, obj_to_add, link):
|
|
131
|
+
queried_domain = None
|
|
132
|
+
ns_domain = None
|
|
133
|
+
object_dns_record = MISPObject('dns-record')
|
|
134
|
+
if link == 'dst':
|
|
135
|
+
queried_domain = self.attribute['value']
|
|
136
|
+
ns_domain = obj_to_add['value']
|
|
137
|
+
elif link == 'src':
|
|
138
|
+
queried_domain = obj_to_add['value']
|
|
139
|
+
ns_domain = self.attribute['value']
|
|
140
|
+
if queried_domain and ns_domain:
|
|
141
|
+
object_dns_record.add_attribute('queried-domain', queried_domain)
|
|
142
|
+
object_dns_record.add_attribute('ns-record', ns_domain)
|
|
143
|
+
object_dns_record.add_reference(self.attribute['uuid'], 'related_to')
|
|
144
|
+
|
|
145
|
+
return object_dns_record
|
|
146
|
+
|
|
147
|
+
def __get_relation(self, obj, is_yeti_object=True):
|
|
148
|
+
if is_yeti_object:
|
|
149
|
+
type_attribute = self.misp_mapping[obj['type']]
|
|
150
|
+
else:
|
|
151
|
+
type_attribute = obj['type']
|
|
152
|
+
if type_attribute == 'ip-src' or type_attribute == 'ip-dst':
|
|
153
|
+
return 'ip'
|
|
154
|
+
elif 'domain' == type_attribute:
|
|
155
|
+
return 'domain'
|
|
156
|
+
elif 'hostname' == type_attribute:
|
|
157
|
+
return 'domain'
|
|
158
|
+
elif type_attribute == 'url':
|
|
159
|
+
return type_attribute
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def handler(q=False):
|
|
163
|
+
if q is False:
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
apikey = None
|
|
167
|
+
yeti_url = None
|
|
168
|
+
yeti_client = None
|
|
169
|
+
|
|
170
|
+
request = json.loads(q)
|
|
171
|
+
attribute = request['attribute']
|
|
172
|
+
if attribute['type'] not in mispattributes['input']:
|
|
173
|
+
return {'error': 'Unsupported attributes type'}
|
|
174
|
+
|
|
175
|
+
if 'config' in request and 'url' in request['config']:
|
|
176
|
+
yeti_url = request['config']['url']
|
|
177
|
+
if 'config' in request and 'apikey' in request['config']:
|
|
178
|
+
apikey = request['config']['apikey']
|
|
179
|
+
if apikey and yeti_url:
|
|
180
|
+
yeti_client = Yeti(yeti_url, apikey, attribute)
|
|
181
|
+
|
|
182
|
+
if yeti_client:
|
|
183
|
+
yeti_client.parse_yeti_result()
|
|
184
|
+
return {'results': yeti_client.get_result()}
|
|
185
|
+
else:
|
|
186
|
+
misperrors['error'] = 'Yeti Config Error'
|
|
187
|
+
return misperrors
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def version():
|
|
191
|
+
moduleinfo['config'] = moduleconfig
|
|
192
|
+
return moduleinfo
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def introspection():
|
|
196
|
+
return mispattributes
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
__all__ = ['cef_export', 'mass_eql_export', 'liteexport', 'goamlexport', 'threat_connect_export', 'pdfexport',
|
|
2
|
+
'threatStream_misp_export', 'osqueryexport', 'nexthinkexport', 'vt_graph', 'defender_endpoint_export',
|
|
3
|
+
'virustotal_collections', 'yara_export', 'cisco_firesight_manager_ACL_rule_export']
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import base64
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
misperrors = {'error': 'Error'}
|
|
6
|
+
|
|
7
|
+
# possible module-types: 'expansion', 'hover' or both
|
|
8
|
+
moduleinfo = {
|
|
9
|
+
'version': '1',
|
|
10
|
+
'author': 'Hannah Ward',
|
|
11
|
+
'description': 'Module to export a MISP event in CEF format.',
|
|
12
|
+
'module-type': ['export'],
|
|
13
|
+
'name': 'CEF Export',
|
|
14
|
+
'logo': '',
|
|
15
|
+
'requirements': [],
|
|
16
|
+
'features': 'The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in Common Event Format.\nThus, there is no particular feature concerning MISP Events since any event can be exported. However, 4 configuration parameters recognized by CEF format are required and should be provided by users before exporting data: the device vendor, product and version, as well as the default severity of data.',
|
|
17
|
+
'references': ['https://community.softwaregrp.com/t5/ArcSight-Connectors/ArcSight-Common-Event-Format-CEF-Guide/ta-p/1589306?attachment-id=65537'],
|
|
18
|
+
'input': 'MISP Event attributes',
|
|
19
|
+
'output': 'Common Event Format file',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# config fields that your code expects from the site admin
|
|
23
|
+
moduleconfig = ["Default_Severity", "Device_Vendor", "Device_Product", "Device_Version"]
|
|
24
|
+
|
|
25
|
+
cefmapping = {"ip-src": "src", "ip-dst": "dst", "hostname": "dhost", "domain": "dhost",
|
|
26
|
+
"md5": "fileHash", "sha1": "fileHash", "sha256": "fileHash",
|
|
27
|
+
"url": "request"}
|
|
28
|
+
|
|
29
|
+
mispattributes = {'input': list(cefmapping.keys())}
|
|
30
|
+
outputFileExtension = "cef"
|
|
31
|
+
responseType = "application/txt"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def handler(q=False):
|
|
35
|
+
if q is False:
|
|
36
|
+
return False
|
|
37
|
+
request = json.loads(q)
|
|
38
|
+
if "config" in request:
|
|
39
|
+
config = request["config"]
|
|
40
|
+
else:
|
|
41
|
+
config = {"Default_Severity": 1, "Device_Vendor": "MISP",
|
|
42
|
+
"Device_Product": "MISP", "Device_Version": 1}
|
|
43
|
+
|
|
44
|
+
data = request["data"]
|
|
45
|
+
response = ""
|
|
46
|
+
for ev in data:
|
|
47
|
+
event = ev["Attribute"]
|
|
48
|
+
for attr in event:
|
|
49
|
+
if attr["type"] in cefmapping:
|
|
50
|
+
response += "{} host CEF:0|{}|{}|{}|{}|{}|{}|{}={}\n".format(
|
|
51
|
+
datetime.datetime.fromtimestamp(int(attr["timestamp"])).strftime("%b %d %H:%M:%S"),
|
|
52
|
+
config["Device_Vendor"],
|
|
53
|
+
config["Device_Product"],
|
|
54
|
+
config["Device_Version"],
|
|
55
|
+
attr["category"],
|
|
56
|
+
attr["category"],
|
|
57
|
+
config["Default_Severity"],
|
|
58
|
+
cefmapping[attr["type"]],
|
|
59
|
+
attr["value"],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
r = {"response": [], "data": str(base64.b64encode(bytes(response, 'utf-8')), 'utf-8')}
|
|
63
|
+
return r
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def introspection():
|
|
67
|
+
modulesetup = {}
|
|
68
|
+
try:
|
|
69
|
+
responseType
|
|
70
|
+
modulesetup['responseType'] = responseType
|
|
71
|
+
except NameError:
|
|
72
|
+
pass
|
|
73
|
+
try:
|
|
74
|
+
userConfig
|
|
75
|
+
modulesetup['userConfig'] = userConfig
|
|
76
|
+
except NameError:
|
|
77
|
+
pass
|
|
78
|
+
try:
|
|
79
|
+
outputFileExtension
|
|
80
|
+
modulesetup['outputFileExtension'] = outputFileExtension
|
|
81
|
+
except NameError:
|
|
82
|
+
pass
|
|
83
|
+
try:
|
|
84
|
+
inputSource
|
|
85
|
+
modulesetup['inputSource'] = inputSource
|
|
86
|
+
except NameError:
|
|
87
|
+
pass
|
|
88
|
+
return modulesetup
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def version():
|
|
92
|
+
moduleinfo['config'] = moduleconfig
|
|
93
|
+
return moduleinfo
|