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,125 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Export module for converting MISP events into ThreatConnect Structured Import files. This export data is meant to be used with the "Structured Import" ability of ThreatConnect.
|
|
3
|
+
|
|
4
|
+
Source: http://kb.threatconnect.com/customer/en/portal/articles/1912599-using-structured-import/
|
|
5
|
+
Source: http://kb.threatconnect.com/customer/en/portal/articles/2092925-the-threatconnect-data-model/
|
|
6
|
+
"""
|
|
7
|
+
import base64
|
|
8
|
+
import csv
|
|
9
|
+
import io
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
misperrors = {"error": "Error"}
|
|
14
|
+
|
|
15
|
+
moduleinfo = {
|
|
16
|
+
'version': '0.1',
|
|
17
|
+
'author': 'CenturyLink CIRT',
|
|
18
|
+
'description': 'Module to export a structured CSV file for uploading to ThreatConnect.',
|
|
19
|
+
'module-type': ['export'],
|
|
20
|
+
'name': 'ThreadConnect Export',
|
|
21
|
+
'logo': 'threatconnect.png',
|
|
22
|
+
'requirements': ['csv'],
|
|
23
|
+
'features': 'The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in a CSV format recognized by ThreatConnect.\nUsers should then provide, as module configuration, the source of data they export, because it is required by the output format.',
|
|
24
|
+
'references': ['https://www.threatconnect.com'],
|
|
25
|
+
'input': 'MISP Event attributes',
|
|
26
|
+
'output': 'ThreatConnect CSV format file',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# config fields expected from the MISP administrator
|
|
30
|
+
# Default_Source: The source of the data. Typically this won't be changed from the default
|
|
31
|
+
moduleconfig = ["Default_Source"]
|
|
32
|
+
|
|
33
|
+
# Map of MISP fields => ThreatConnect fields
|
|
34
|
+
fieldmap = {
|
|
35
|
+
"domain": "Host",
|
|
36
|
+
"domain|ip": "Host|Address",
|
|
37
|
+
"hostname": "Host",
|
|
38
|
+
"ip-src": "Address",
|
|
39
|
+
"ip-dst": "Address",
|
|
40
|
+
"ip-src|port": "Address",
|
|
41
|
+
"ip-dst|port": "Address",
|
|
42
|
+
"whois-registrant-email": "EmailAddress",
|
|
43
|
+
"email-src": "EmailAddress",
|
|
44
|
+
"email-dst": "EmailAddress",
|
|
45
|
+
"url": "URL",
|
|
46
|
+
"md5": "File",
|
|
47
|
+
"filename|md5": "File"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# combine all the MISP fields from fieldmap into one big list
|
|
51
|
+
mispattributes = {
|
|
52
|
+
"input": list(fieldmap.keys())
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def handler(q=False):
|
|
57
|
+
"""
|
|
58
|
+
Convert a MISP query into a CSV file matching the ThreatConnect Structured Import file format.
|
|
59
|
+
Input
|
|
60
|
+
q: Query dictionary
|
|
61
|
+
"""
|
|
62
|
+
if q is False or not q:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
# Check if we were given a configuration
|
|
66
|
+
request = json.loads(q)
|
|
67
|
+
config = request.get("config", {"Default_Source": ""})
|
|
68
|
+
logging.info("Setting config to: %s", config)
|
|
69
|
+
|
|
70
|
+
response = io.StringIO()
|
|
71
|
+
writer = csv.DictWriter(response, fieldnames=["Type", "Value", "Source", "Description"])
|
|
72
|
+
writer.writeheader()
|
|
73
|
+
|
|
74
|
+
# start parsing MISP data
|
|
75
|
+
for event in request["data"]:
|
|
76
|
+
for attribute in event["Attribute"]:
|
|
77
|
+
if attribute["type"] in mispattributes["input"]:
|
|
78
|
+
logging.debug("Adding %s to structured CSV export of ThreatConnectExport", attribute["value"])
|
|
79
|
+
if "|" in attribute["type"]:
|
|
80
|
+
# if the attribute type has multiple values, line it up with the corresponding ThreatConnect values in fieldmap
|
|
81
|
+
indicators = tuple(attribute["value"].split("|"))
|
|
82
|
+
tc_types = tuple(fieldmap[attribute["type"]].split("|"))
|
|
83
|
+
for i, indicator in enumerate(indicators):
|
|
84
|
+
writer.writerow({
|
|
85
|
+
"Type": tc_types[i],
|
|
86
|
+
"Value": indicator,
|
|
87
|
+
"Source": config["Default_Source"],
|
|
88
|
+
"Description": attribute["comment"]
|
|
89
|
+
})
|
|
90
|
+
else:
|
|
91
|
+
writer.writerow({
|
|
92
|
+
"Type": fieldmap[attribute["type"]],
|
|
93
|
+
"Value": attribute["value"],
|
|
94
|
+
"Source": config["Default_Source"],
|
|
95
|
+
"Description": attribute["comment"]
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return {"response": [], "data": str(base64.b64encode(bytes(response.getvalue(), 'utf-8')), 'utf-8')}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def introspection():
|
|
102
|
+
"""
|
|
103
|
+
Relay the supported attributes to MISP.
|
|
104
|
+
No Input
|
|
105
|
+
Output
|
|
106
|
+
Dictionary of supported MISP attributes
|
|
107
|
+
"""
|
|
108
|
+
modulesetup = {
|
|
109
|
+
"responseType": "application/txt",
|
|
110
|
+
"outputFileExtension": "csv",
|
|
111
|
+
"userConfig": {},
|
|
112
|
+
"inputSource": []
|
|
113
|
+
}
|
|
114
|
+
return modulesetup
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def version():
|
|
118
|
+
"""
|
|
119
|
+
Relay module version and associated metadata to MISP.
|
|
120
|
+
No Input
|
|
121
|
+
Output
|
|
122
|
+
moduleinfo: metadata output containing all potential configuration values
|
|
123
|
+
"""
|
|
124
|
+
moduleinfo["config"] = moduleconfig
|
|
125
|
+
return moduleinfo
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2022 Google Inc. All Rights Reserved.
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Creates a VT Collection with indicators present in a given event."""
|
|
17
|
+
|
|
18
|
+
import base64
|
|
19
|
+
import json
|
|
20
|
+
import requests
|
|
21
|
+
|
|
22
|
+
misperrors = {
|
|
23
|
+
'error': 'Error'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
mispattributes = {
|
|
27
|
+
'input': [
|
|
28
|
+
'hostname',
|
|
29
|
+
'domain',
|
|
30
|
+
'ip-src',
|
|
31
|
+
'ip-dst',
|
|
32
|
+
'md5',
|
|
33
|
+
'sha1',
|
|
34
|
+
'sha256',
|
|
35
|
+
'url'
|
|
36
|
+
],
|
|
37
|
+
'format': 'misp_standard',
|
|
38
|
+
'responseType': 'application/txt',
|
|
39
|
+
'outputFileExtension': 'txt',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
moduleinfo = {
|
|
43
|
+
'version': '1.0',
|
|
44
|
+
'author': 'VirusTotal',
|
|
45
|
+
'description': 'Creates a VT Collection from an event iocs.',
|
|
46
|
+
'module-type': ['export'],
|
|
47
|
+
'name': 'VirusTotal Collections Export',
|
|
48
|
+
'logo': 'virustotal.png',
|
|
49
|
+
'requirements': ['An access to the VirusTotal API (apikey).'],
|
|
50
|
+
'features': 'This export module which takes advantage of a new endpoint in VT APIv3 to create VT Collections from IOCs contained in a MISP event. With this module users will be able to create a collection just using the Download as... button.',
|
|
51
|
+
'references': ['https://www.virustotal.com/', 'https://blog.virustotal.com/2021/11/introducing-virustotal-collections.html'],
|
|
52
|
+
'input': 'A domain, hash (md5, sha1, sha256 or sha512), hostname, url or IP address attribute.',
|
|
53
|
+
'output': 'A VirusTotal collection in VT.',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
moduleconfig = [
|
|
57
|
+
'vt_api_key',
|
|
58
|
+
'proxy_host',
|
|
59
|
+
'proxy_port',
|
|
60
|
+
'proxy_username',
|
|
61
|
+
'proxy_password'
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class VTError(Exception):
|
|
66
|
+
"Exception class to map vt api response errors."
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def create_collection(api_key, event_data):
|
|
71
|
+
headers = {
|
|
72
|
+
'x-apikey': api_key,
|
|
73
|
+
'content-type': 'application/json',
|
|
74
|
+
'x-tool': 'MISPModuleVirusTotalCollectionExport',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
response = requests.post('https://www.virustotal.com/api/v3/integrations/misp/collections',
|
|
78
|
+
headers=headers,
|
|
79
|
+
json=event_data)
|
|
80
|
+
|
|
81
|
+
uuid = event_data['Event']['uuid']
|
|
82
|
+
response_data = response.json()
|
|
83
|
+
|
|
84
|
+
if response.status_code == 200:
|
|
85
|
+
col_id = response_data['data']['id']
|
|
86
|
+
return f'{uuid}: https://www.virustotal.com/gui/collection/{col_id}/iocs'
|
|
87
|
+
|
|
88
|
+
error = response_data['error']['message']
|
|
89
|
+
if response.status_code == 400:
|
|
90
|
+
return f'{uuid}: {error}'
|
|
91
|
+
else:
|
|
92
|
+
misperrors['error'] = error
|
|
93
|
+
raise VTError(error)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def normalize_misp_data(data):
|
|
97
|
+
normalized_data = {'Event': data.pop('Event', {})}
|
|
98
|
+
for attr_key in data:
|
|
99
|
+
if isinstance(data[attr_key], list) or isinstance(data[attr_key], dict):
|
|
100
|
+
if attr_key == 'EventTag':
|
|
101
|
+
normalized_data['Event']['Tag'] = [tag['Tag'] for tag in data[attr_key]]
|
|
102
|
+
else:
|
|
103
|
+
normalized_data['Event'][attr_key] = data[attr_key]
|
|
104
|
+
|
|
105
|
+
return normalized_data
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def handler(q=False):
|
|
109
|
+
request = json.loads(q)
|
|
110
|
+
|
|
111
|
+
if not request.get('config') or not request['config'].get('vt_api_key'):
|
|
112
|
+
misperrors['error'] = 'A VirusTotal api key is required for this module.'
|
|
113
|
+
return misperrors
|
|
114
|
+
|
|
115
|
+
config = request['config']
|
|
116
|
+
data = request['data']
|
|
117
|
+
responses = []
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
for event_data in data:
|
|
121
|
+
normalized_event = normalize_misp_data(event_data)
|
|
122
|
+
responses.append(create_collection(config.get('vt_api_key'),
|
|
123
|
+
normalized_event))
|
|
124
|
+
|
|
125
|
+
output = '\n'.join(responses)
|
|
126
|
+
return {
|
|
127
|
+
"response": [],
|
|
128
|
+
"data": str(base64.b64encode(bytes(output, 'utf-8')), 'utf-8'),
|
|
129
|
+
}
|
|
130
|
+
except VTError:
|
|
131
|
+
return misperrors
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def introspection():
|
|
136
|
+
return mispattributes
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def version():
|
|
140
|
+
moduleinfo['config'] = moduleconfig
|
|
141
|
+
return moduleinfo
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'''Export MISP event to VirusTotal Graph.'''
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import base64
|
|
5
|
+
import json
|
|
6
|
+
from vt_graph_parser.importers.pymisp_response import from_pymisp_response
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
misperrors = {
|
|
10
|
+
'error': 'Error'
|
|
11
|
+
}
|
|
12
|
+
moduleinfo = {
|
|
13
|
+
'version': '0.1',
|
|
14
|
+
'author': 'VirusTotal',
|
|
15
|
+
'description': 'This module is used to create a VirusTotal Graph from a MISP event.',
|
|
16
|
+
'module-type': ['export'],
|
|
17
|
+
'name': 'VirusTotal Graph Export',
|
|
18
|
+
'logo': 'virustotal.png',
|
|
19
|
+
'requirements': ['vt_graph_api, the python library to query the VirusTotal graph API'],
|
|
20
|
+
'features': 'The module takes the MISP event as input and queries the VirusTotal Graph API to create a new graph out of the event.\n\nOnce the graph is ready, we get the url of it, which is returned so we can view it on VirusTotal.',
|
|
21
|
+
'references': ['https://www.virustotal.com/gui/graph-overview'],
|
|
22
|
+
'input': 'A MISP event.',
|
|
23
|
+
'output': 'Link of the VirusTotal Graph created for the event.',
|
|
24
|
+
}
|
|
25
|
+
mispattributes = {
|
|
26
|
+
'input': [
|
|
27
|
+
'hostname',
|
|
28
|
+
'domain',
|
|
29
|
+
'ip-src',
|
|
30
|
+
'ip-dst',
|
|
31
|
+
'md5',
|
|
32
|
+
'sha1',
|
|
33
|
+
'sha256',
|
|
34
|
+
'url',
|
|
35
|
+
'filename|md5',
|
|
36
|
+
'filename'
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
moduleconfig = [
|
|
40
|
+
'vt_api_key',
|
|
41
|
+
'fetch_information',
|
|
42
|
+
'private',
|
|
43
|
+
'fetch_vt_enterprise',
|
|
44
|
+
'expand_one_level',
|
|
45
|
+
'user_editors',
|
|
46
|
+
'user_viewers',
|
|
47
|
+
'group_editors',
|
|
48
|
+
'group_viewers'
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def handler(q=False):
|
|
53
|
+
"""Expansion handler.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
q (bool, optional): module data. Defaults to False.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
[str]: VirusTotal graph links
|
|
60
|
+
"""
|
|
61
|
+
if not q:
|
|
62
|
+
return False
|
|
63
|
+
request = json.loads(q)
|
|
64
|
+
|
|
65
|
+
if not request.get('config') or not request['config'].get('vt_api_key'):
|
|
66
|
+
misperrors['error'] = 'A VirusTotal api key is required for this module.'
|
|
67
|
+
return misperrors
|
|
68
|
+
|
|
69
|
+
config = request['config']
|
|
70
|
+
|
|
71
|
+
api_key = config.get('vt_api_key')
|
|
72
|
+
fetch_information = config.get('fetch_information') or False
|
|
73
|
+
private = config.get('private') or False
|
|
74
|
+
fetch_vt_enterprise = config.get('fetch_vt_enterprise') or False
|
|
75
|
+
expand_one_level = config.get('expand_one_level') or False
|
|
76
|
+
|
|
77
|
+
user_editors = config.get('user_editors')
|
|
78
|
+
if user_editors:
|
|
79
|
+
user_editors = user_editors.split(',')
|
|
80
|
+
user_viewers = config.get('user_viewers')
|
|
81
|
+
if user_viewers:
|
|
82
|
+
user_viewers = user_viewers.split(',')
|
|
83
|
+
group_editors = config.get('group_editors')
|
|
84
|
+
if group_editors:
|
|
85
|
+
group_editors = group_editors.split(',')
|
|
86
|
+
group_viewers = config.get('group_viewers')
|
|
87
|
+
if group_viewers:
|
|
88
|
+
group_viewers = group_viewers.split(',')
|
|
89
|
+
|
|
90
|
+
graphs = from_pymisp_response(
|
|
91
|
+
request, api_key, fetch_information=fetch_information,
|
|
92
|
+
private=private, fetch_vt_enterprise=fetch_vt_enterprise,
|
|
93
|
+
user_editors=user_editors, user_viewers=user_viewers,
|
|
94
|
+
group_editors=group_editors, group_viewers=group_viewers,
|
|
95
|
+
expand_node_one_level=expand_one_level)
|
|
96
|
+
links = []
|
|
97
|
+
|
|
98
|
+
for graph in graphs:
|
|
99
|
+
graph.save_graph()
|
|
100
|
+
links.append(graph.get_ui_link())
|
|
101
|
+
|
|
102
|
+
# This file will contains one VirusTotal graph link for each exported event
|
|
103
|
+
file_data = str(base64.b64encode(
|
|
104
|
+
bytes('\n'.join(links), 'utf-8')), 'utf-8')
|
|
105
|
+
return {'response': [], 'data': file_data}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def introspection():
|
|
109
|
+
modulesetup = {
|
|
110
|
+
'responseType': 'application/txt',
|
|
111
|
+
'outputFileExtension': 'txt',
|
|
112
|
+
'userConfig': {},
|
|
113
|
+
'inputSource': []
|
|
114
|
+
}
|
|
115
|
+
return modulesetup
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def version():
|
|
119
|
+
moduleinfo['config'] = moduleconfig
|
|
120
|
+
return moduleinfo
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import base64
|
|
3
|
+
import re
|
|
4
|
+
try:
|
|
5
|
+
import yara
|
|
6
|
+
except (OSError, ImportError):
|
|
7
|
+
print("yara is missing, use 'pip3 install -I -r REQUIREMENTS' from the root of this repository to install it.")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
misperrors = {'error': 'Error'}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
userConfig = {
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
moduleconfig = []
|
|
18
|
+
|
|
19
|
+
# fixed for now, options in the future:
|
|
20
|
+
# event, attribute, event-collection, attribute-collection
|
|
21
|
+
inputSource = ['event']
|
|
22
|
+
|
|
23
|
+
outputFileExtension = 'yara'
|
|
24
|
+
responseType = 'text/plain'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
moduleinfo = {
|
|
28
|
+
'version': '0.1',
|
|
29
|
+
'author': 'Christophe Vandeplas',
|
|
30
|
+
'description': 'This module is used to export MISP events to YARA.',
|
|
31
|
+
'module-type': ['export'],
|
|
32
|
+
'name': 'YARA Rule Export',
|
|
33
|
+
'logo': 'yara.png',
|
|
34
|
+
'requirements': ['yara-python python library'],
|
|
35
|
+
'features': 'The module will dynamically generate YARA rules for attributes that are marked as to IDS. Basic metadata about the event is added to the rule.\nAttributes that are already YARA rules are also exported, with a rewritten rule name.',
|
|
36
|
+
'references': ['https://virustotal.github.io/yara/'],
|
|
37
|
+
'input': 'Attributes and Objects.',
|
|
38
|
+
'output': 'A YARA file that can be used with the YARA scanning tool.',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class YaraRule():
|
|
43
|
+
def __init__(self, name):
|
|
44
|
+
self.name = name
|
|
45
|
+
self.strings = {}
|
|
46
|
+
self.conditions = []
|
|
47
|
+
self.meta = {}
|
|
48
|
+
|
|
49
|
+
def add_string(self, type_: str, s: str):
|
|
50
|
+
type_clean = ''.join(c if c.isalnum() or c == '_' else '_' for c in type_)
|
|
51
|
+
if type_clean not in self.strings:
|
|
52
|
+
self.strings[type_clean] = []
|
|
53
|
+
self.strings[type_clean].append(s)
|
|
54
|
+
|
|
55
|
+
def add_condition(self, condition: str):
|
|
56
|
+
self.conditions.append(condition)
|
|
57
|
+
|
|
58
|
+
def add_meta(self, key: str, value: str):
|
|
59
|
+
if key not in self.meta:
|
|
60
|
+
self.meta[key] = []
|
|
61
|
+
self.meta[key].append(value)
|
|
62
|
+
|
|
63
|
+
def __str__(self):
|
|
64
|
+
if len(self.strings) == 0 and len(self.conditions) == 0:
|
|
65
|
+
return "\n" # no strings, so no rule
|
|
66
|
+
|
|
67
|
+
result = []
|
|
68
|
+
result.append(f"rule {self.name} {{")
|
|
69
|
+
|
|
70
|
+
result.append(" meta:")
|
|
71
|
+
for key, values in self.meta.items():
|
|
72
|
+
i = 0
|
|
73
|
+
if len(values) == 1:
|
|
74
|
+
result.append(f" {key} = \"{values[0]}\"")
|
|
75
|
+
continue
|
|
76
|
+
for value in values:
|
|
77
|
+
result.append(f" {key}_{i} = \"{value}\"")
|
|
78
|
+
i += 1
|
|
79
|
+
|
|
80
|
+
result.append(" strings:")
|
|
81
|
+
for key, values in self.strings.items():
|
|
82
|
+
i = 0
|
|
83
|
+
for value in values:
|
|
84
|
+
result.append(f" ${key}_{i} = \"{value}\"")
|
|
85
|
+
i += 1
|
|
86
|
+
|
|
87
|
+
result.append(" condition:")
|
|
88
|
+
if len(self.conditions) == 0:
|
|
89
|
+
result.append(" any of them")
|
|
90
|
+
for condition in self.conditions:
|
|
91
|
+
result.append(f" {condition}")
|
|
92
|
+
|
|
93
|
+
result.append("}")
|
|
94
|
+
result.append("")
|
|
95
|
+
return '\n'.join(result)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def handle_string(yara_rules: list, yr: YaraRule, attribute: dict):
|
|
99
|
+
if not attribute['to_ids']: # skip non IDS attributes
|
|
100
|
+
return
|
|
101
|
+
yr.add_string(attribute['type'], attribute['value'])
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def handle_combined(yara_rules: list, yr: YaraRule, attribute: dict):
|
|
106
|
+
if not attribute['to_ids']: # skip non IDS attributes
|
|
107
|
+
return
|
|
108
|
+
type_1, type_2 = attribute['type'].split('|')
|
|
109
|
+
value_1, value_2 = attribute['value'].split('|')
|
|
110
|
+
try:
|
|
111
|
+
handlers[type_1](yara_rules, yr, type_1, value_1)
|
|
112
|
+
except KeyError:
|
|
113
|
+
# ignore unsupported types
|
|
114
|
+
pass
|
|
115
|
+
try:
|
|
116
|
+
handlers[type_2](yara_rules, yr, type_2, value_2)
|
|
117
|
+
except KeyError:
|
|
118
|
+
# ignore unsupported types
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def handle_yara(yara_rules: list, yr: YaraRule, attribute: dict):
|
|
123
|
+
# do not check for to_ids, as we want to always export the Yara rule
|
|
124
|
+
# split out as a separate rule, and rewrite the rule name
|
|
125
|
+
value = re.sub('^[ \t]*rule ', 'rule MISP_e{}_'.format(attribute['event_id']), attribute['value'], flags=re.MULTILINE)
|
|
126
|
+
# cleanup dirty stuff from people
|
|
127
|
+
substitutions = (('”', '"'),
|
|
128
|
+
('“', '"'),
|
|
129
|
+
('″', '"'),
|
|
130
|
+
('`', "'"),
|
|
131
|
+
('\r', ''),
|
|
132
|
+
('Rule ', 'rule ') # some people write this with the wrong case
|
|
133
|
+
# ('$ ', '$'), # this breaks rules
|
|
134
|
+
# ('\t\t', '\n'), # this breaks rules
|
|
135
|
+
)
|
|
136
|
+
for substitution in substitutions:
|
|
137
|
+
if substitution[0] in value:
|
|
138
|
+
value = value.replace(substitution[0], substitution[1])
|
|
139
|
+
|
|
140
|
+
# we may ignore any global rules as they might disable everything
|
|
141
|
+
# on the other hand we're only processing one event...
|
|
142
|
+
# if 'global rule' in value:
|
|
143
|
+
# return
|
|
144
|
+
|
|
145
|
+
# private rules need some more rewriting
|
|
146
|
+
if 'private rule' in value:
|
|
147
|
+
priv_rules = re.findall(r'private rule (\w+)', value, flags=re.MULTILINE)
|
|
148
|
+
for priv_rule in priv_rules:
|
|
149
|
+
value = re.sub(priv_rule, 'MISP_e{}_{}'.format(attribute['event_id'], priv_rule), value, flags=re.MULTILINE)
|
|
150
|
+
|
|
151
|
+
# compile the yara rule to confirm it's validity
|
|
152
|
+
try:
|
|
153
|
+
yara.compile(source=value)
|
|
154
|
+
except Exception:
|
|
155
|
+
# skip rules that do not compile
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# all checks done, add the rule
|
|
159
|
+
yara_rules.append(value)
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def handle_malware_sample(yara_rules: list, yr: YaraRule, attribute: dict):
|
|
164
|
+
if not attribute['to_ids']: # skip non IDS attributes
|
|
165
|
+
return
|
|
166
|
+
handle_combined(yara_rules, yr, 'filename|md5', attribute['value'])
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def handle_meta(yara_rules: list, yr: YaraRule, attribute: dict):
|
|
170
|
+
yr.add_meta(attribute['type'], attribute['value'])
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
handlers = {
|
|
175
|
+
'yara': handle_yara,
|
|
176
|
+
'hostname': handle_string,
|
|
177
|
+
'hostname|port': handle_combined,
|
|
178
|
+
'domain': handle_string,
|
|
179
|
+
'domain|ip': handle_combined,
|
|
180
|
+
'ip': handle_string,
|
|
181
|
+
'ip-src': handle_string,
|
|
182
|
+
'ip-dst': handle_string,
|
|
183
|
+
'ip-dst|port': handle_combined, # we could also handle_string, which would be more specific. Less false positives, but less true positives too...
|
|
184
|
+
'ip-src|port': handle_combined,
|
|
185
|
+
'url': handle_string,
|
|
186
|
+
'email': handle_string,
|
|
187
|
+
'email-src': handle_string,
|
|
188
|
+
'email-dst': handle_string,
|
|
189
|
+
'email-subject': handle_string,
|
|
190
|
+
'email-attachment': handle_string,
|
|
191
|
+
'email-header': handle_string,
|
|
192
|
+
'email-reply-to': handle_string,
|
|
193
|
+
'email-x-mailer': handle_string,
|
|
194
|
+
'email-mime-boundary': handle_string,
|
|
195
|
+
'email-thread-index': handle_string,
|
|
196
|
+
'email-message-id': handle_string,
|
|
197
|
+
'filename': handle_string,
|
|
198
|
+
'filename|md5': handle_combined,
|
|
199
|
+
'filename|sha1': handle_combined,
|
|
200
|
+
'filename|sha256': handle_combined,
|
|
201
|
+
'filename|authentihash': handle_combined,
|
|
202
|
+
'filename|vhash': handle_combined,
|
|
203
|
+
'filename|ssdeep': handle_combined,
|
|
204
|
+
'filename|imphash': handle_combined,
|
|
205
|
+
'filename|impfuzzy': handle_combined,
|
|
206
|
+
'filename|pehash': handle_combined,
|
|
207
|
+
'filename|sha224': handle_combined,
|
|
208
|
+
'filename|sha384': handle_combined,
|
|
209
|
+
'filename|sha512': handle_combined,
|
|
210
|
+
'filename|sha512/224': handle_combined,
|
|
211
|
+
'filename|sha512/256': handle_combined,
|
|
212
|
+
'filename|sha3-224': handle_combined,
|
|
213
|
+
'filename|sha3-256': handle_combined,
|
|
214
|
+
'filename|sha3-384': handle_combined,
|
|
215
|
+
'filename|sha3-512': handle_combined,
|
|
216
|
+
'filename|tlsh': handle_combined,
|
|
217
|
+
'malware-sample': handle_malware_sample,
|
|
218
|
+
'pattern-in-file': handle_string,
|
|
219
|
+
'pattern-in-traffic': handle_string,
|
|
220
|
+
'pattern-in-memory': handle_string,
|
|
221
|
+
'link': handle_meta
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# auto-generate the list of types to use
|
|
225
|
+
types_to_use = handlers.keys()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def handler(q=False):
|
|
229
|
+
if q is False:
|
|
230
|
+
return False
|
|
231
|
+
request = json.loads(q)
|
|
232
|
+
|
|
233
|
+
yara_rules = []
|
|
234
|
+
for event in request["data"]:
|
|
235
|
+
event_info_clean = ''.join(c if c.isalnum() or c == '_' else '_' for c in event['Event']['info'])
|
|
236
|
+
yr = YaraRule(f"MISP_e{event['Event']['id']}_{event_info_clean}")
|
|
237
|
+
|
|
238
|
+
yr.add_meta('description', event['Event']['info'])
|
|
239
|
+
yr.add_meta('author', f"MISP - {event['Orgc']['name']}")
|
|
240
|
+
yr.add_meta('misp_event_date', event['Event']['date'])
|
|
241
|
+
yr.add_meta('misp_event_id', event['Event']['id'])
|
|
242
|
+
yr.add_meta('misp_event_uuid', event['Event']['uuid'])
|
|
243
|
+
|
|
244
|
+
for attribute in event.get("Attribute", []):
|
|
245
|
+
try:
|
|
246
|
+
handlers[attribute['type']](yara_rules, yr, attribute)
|
|
247
|
+
except KeyError:
|
|
248
|
+
# ignore unsupported types
|
|
249
|
+
pass
|
|
250
|
+
for obj in event.get("Object", []):
|
|
251
|
+
for attribute in obj["Attribute"]:
|
|
252
|
+
try:
|
|
253
|
+
handlers[attribute['type']](yara_rules, yr, attribute)
|
|
254
|
+
except KeyError:
|
|
255
|
+
# ignore unsupported types
|
|
256
|
+
pass
|
|
257
|
+
yara_rules.append(str(yr))
|
|
258
|
+
r = {"response": [], "data": str(base64.b64encode(bytes('\n'.join(yara_rules), 'utf-8')), 'utf-8')}
|
|
259
|
+
|
|
260
|
+
return r
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def introspection():
|
|
264
|
+
modulesetup = {}
|
|
265
|
+
try:
|
|
266
|
+
responseType
|
|
267
|
+
modulesetup['responseType'] = responseType
|
|
268
|
+
except NameError:
|
|
269
|
+
pass
|
|
270
|
+
try:
|
|
271
|
+
userConfig
|
|
272
|
+
modulesetup['userConfig'] = userConfig
|
|
273
|
+
except NameError:
|
|
274
|
+
pass
|
|
275
|
+
try:
|
|
276
|
+
outputFileExtension
|
|
277
|
+
modulesetup['outputFileExtension'] = outputFileExtension
|
|
278
|
+
except NameError:
|
|
279
|
+
pass
|
|
280
|
+
try:
|
|
281
|
+
inputSource
|
|
282
|
+
modulesetup['inputSource'] = inputSource
|
|
283
|
+
except NameError:
|
|
284
|
+
pass
|
|
285
|
+
return modulesetup
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def version():
|
|
289
|
+
moduleinfo['config'] = moduleconfig
|
|
290
|
+
return moduleinfo
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/')[:-3])))
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
'vmray_import',
|
|
7
|
+
'lastline_import',
|
|
8
|
+
'ocr',
|
|
9
|
+
'cuckooimport',
|
|
10
|
+
'goamlimport',
|
|
11
|
+
'email_import',
|
|
12
|
+
'mispjson',
|
|
13
|
+
'openiocimport',
|
|
14
|
+
'threatanalyzer_import',
|
|
15
|
+
'csvimport',
|
|
16
|
+
'cof2misp',
|
|
17
|
+
'joe_import',
|
|
18
|
+
'taxii21',
|
|
19
|
+
'url_import',
|
|
20
|
+
'vmray_summary_json_import',
|
|
21
|
+
'import_blueprint'
|
|
22
|
+
]
|