Skip to content

Commit 4f85bdf

Browse files
Merge branch 'release/3.5.13'
2 parents c03305f + 12e1d9e commit 4f85bdf

File tree

4 files changed

+110
-202
lines changed

4 files changed

+110
-202
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## [3.5.12](https://github.com/TheHive-Project/Cortex-Analyzers/tree/3.5.12) (2025-05-07)
4+
5+
[Full Changelog](https://github.com/TheHive-Project/Cortex-Analyzers/compare/3.5.11...3.5.12)
6+
7+
**Merged pull requests:**
8+
9+
- New Analyzer & Responders Watcher [\#1353](https://github.com/TheHive-Project/Cortex-Analyzers/pull/1353) ([ygalnezri](https://github.com/ygalnezri))
10+
311
## [3.5.11](https://github.com/TheHive-Project/Cortex-Analyzers/tree/3.5.11) (2025-04-17)
412

513
[Full Changelog](https://github.com/TheHive-Project/Cortex-Analyzers/compare/3.5.10...3.5.11)

analyzers/Malwares/Malwares_Scan.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
66
"license": "AGPL-V3",
77
"description": "Use Malwares' API to scan a file or URL.",
8-
"dataTypeList": ["file", "url"],
8+
"dataTypeList": ["file"],
99
"baseConfig": "Malwares",
1010
"config": {
1111
"check_tlp": true,

analyzers/Malwares/malwares.py

Lines changed: 59 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,109 @@
11
#!/usr/bin/env python3
22
# encoding: utf-8
3+
34
import time
45
import hashlib
56

67
from malwares_api import Api
78
from cortexutils.analyzer import Analyzer
89

9-
from io import StringIO
10-
1110

1211
class MalwaresAnalyzer(Analyzer):
13-
1412
def __init__(self):
1513
Analyzer.__init__(self)
16-
self.service = self.get_param('config.service', None, 'Service parameter is missing')
17-
self.key = self.get_param('config.key', None, 'Missing Malware API key')
18-
self.polling_interval = self.get_param('config.polling_interval', 60)
14+
self.service = self.get_param(
15+
"config.service", None, "Service parameter is missing"
16+
)
17+
self.key = self.get_param("config.key", None, "Missing Malware API key")
18+
self.polling_interval = self.get_param("config.polling_interval", 60)
1919
self.m_api = Api(self.key)
2020

21+
def check_response(self, response):
22+
if type(response) is not dict:
23+
self.error("Bad response : " + str(response))
24+
status = response.get("response_code", 500)
25+
if status == 429:
26+
self.error("Malwares api rate limit exceeded.")
27+
elif status >= 400:
28+
self.error("Bad status : " + str(status))
29+
return response.get("results", {})
30+
2131
def wait_file_report(self, id):
22-
results = self.check_response(self.m_api.get_file_report(id))
23-
code = results.get('result_code', None)
24-
if code == 1:
32+
response = self.m_api.get_file_report(id)
33+
results = self.check_response(response)
34+
status = response.get("response_code", 500)
35+
if status == 200:
2536
self.report(results)
2637
else:
2738
time.sleep(self.polling_interval)
2839
self.wait_file_report(id)
2940

30-
def wait_url_report(self, id):
31-
results = self.check_response(self.m_api.get_url_report(id))
32-
code = results.get('result_code', None)
33-
if code == 1:
34-
self.report(results)
35-
else:
36-
time.sleep(self.polling_interval)
37-
self.wait_url_report(id)
38-
39-
def check_response(self, response):
40-
if type(response) is not dict:
41-
self.error('Bad response : ' + str(response))
42-
status = response.get('response_code', -1)
43-
if status in (-14, -15):
44-
self.error('Malwares api rate limit exceeded.')
45-
elif int(status) < 1:
46-
self.error('Bad status : ' + str(status))
47-
results = response.get('results', {})
48-
return results
49-
50-
# 0 => not found
51-
# -2 => in queue
52-
# 1 => ready
53-
5441
def read_scan_response(self, response, func):
5542
results = self.check_response(response)
56-
code = results.get('result_code', None)
57-
md5 = results.get('md5', None)
58-
url = results.get('url', None)
59-
if code in (1, 2) and md5 is not None:
60-
func(md5)
61-
elif code in (1, 2) and url is not None:
62-
func(url)
43+
status = response.get("response_code", 500)
44+
sha256 = results.get("ctx_data", {}).get("sha256", None)
45+
46+
if status in (200, 202) and sha256 is not None:
47+
func(sha256)
6348
else:
64-
self.error('%d %s %s - Scan not found' % (code, md5, url))
49+
self.error("%d %s - Scan not found" % (status, sha256))
6550

6651
def summary(self, raw):
6752
taxonomies = []
6853
level = "info"
6954
namespace = "Malwares"
70-
predicate = "Score"
71-
value = "No info"
72-
score = -1
73-
74-
result = {
75-
"has_result": True
76-
}
77-
78-
if "ai_score" in raw.keys():
79-
score = raw["ai_score"]
80-
if score < 10:
81-
level = "safe"
82-
elif 10 <= score < 30:
83-
level = "suspicious"
84-
else:
85-
level = "malicious"
86-
87-
result['score'] = score
8855

89-
value = "{}/100".format(score)
56+
ctx_data = raw.get("ctx_data", {})
57+
detect = ctx_data.get("detect", "malicious")
58+
tags = raw.get("ctx_data", {}).get("tags", [])
9059

60+
if detect == "normal":
61+
level = "safe"
9162
else:
92-
if "detected_communicating_file" in raw.keys() or "detected_url" in raw.keys() or "detected_downloaded_file" in raw.keys():
93-
score = max(
94-
raw.get("detected_communicating_file", {}).get("total", 0),
95-
raw.get("detected_url", {}).get("total", 0),
96-
raw.get("detected_downloaded_file", {}).get("total", 0)
97-
)
98-
value = "{} results".format(score)
99-
100-
elif "virustotal" in raw.keys():
101-
score = raw.get("virustotal", {}).get("positives", 0)
102-
total = raw.get("virustotal", {}).get("total", 0)
103-
value = "{}/{} positives".format(score, total)
104-
105-
if score == 0:
106-
level = "safe"
107-
elif 0 < score <= 5:
108-
level = "suspicious"
109-
elif score > 5:
110-
level = "malicious"
111-
112-
taxonomies.append(self.build_taxonomy(
113-
level, namespace, predicate, value))
63+
level = "malicious"
64+
65+
taxonomies.append(self.build_taxonomy(level, namespace, "Detect", detect))
66+
taxonomies.append(self.build_taxonomy(level, namespace, "Tags", tags))
67+
11468
return {"taxonomies": taxonomies}
11569

11670
def run(self):
117-
if self.service == 'scan':
118-
if self.data_type == 'file':
119-
filename = self.get_param('filename', 'noname.ext')
120-
filepath = self.get_param('file', None, 'File is missing')
121-
self.read_scan_response(self.m_api.scan_file(
122-
open(filepath, 'rb'), filename), self.wait_file_report)
123-
elif self.data_type == 'url':
124-
data = self.get_param('data', None, 'Data is missing')
71+
if self.service == "scan":
72+
if self.data_type == "file":
73+
filename = self.get_param("filename", "noname.ext")
74+
filepath = self.get_param("file", None, "File is missing")
12575
self.read_scan_response(
126-
self.m_api.scan_url(data), self.wait_url_report)
76+
self.m_api.scan_file(filepath, filename), self.wait_file_report
77+
)
12778
else:
128-
self.error('Invalid data type')
129-
elif self.service == 'get':
130-
if self.data_type == 'domain':
131-
data = self.get_param('data', None, 'Data is missing')
132-
self.report(self.check_response(
133-
self.m_api.get_domain_report(data)))
134-
elif self.data_type == 'ip':
135-
data = self.get_param('data', None, 'Data is missing')
79+
self.error("Invalid data type")
80+
81+
elif self.service == "get":
82+
if self.data_type == "domain":
83+
data = self.get_param("data", None, "Data is missing")
84+
self.report(self.check_response(self.m_api.get_domain_report(data)))
85+
elif self.data_type == "ip":
86+
data = self.get_param("data", None, "Data is missing")
13687
self.report(self.check_response(self.m_api.get_ip_report(data)))
137-
elif self.data_type == 'file':
138-
139-
hashes = self.get_param('attachment.hashes',
140-
None)
88+
elif self.data_type == "file":
89+
hashes = self.get_param("attachment.hashes", None)
14190
if hashes is None:
142-
filepath = self.get_param('file', None, 'File is missing')
143-
hash = hashlib.sha256(open(filepath, 'rb').read()).hexdigest();
91+
filepath = self.get_param("file", None, "File is missing")
92+
hash = hashlib.sha256(open(filepath, "rb").read()).hexdigest()
14493
else:
14594
# find SHA256 hash
14695
hash = next(h for h in hashes if len(h) == 64)
14796

14897
self.report(self.check_response(self.m_api.get_file_report(hash)))
14998

150-
elif self.data_type == 'hash':
151-
data = self.get_param('data', None, 'Data is missing')
99+
elif self.data_type == "hash":
100+
data = self.get_param("data", None, "Data is missing")
152101
self.report(self.check_response(self.m_api.get_file_report(data)))
153102
else:
154-
self.error('Invalid data type')
103+
self.error("Invalid data type")
155104
else:
156-
self.error('Invalid service')
105+
self.error("Invalid service")
157106

158107

159-
if __name__ == '__main__':
108+
if __name__ == "__main__":
160109
MalwaresAnalyzer().run()

0 commit comments

Comments
 (0)