misp-modules 2.4.196__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. misp_modules/__init__.py +379 -0
  2. misp_modules/helpers/__init__.py +1 -0
  3. misp_modules/helpers/cache.py +84 -0
  4. misp_modules/lib/__init__.py +4 -0
  5. misp_modules/lib/_vmray/__init__.py +0 -0
  6. misp_modules/lib/_vmray/parser.py +1417 -0
  7. misp_modules/lib/_vmray/rest_api.py +148 -0
  8. misp_modules/lib/cof2misp/LICENSE-2.0.txt +202 -0
  9. misp_modules/lib/cof2misp/__init__.py +0 -0
  10. misp_modules/lib/cof2misp/cof.py +165 -0
  11. misp_modules/lib/joe_mapping.py +114 -0
  12. misp_modules/lib/joe_parser.py +573 -0
  13. misp_modules/lib/lastline_api.py +841 -0
  14. misp_modules/lib/qintel_helper.py +263 -0
  15. misp_modules/lib/stix2misp.py +2080 -0
  16. misp_modules/lib/stix2misp_mapping.py +460 -0
  17. misp_modules/lib/synonymsToTagNames.json +1 -0
  18. misp_modules/lib/vt_graph_parser/__init__.py +8 -0
  19. misp_modules/lib/vt_graph_parser/errors.py +20 -0
  20. misp_modules/lib/vt_graph_parser/helpers/__init__.py +7 -0
  21. misp_modules/lib/vt_graph_parser/helpers/parsers.py +88 -0
  22. misp_modules/lib/vt_graph_parser/helpers/rules.py +304 -0
  23. misp_modules/lib/vt_graph_parser/helpers/wrappers.py +58 -0
  24. misp_modules/lib/vt_graph_parser/importers/__init__.py +7 -0
  25. misp_modules/lib/vt_graph_parser/importers/base.py +98 -0
  26. misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +73 -0
  27. misp_modules/modules/__init__.py +4 -0
  28. misp_modules/modules/action_mod/__init__.py +1 -0
  29. misp_modules/modules/action_mod/_utils/__init__.py +1 -0
  30. misp_modules/modules/action_mod/_utils/utils.py +70 -0
  31. misp_modules/modules/action_mod/mattermost.py +113 -0
  32. misp_modules/modules/action_mod/slack.py +96 -0
  33. misp_modules/modules/action_mod/testaction.py +68 -0
  34. misp_modules/modules/expansion/__init__.py +36 -0
  35. misp_modules/modules/expansion/_dnsdb_query/COPYRIGHT +27 -0
  36. misp_modules/modules/expansion/_dnsdb_query/LICENSE +202 -0
  37. misp_modules/modules/expansion/_dnsdb_query/README.md +162 -0
  38. misp_modules/modules/expansion/_dnsdb_query/__init__.py +0 -0
  39. misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py +327 -0
  40. misp_modules/modules/expansion/_ransomcoindb/__init__.py +0 -0
  41. misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +96 -0
  42. misp_modules/modules/expansion/_vulnerability_parser/__init__.py +0 -0
  43. misp_modules/modules/expansion/_vulnerability_parser/vulnerability_parser.py +118 -0
  44. misp_modules/modules/expansion/abuseipdb.py +146 -0
  45. misp_modules/modules/expansion/apiosintds.py +365 -0
  46. misp_modules/modules/expansion/apivoid.py +133 -0
  47. misp_modules/modules/expansion/assemblyline_query.py +179 -0
  48. misp_modules/modules/expansion/assemblyline_submit.py +100 -0
  49. misp_modules/modules/expansion/backscatter_io.py +84 -0
  50. misp_modules/modules/expansion/btc_scam_check.py +54 -0
  51. misp_modules/modules/expansion/btc_steroids.py +238 -0
  52. misp_modules/modules/expansion/censys_enrich.py +287 -0
  53. misp_modules/modules/expansion/circl_passivedns.py +85 -0
  54. misp_modules/modules/expansion/circl_passivessl.py +113 -0
  55. misp_modules/modules/expansion/clamav.py +129 -0
  56. misp_modules/modules/expansion/cluster25_expand.py +239 -0
  57. misp_modules/modules/expansion/countrycode.py +69 -0
  58. misp_modules/modules/expansion/cpe.py +140 -0
  59. misp_modules/modules/expansion/crowdsec.py +235 -0
  60. misp_modules/modules/expansion/crowdstrike_falcon.py +148 -0
  61. misp_modules/modules/expansion/cuckoo_submit.py +161 -0
  62. misp_modules/modules/expansion/cve.py +57 -0
  63. misp_modules/modules/expansion/cve_advanced.py +185 -0
  64. misp_modules/modules/expansion/cytomic_orion.py +196 -0
  65. misp_modules/modules/expansion/dbl_spamhaus.py +78 -0
  66. misp_modules/modules/expansion/dns.py +71 -0
  67. misp_modules/modules/expansion/docx_enrich.py +71 -0
  68. misp_modules/modules/expansion/domaintools.py +290 -0
  69. misp_modules/modules/expansion/eql.py +91 -0
  70. misp_modules/modules/expansion/eupi.py +86 -0
  71. misp_modules/modules/expansion/extract_url_components.py +83 -0
  72. misp_modules/modules/expansion/farsight_passivedns.py +243 -0
  73. misp_modules/modules/expansion/geoip_asn.py +75 -0
  74. misp_modules/modules/expansion/geoip_city.py +75 -0
  75. misp_modules/modules/expansion/geoip_country.py +73 -0
  76. misp_modules/modules/expansion/google_safe_browsing.py +86 -0
  77. misp_modules/modules/expansion/google_search.py +57 -0
  78. misp_modules/modules/expansion/google_threat_intelligence.py +453 -0
  79. misp_modules/modules/expansion/greynoise.py +346 -0
  80. misp_modules/modules/expansion/hashdd.py +53 -0
  81. misp_modules/modules/expansion/hashlookup.py +118 -0
  82. misp_modules/modules/expansion/hibp.py +62 -0
  83. misp_modules/modules/expansion/html_to_markdown.py +63 -0
  84. misp_modules/modules/expansion/hyasinsight.py +881 -0
  85. misp_modules/modules/expansion/intel471.py +73 -0
  86. misp_modules/modules/expansion/intelmq_eventdb.py.experimental +67 -0
  87. misp_modules/modules/expansion/ip2locationio.py +90 -0
  88. misp_modules/modules/expansion/ipasn.py +70 -0
  89. misp_modules/modules/expansion/ipinfo.py +112 -0
  90. misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +633 -0
  91. misp_modules/modules/expansion/iprep.py +98 -0
  92. misp_modules/modules/expansion/jinja_template_rendering.py +54 -0
  93. misp_modules/modules/expansion/joesandbox_query.py +91 -0
  94. misp_modules/modules/expansion/joesandbox_submit.py +147 -0
  95. misp_modules/modules/expansion/lastline_query.py +148 -0
  96. misp_modules/modules/expansion/lastline_submit.py +180 -0
  97. misp_modules/modules/expansion/macaddress_io.py +130 -0
  98. misp_modules/modules/expansion/macvendors.py +55 -0
  99. misp_modules/modules/expansion/malshare_upload.py +107 -0
  100. misp_modules/modules/expansion/malwarebazaar.py +67 -0
  101. misp_modules/modules/expansion/mcafee_insights_enrich.py +249 -0
  102. misp_modules/modules/expansion/mmdb_lookup.py +138 -0
  103. misp_modules/modules/expansion/module.py.skeleton +44 -0
  104. misp_modules/modules/expansion/mwdb.py +152 -0
  105. misp_modules/modules/expansion/ocr_enrich.py +74 -0
  106. misp_modules/modules/expansion/ods_enrich.py +71 -0
  107. misp_modules/modules/expansion/odt_enrich.py +61 -0
  108. misp_modules/modules/expansion/onyphe.py +243 -0
  109. misp_modules/modules/expansion/onyphe_full.py +387 -0
  110. misp_modules/modules/expansion/otx.py +167 -0
  111. misp_modules/modules/expansion/passive_ssh.py +150 -0
  112. misp_modules/modules/expansion/passivetotal.py +358 -0
  113. misp_modules/modules/expansion/pdf_enrich.py +58 -0
  114. misp_modules/modules/expansion/pptx_enrich.py +65 -0
  115. misp_modules/modules/expansion/qintel_qsentry.py +229 -0
  116. misp_modules/modules/expansion/qrcode.py +99 -0
  117. misp_modules/modules/expansion/ransomcoindb.py +82 -0
  118. misp_modules/modules/expansion/rbl.py +126 -0
  119. misp_modules/modules/expansion/recordedfuture.py +544 -0
  120. misp_modules/modules/expansion/reversedns.py +77 -0
  121. misp_modules/modules/expansion/securitytrails.py +571 -0
  122. misp_modules/modules/expansion/shodan.py +244 -0
  123. misp_modules/modules/expansion/sigma_queries.py +61 -0
  124. misp_modules/modules/expansion/sigma_syntax_validator.py +49 -0
  125. misp_modules/modules/expansion/sigmf_expand.py +303 -0
  126. misp_modules/modules/expansion/socialscan.py +108 -0
  127. misp_modules/modules/expansion/sophoslabs_intelix.py +146 -0
  128. misp_modules/modules/expansion/sourcecache.py +57 -0
  129. misp_modules/modules/expansion/stairwell.py +156 -0
  130. misp_modules/modules/expansion/stix2_pattern_syntax_validator.py +56 -0
  131. misp_modules/modules/expansion/threatcrowd.py +170 -0
  132. misp_modules/modules/expansion/threatfox.py +75 -0
  133. misp_modules/modules/expansion/threatminer.py +152 -0
  134. misp_modules/modules/expansion/triage_submit.py +119 -0
  135. misp_modules/modules/expansion/trustar_enrich.py +235 -0
  136. misp_modules/modules/expansion/urlhaus.py +164 -0
  137. misp_modules/modules/expansion/urlscan.py +269 -0
  138. misp_modules/modules/expansion/variotdbs.py +133 -0
  139. misp_modules/modules/expansion/virustotal.py +311 -0
  140. misp_modules/modules/expansion/virustotal_public.py +272 -0
  141. misp_modules/modules/expansion/virustotal_upload.py +91 -0
  142. misp_modules/modules/expansion/vmray_submit.py +161 -0
  143. misp_modules/modules/expansion/vmware_nsx.py +628 -0
  144. misp_modules/modules/expansion/vulndb.py +292 -0
  145. misp_modules/modules/expansion/vulnerability_lookup.py +324 -0
  146. misp_modules/modules/expansion/vulners.py +81 -0
  147. misp_modules/modules/expansion/vysion.py +221 -0
  148. misp_modules/modules/expansion/whois.py +72 -0
  149. misp_modules/modules/expansion/whoisfreaks.py +232 -0
  150. misp_modules/modules/expansion/wiki.py +58 -0
  151. misp_modules/modules/expansion/xforceexchange.py +189 -0
  152. misp_modules/modules/expansion/xlsx_enrich.py +63 -0
  153. misp_modules/modules/expansion/yara_query.py +70 -0
  154. misp_modules/modules/expansion/yara_syntax_validator.py +49 -0
  155. misp_modules/modules/expansion/yeti.py +196 -0
  156. misp_modules/modules/export_mod/__init__.py +3 -0
  157. misp_modules/modules/export_mod/cef_export.py +93 -0
  158. misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py +150 -0
  159. misp_modules/modules/export_mod/defender_endpoint_export.py +141 -0
  160. misp_modules/modules/export_mod/goamlexport.py +255 -0
  161. misp_modules/modules/export_mod/liteexport.py +97 -0
  162. misp_modules/modules/export_mod/mass_eql_export.py +106 -0
  163. misp_modules/modules/export_mod/nexthinkexport.py +131 -0
  164. misp_modules/modules/export_mod/osqueryexport.py +125 -0
  165. misp_modules/modules/export_mod/pdfexport.py +107 -0
  166. misp_modules/modules/export_mod/testexport.py +73 -0
  167. misp_modules/modules/export_mod/threatStream_misp_export.py +114 -0
  168. misp_modules/modules/export_mod/threat_connect_export.py +125 -0
  169. misp_modules/modules/export_mod/virustotal_collections.py +141 -0
  170. misp_modules/modules/export_mod/vt_graph.py +120 -0
  171. misp_modules/modules/export_mod/yara_export.py +290 -0
  172. misp_modules/modules/import_mod/__init__.py +22 -0
  173. misp_modules/modules/import_mod/cof2misp.py +264 -0
  174. misp_modules/modules/import_mod/csvimport.py +319 -0
  175. misp_modules/modules/import_mod/cuckooimport.py +749 -0
  176. misp_modules/modules/import_mod/email_import.py +294 -0
  177. misp_modules/modules/import_mod/goamlimport.py +188 -0
  178. misp_modules/modules/import_mod/import_blueprint.py +93 -0
  179. misp_modules/modules/import_mod/joe_import.py +74 -0
  180. misp_modules/modules/import_mod/lastline_import.py +160 -0
  181. misp_modules/modules/import_mod/mispjson.py +73 -0
  182. misp_modules/modules/import_mod/ocr.py +122 -0
  183. misp_modules/modules/import_mod/openiocimport.py +99 -0
  184. misp_modules/modules/import_mod/taxii21.py +383 -0
  185. misp_modules/modules/import_mod/testimport.py +73 -0
  186. misp_modules/modules/import_mod/threatanalyzer_import.py +552 -0
  187. misp_modules/modules/import_mod/url_import.py +97 -0
  188. misp_modules/modules/import_mod/vmray_import.py +96 -0
  189. misp_modules/modules/import_mod/vmray_summary_json_import.py +87 -0
  190. misp_modules-2.4.196.dist-info/LICENSE +661 -0
  191. misp_modules-2.4.196.dist-info/METADATA +281 -0
  192. misp_modules-2.4.196.dist-info/RECORD +194 -0
  193. misp_modules-2.4.196.dist-info/WHEEL +4 -0
  194. misp_modules-2.4.196.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,170 @@
1
+ import json
2
+ import requests
3
+ import re
4
+
5
+ misperrors = {'error': 'Error'}
6
+ mispattributes = {'input': ["hostname", "domain", "ip-src", "ip-dst", "md5", "sha1", "sha256", "sha512", "whois-registrant-email"],
7
+ 'output': ["domain", "ip-src", "ip-dst", "text", "md5", "sha1", "sha256", "sha512", "hostname", "whois-registrant-email"]
8
+ }
9
+
10
+ # possible module-types: 'expansion', 'hover' or both
11
+ moduleinfo = {
12
+ 'version': '1',
13
+ 'author': 'chrisdoman',
14
+ 'description': 'Module to get information from ThreatCrowd.',
15
+ 'module-type': ['expansion'],
16
+ 'name': 'ThreatCrowd Lookup',
17
+ 'logo': 'threatcrowd.png',
18
+ 'requirements': [],
19
+ 'features': 'This module takes a MISP attribute as input and queries ThreatCrowd with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute.',
20
+ 'references': ['https://www.threatcrowd.org/'],
21
+ 'input': 'A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512\n- whois-registrant-email',
22
+ 'output': 'MISP attributes mapped from the result of the query on ThreatCrowd, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- hostname\n- whois-registrant-email',
23
+ }
24
+
25
+ moduleconfig = []
26
+
27
+
28
+ # Avoid adding windows update to enrichment etc.
29
+ def isBlacklisted(value):
30
+ blacklist = ['8.8.8.8', '255.255.255.255', '192.168.56.', 'time.windows.com']
31
+
32
+ for b in blacklist:
33
+ if value in b:
34
+ return True
35
+
36
+ return False
37
+
38
+
39
+ def valid_ip(ip):
40
+ m = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip)
41
+ return bool(m) and all(map(lambda n: 0 <= int(n) <= 255, m.groups()))
42
+
43
+
44
+ def valid_domain(hostname):
45
+ if len(hostname) > 255:
46
+ return False
47
+ if hostname[-1] == ".":
48
+ hostname = hostname[:-1] # strip exactly one dot from the right, if present
49
+ allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
50
+ return all(allowed.match(x) for x in hostname.split("."))
51
+
52
+
53
+ def valid_email(email):
54
+ return bool(re.search(r"^[\w\.\+\-]+\@[\w]+\.[a-z]{2,3}$", email))
55
+
56
+
57
+ def handler(q=False):
58
+ if q is False:
59
+ return False
60
+
61
+ q = json.loads(q)
62
+
63
+ r = {"results": []}
64
+
65
+ if "ip-src" in q:
66
+ r["results"] += getIP(q["ip-src"])
67
+ if "ip-dst" in q:
68
+ r["results"] += getIP(q["ip-dst"])
69
+ if "domain" in q:
70
+ r["results"] += getDomain(q["domain"])
71
+ if 'hostname' in q:
72
+ r["results"] += getDomain(q['hostname'])
73
+ if 'md5' in q:
74
+ r["results"] += getHash(q['md5'])
75
+ if 'sha1' in q:
76
+ r["results"] += getHash(q['sha1'])
77
+ if 'sha256' in q:
78
+ r["results"] += getHash(q['sha256'])
79
+ if 'sha512' in q:
80
+ r["results"] += getHash(q['sha512'])
81
+ if 'whois-registrant-email' in q:
82
+ r["results"] += getEmail(q['whois-registrant-email'])
83
+
84
+ uniq = []
85
+ for res in r["results"]:
86
+ if res not in uniq:
87
+ uniq.append(res)
88
+ r["results"] = uniq
89
+ return r
90
+
91
+
92
+ def getHash(hash):
93
+
94
+ ret = []
95
+ req = json.loads(requests.get("https://www.threatcrowd.org/searchApi/v2/file/report/?resource=" + hash).text)
96
+
97
+ if "domains" in req:
98
+ domains = req["domains"]
99
+ for domain in domains:
100
+ if not isBlacklisted(domain) and valid_domain(domain):
101
+ ret.append({"types": ["hostname"], "values": [domain]})
102
+
103
+ if "ips" in req:
104
+ ips = req["ips"]
105
+ for ip in ips:
106
+ if not isBlacklisted(ip):
107
+ ret.append({"types": ["ip-dst"], "values": [ip]})
108
+
109
+ return ret
110
+
111
+
112
+ def getIP(ip):
113
+ ret = []
114
+ req = json.loads(requests.get("https://www.threatcrowd.org/searchApi/v2/ip/report/?ip=" + ip).text)
115
+
116
+ if "resolutions" in req:
117
+ for dns in req["resolutions"]:
118
+ if "domain" in dns:
119
+ if valid_domain(dns["domain"]):
120
+ ret.append({"types": ["hostname"], "values": [dns["domain"]]})
121
+
122
+ if "hashes" in req:
123
+ for hash in req["hashes"]:
124
+ ret.append({"types": ["md5"], "values": [hash]})
125
+
126
+ return ret
127
+
128
+
129
+ def getEmail(email):
130
+ ret = []
131
+ j = requests.get("https://www.threatcrowd.org/searchApi/v2/email/report/?email=" + email).text
132
+ req = json.loads(j)
133
+
134
+ if "domains" in req:
135
+ domains = req["domains"]
136
+ for domain in domains:
137
+ if not isBlacklisted(domain) and valid_domain(domain):
138
+ ret.append({"types": ["hostname"], "values": [domain]})
139
+
140
+ return ret
141
+
142
+
143
+ def getDomain(domain):
144
+
145
+ ret = []
146
+ req = json.loads(requests.get("https://www.threatcrowd.org/searchApi/v2/domain/report/?domain=" + domain).text)
147
+
148
+ if "resolutions" in req:
149
+ for dns in req["resolutions"]:
150
+ if "ip_address" in dns:
151
+ ret.append({"types": ["ip-dst"], "values": [dns["ip_address"]]})
152
+
153
+ if "emails" in req:
154
+ for email in req["emails"]:
155
+ ret.append({"types": ["whois-registrant-email"], "values": [email]})
156
+
157
+ if "hashes" in req:
158
+ for hash in req["hashes"]:
159
+ ret.append({"types": ["md5"], "values": [hash]})
160
+
161
+ return ret
162
+
163
+
164
+ def introspection():
165
+ return mispattributes
166
+
167
+
168
+ def version():
169
+ moduleinfo['config'] = moduleconfig
170
+ return moduleinfo
@@ -0,0 +1,75 @@
1
+ # -*- coding: utf-8 -*-
2
+ import requests
3
+ import json
4
+
5
+ misperrors = {'error': 'Error'}
6
+ mispattributes = {'input': ['md5', 'sha1', 'sha256', 'domain', 'url', 'email-src', 'ip-dst|port', 'ip-src|port'], 'output': ['text']}
7
+ moduleinfo = {
8
+ 'version': '0.1',
9
+ 'author': 'Corsin Camichel',
10
+ 'description': 'Module to search for an IOC on ThreatFox by abuse.ch.',
11
+ 'module-type': ['hover', 'expansion'],
12
+ 'name': 'ThreadFox Lookup',
13
+ 'logo': '',
14
+ 'requirements': [],
15
+ 'features': '',
16
+ 'references': [],
17
+ 'input': '',
18
+ 'output': '',
19
+ }
20
+ moduleconfig = []
21
+
22
+ API_URL = "https://threatfox-api.abuse.ch/api/v1/"
23
+
24
+
25
+ # copied from
26
+ # https://github.com/marjatech/threatfox2misp/blob/main/threatfox2misp.py
27
+ def confidence_level_to_tag(level: int) -> str:
28
+ confidence_tagging = {
29
+ 0: 'misp:confidence-level="unconfident"',
30
+ 10: 'misp:confidence-level="rarely-confident"',
31
+ 37: 'misp:confidence-level="fairly-confident"',
32
+ 63: 'misp:confidence-level="usually-confident"',
33
+ 90: 'misp:confidence-level="completely-confident"',
34
+ }
35
+
36
+ confidence_tag = ""
37
+ for tag_minvalue, tag in confidence_tagging.items():
38
+ if level >= tag_minvalue:
39
+ confidence_tag = tag
40
+ return confidence_tag
41
+
42
+
43
+ def handler(q=False):
44
+ if q is False:
45
+ return False
46
+
47
+ request = json.loads(q)
48
+ ret_val = ""
49
+
50
+ for input_type in mispattributes['input']:
51
+ if input_type in request:
52
+ to_query = request[input_type]
53
+ break
54
+ else:
55
+ misperrors['error'] = "Unsupported attributes type:"
56
+ return misperrors
57
+
58
+ data = {"query": "search_ioc", "search_term": f"{to_query}"}
59
+ response = requests.post(API_URL, data=json.dumps(data))
60
+ if response.status_code == 200:
61
+ result = json.loads(response.text)
62
+ if(result["query_status"] == "ok"):
63
+ confidence_tag = confidence_level_to_tag(result["data"][0]["confidence_level"])
64
+ ret_val = {'results': [{'types': mispattributes['output'], 'values': [result["data"][0]["threat_type_desc"]], 'tags': [result["data"][0]["malware"], result["data"][0]["malware_printable"], confidence_tag]}]}
65
+
66
+ return ret_val
67
+
68
+
69
+ def introspection():
70
+ return mispattributes
71
+
72
+
73
+ def version():
74
+ moduleinfo['config'] = moduleconfig
75
+ return moduleinfo
@@ -0,0 +1,152 @@
1
+ import json
2
+ import requests
3
+ from collections import defaultdict
4
+
5
+ misperrors = {'error': 'Error'}
6
+ mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst', 'md5', 'sha1', 'sha256', 'sha512'],
7
+ 'output': ['domain', 'ip-src', 'ip-dst', 'text', 'md5', 'sha1', 'sha256', 'sha512', 'ssdeep',
8
+ 'authentihash', 'filename', 'whois-registrant-email', 'url', 'link']
9
+ }
10
+
11
+ # possible module-types: 'expansion', 'hover' or both
12
+ moduleinfo = {
13
+ 'version': '1',
14
+ 'author': 'KX499',
15
+ 'description': 'Module to get information from ThreatMiner.',
16
+ 'module-type': ['expansion'],
17
+ 'name': 'ThreatMiner Lookup',
18
+ 'logo': 'threatminer.png',
19
+ 'requirements': [],
20
+ 'features': 'This module takes a MISP attribute as input and queries ThreatMiner with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute.',
21
+ 'references': ['https://www.threatminer.org/'],
22
+ 'input': 'A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512',
23
+ 'output': 'MISP attributes mapped from the result of the query on ThreatMiner, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- ssdeep\n- authentihash\n- filename\n- whois-registrant-email\n- url\n- link',
24
+ }
25
+
26
+
27
+ class ThreatMiner():
28
+ def __init__(self):
29
+ self.results = defaultdict(set)
30
+ self.comment = '{}: Threatminer - {}'
31
+ self.types_mapping = {'domain': '_get_domain', 'hostname': '_get_domain',
32
+ 'ip-dst': '_get_ip', 'ip-src': '_get_ip',
33
+ 'md5': '_get_hash', 'sha1': '_get_hash',
34
+ 'sha256': '_get_hash', 'sha512': '_get_hash'}
35
+
36
+ @property
37
+ def parsed_results(self):
38
+ to_return = []
39
+ for key, values in self.results.items():
40
+ if values:
41
+ input_value, comment = key[:2]
42
+ types = [k for k in key[2:]]
43
+ to_return.append({'types': types, 'values': list(values),
44
+ 'comment': self.comment.format(input_value, comment)})
45
+ return to_return
46
+
47
+ def parse_query(self, request):
48
+ for input_type, to_call in self.types_mapping.items():
49
+ if request.get(input_type):
50
+ getattr(self, to_call)(request[input_type])
51
+
52
+ def _get_domain(self, q):
53
+ queries_mapping = {1: ('_add_whois', 'whois'), 2: ('_add_ip', 'pdns'),
54
+ 3: ('_add_uri', 'uri'), 4: ('_add_hash', 'samples'),
55
+ 5: ('_add_domain', 'subdomain'), 6: ('_add_link', 'report')}
56
+ for flag, mapped in queries_mapping.items():
57
+ req = requests.get('https://www.threatminer.org/domain.php', params={'q': q, 'api': 'True', 'rt': flag})
58
+ if not req.status_code == 200:
59
+ continue
60
+ results = req.json().get('results')
61
+ if not results:
62
+ continue
63
+ to_call, comment = mapped
64
+ getattr(self, to_call)(results, q, comment)
65
+
66
+ def _get_hash(self, q):
67
+ queries_mapping = {1: ('_add_filename', 'file'), 3: ('_add_network', 'network'),
68
+ 6: ('_add_text', 'detection'), 7: ('_add_hash', 'report')}
69
+ for flag, mapped in queries_mapping.items():
70
+ req = requests.get('https://www.threatminer.org/sample.php', params={'q': q, 'api': 'True', 'rt': flag})
71
+ if not req.status_code == 200:
72
+ continue
73
+ results = req.json().get('results')
74
+ if not results:
75
+ continue
76
+ to_call, comment = mapped
77
+ getattr(self, to_call)(results, q, comment)
78
+
79
+ def _get_ip(self, q):
80
+ queries_mapping = {1: ('_add_whois', 'whois'), 2: ('_add_ip', 'pdns'),
81
+ 3: ('_add_uri', 'uri'), 4: ('_add_hash', 'samples'),
82
+ 5: ('_add_x509', 'ssl'), 6: ('_add_link', 'report')}
83
+ for flag, mapped in queries_mapping.items():
84
+ req = requests.get('https://www.threatminer.org/host.php', params={'q': q, 'api': 'True', 'rt': flag})
85
+ if not req.status_code == 200:
86
+ continue
87
+ results = req.json().get('results')
88
+ if not results:
89
+ continue
90
+ to_call, comment = mapped
91
+ getattr(self, to_call)(results, q, comment)
92
+
93
+ def _add_domain(self, results, q, comment):
94
+ self.results[(q, comment, 'domain')].update({result for result in results if isinstance(result, str)})
95
+
96
+ def _add_filename(self, results, q, comment):
97
+ self.results[(q, comment, 'filename')].update({result['file_name'] for result in results if result.get('file_name')})
98
+
99
+ def _add_hash(self, results, q, comment):
100
+ self.results[(q, comment, 'sha256')].update({result for result in results if isinstance(result, str)})
101
+
102
+ def _add_ip(self, results, q, comment):
103
+ self.results[(q, comment, 'ip-src', 'ip-dst')].update({result['ip'] for result in results if result.get('ip')})
104
+
105
+ def _add_link(self, results, q, comment):
106
+ self.results[(q, comment, 'link')].update({result['URL'] for result in results if result.get('URL')})
107
+
108
+ def _add_network(self, results, q, comment):
109
+ for result in results:
110
+ domains = result.get('domains')
111
+ if domains:
112
+ self.results[(q, comment, 'domain')].update({domain['domain'] for domain in domains if domain.get('domain')})
113
+ hosts = result.get('hosts')
114
+ if hosts:
115
+ self.results[(q, comment, 'ip-src', 'ip-dst')].update({host for host in hosts if isinstance(host, str)})
116
+
117
+ def _add_text(self, results, q, comment):
118
+ for result in results:
119
+ detections = result.get('av_detections')
120
+ if detections:
121
+ self.results[(q, comment, 'text')].update({d['detection'] for d in detections if d.get('detection')})
122
+
123
+ def _add_uri(self, results, q, comment):
124
+ self.results[(q, comment, 'url')].update({result['uri'] for result in results if result.get('uri')})
125
+
126
+ def _add_whois(self, results, q, comment):
127
+ for result in results:
128
+ emails = result.get('whois', {}).get('emails')
129
+ if emails:
130
+ self.results[(q, comment, 'whois-registrant-email')].update({email for em_type, email in emails.items() if em_type == 'registrant' and email})
131
+
132
+ def _add_x509(self, results, q, comment):
133
+ self.results[(q, comment, 'x509-fingerprint-sha1')].update({result for result in results if isinstance(result, str)})
134
+
135
+
136
+ def handler(q=False):
137
+ if q is False:
138
+ return False
139
+
140
+ q = json.loads(q)
141
+
142
+ parser = ThreatMiner()
143
+ parser.parse_query(q)
144
+ return {'results': parser.parsed_results}
145
+
146
+
147
+ def introspection():
148
+ return mispattributes
149
+
150
+
151
+ def version():
152
+ return moduleinfo
@@ -0,0 +1,119 @@
1
+ import json
2
+ import requests
3
+ import base64
4
+ import io
5
+ import zipfile
6
+
7
+ misperrors = {'error': 'Error'}
8
+ mispattributes = {'input': ['attachment', 'malware-sample', 'url'], 'output': ['link']}
9
+ moduleinfo = {
10
+ 'version': '1',
11
+ 'author': 'Karen Yousefi',
12
+ 'description': 'Module to submit samples to tria.ge',
13
+ 'module-type': ['expansion', 'hover'],
14
+ 'name': 'Triage Submit',
15
+ 'logo': '',
16
+ }
17
+
18
+ moduleconfig = ['apikey', 'url_mode']
19
+
20
+
21
+ def handler(q=False):
22
+ if q is False:
23
+ return False
24
+
25
+ request = json.loads(q)
26
+
27
+ if request.get('config', {}).get('apikey') is None:
28
+ misperrors['error'] = 'tria.ge API key is missing'
29
+ return misperrors
30
+
31
+ api_key = request['config']['apikey']
32
+ url_mode = request['config'].get('url_mode', 'submit') # 'submit' or 'fetch'
33
+ base_url = 'https://tria.ge/api/v0/samples'
34
+ headers = {'Authorization': f'Bearer {api_key}'}
35
+
36
+ if 'attachment' in request:
37
+ data = request['data']
38
+ filename = request['attachment']
39
+ return submit_file(headers, base_url, data, filename)
40
+ elif 'malware-sample' in request:
41
+ data = request['data']
42
+ filename = request['malware-sample'].split('|')[0]
43
+ return submit_file(headers, base_url, data, filename, is_malware_sample=True)
44
+ elif 'url' in request:
45
+ url = request['url']
46
+ return submit_url(headers, base_url, url, url_mode)
47
+ else:
48
+ misperrors['error'] = 'Unsupported input type'
49
+ return misperrors
50
+
51
+
52
+ def submit_file(headers, base_url, data, filename, is_malware_sample=False):
53
+ try:
54
+ if is_malware_sample:
55
+ file_data = base64.b64decode(data)
56
+ zip_file = zipfile.ZipFile(io.BytesIO(file_data))
57
+ file_data = zip_file.read(zip_file.namelist()[0], pwd=b'infected')
58
+ else:
59
+ file_data = base64.b64decode(data)
60
+
61
+ files = {'file': (filename, file_data)}
62
+ response = requests.post(base_url, headers=headers, files=files)
63
+ response.raise_for_status()
64
+ result = response.json()
65
+
66
+ sample_id = result['id']
67
+ sample_url = f'https://tria.ge/{sample_id}'
68
+
69
+ return {
70
+ 'results': [
71
+ {
72
+ 'types': 'link',
73
+ 'values': sample_url,
74
+ 'comment': 'Link to tria.ge analysis',
75
+ }
76
+ ]
77
+ }
78
+
79
+ except Exception as e:
80
+ misperrors['error'] = f'Error submitting to tria.ge: {str(e)}'
81
+ return misperrors
82
+
83
+
84
+ def submit_url(headers, base_url, url, mode):
85
+ try:
86
+ if mode == 'fetch':
87
+ data = {'kind': 'fetch', 'url': url}
88
+ else: # submit
89
+ data = {'kind': 'url', 'url': url}
90
+
91
+ response = requests.post(base_url, headers=headers, json=data)
92
+ response.raise_for_status()
93
+ result = response.json()
94
+
95
+ sample_id = result['id']
96
+ sample_url = f'https://tria.ge/{sample_id}'
97
+
98
+ return {
99
+ 'results': [
100
+ {
101
+ 'types': 'link',
102
+ 'values': sample_url,
103
+ 'comment': f'Link to tria.ge analysis ({mode} mode)',
104
+ }
105
+ ]
106
+ }
107
+
108
+ except Exception as e:
109
+ misperrors['error'] = f'Error submitting to tria.ge: {str(e)}'
110
+ return misperrors
111
+
112
+
113
+ def introspection():
114
+ return mispattributes
115
+
116
+
117
+ def version():
118
+ moduleinfo['config'] = moduleconfig
119
+ return moduleinfo