Skip to content

Commit 59b0505

Browse files
committed
Adjustment of TLP, PAP, Severity, and Observable Tags in TheHive
- Adjusted TLP and PAP values to 'Green' and changed 'Severity' to 'Low' in TheHive. - Removed irrelevant observables for the 'DNS Finder' module, which were not useful for analysts. - Added tags to observables to provide context and facilitate the search and linkage with other alert or case data. For example, the alert type for the 'Website Monitoring' module was added as a tag in the observables for that module.
1 parent 0aae703 commit 59b0505

File tree

5 files changed

+116
-45
lines changed

5 files changed

+116
-45
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ THE_HIVE_KEY=
4848
# Ensure the custom field referenced here is CREATED IN THEHIVE. Otherwise, Alert exports to TheHive will be impacted
4949
THE_HIVE_CUSTOM_FIELD=watcher-id
5050
THE_HIVE_EMAIL_SENDER=[email protected]
51+
THE_HIVE_TAGS=Watcher,Impersonation,Malicious Domain,Typosquatting
5152

5253
# MISP Setup
5354
MISP_URL=

Watcher/Watcher/common/core.py

+52-25
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,10 @@ def generate_ref():
205205
"**The following trendy words were detected:**\n"
206206
"{words_list}"
207207
),
208-
'severity': 2,
209-
'tags': ["Threats Watcher", "Watcher", "Buzzword", "Trendy Words", "Threat Detection"]
208+
'severity': 1,
209+
'tlp': 1,
210+
'pap': 1,
211+
'tags': settings.THE_HIVE_TAGS
210212
},
211213
'data_leak': {
212214
'title': "New Data Leakage - Alert #{alert_pk} for {keyword_name} keyword",
@@ -216,8 +218,10 @@ def generate_ref():
216218
"*Keyword:* {keyword_name}\n"
217219
"*Source:* {url}\n"
218220
),
219-
'severity': 3,
220-
'tags': ["Data Leak", "Watcher", "Sensitive Data", "Leak Detection"]
221+
'severity': 1,
222+
'tlp': 1,
223+
'pap': 1,
224+
'tags': settings.THE_HIVE_TAGS
221225
},
222226
'website_monitoring': {
223227
'title': "Website Monitoring Detected - {alert_type} on {domain_name_sanitized}",
@@ -236,8 +240,10 @@ def generate_ref():
236240
"*• New Mail Server:* {new_mail_A_record_ip}\n"
237241
"*• Old Mail Server:* {old_mail_A_record_ip}\n"
238242
),
239-
'severity': 2,
240-
'tags': ["Website Monitoring", "Watcher", "Incident", "Website", "Domain Name", "Impersonation" , "Malicious Domain", "Typosquatting"]
243+
'severity': 1,
244+
'tlp': 1,
245+
'pap': 1,
246+
'tags': settings.THE_HIVE_TAGS
241247
},
242248
'dns_finder': {
243249
'title': "New Twisted DNS found - {dns_domain_name_sanitized}",
@@ -249,8 +255,10 @@ def generate_ref():
249255
"*Corporate DNS:* {alert.dns_twisted.dns_monitored}\n"
250256
"*Fuzzer:* {alert.dns_twisted.fuzzer}\n"
251257
),
252-
'severity': 3,
253-
'tags': ["DNS Finder", "Watcher", "Twisted DNS", "Corporate Keywords", "Corporate DNS Assets", "Impersonation" , "Malicious Domain", "Typosquatting"]
258+
'severity': 1,
259+
'tlp': 1,
260+
'pap': 1,
261+
'tags': settings.THE_HIVE_TAGS
254262
},
255263
}
256264

@@ -306,41 +314,56 @@ def collect_observables(app_name, context_data):
306314
elif app_name == 'website_monitoring':
307315
site = context_data.get('site')
308316
alert_data = context_data.get('alert_data', {})
317+
alert_type = alert_data.get('type')
309318
if site:
310-
observables.append({"dataType": "domain", "data": site.domain_name})
319+
domain_tag = f"domain_name:{site.domain_name}"
320+
observable = {"dataType": "domain", "data": site.domain_name, "tags": [domain_tag]}
321+
observables.append(observable)
311322
if alert_data.get('new_ip'):
312-
observables.append({"dataType": "ip", "data": alert_data['new_ip']})
323+
observable = {"dataType": "ip", "data": alert_data['new_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:new_ip"]}
324+
observables.append(observable)
313325
if alert_data.get('old_ip'):
314-
observables.append({"dataType": "ip", "data": alert_data['old_ip']})
326+
observable = {"dataType": "ip", "data": alert_data['old_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:old_ip"]}
327+
observables.append(observable)
315328
if alert_data.get('new_ip_second'):
316-
observables.append({"dataType": "ip", "data": alert_data['new_ip_second']})
329+
observable = {"dataType": "ip", "data": alert_data['new_ip_second'], "tags": [domain_tag, f"type:{alert_type}", "details:new_ip_second"]}
330+
observables.append(observable)
317331
if alert_data.get('old_ip_second'):
318-
observables.append({"dataType": "ip", "data": alert_data['old_ip_second']})
332+
observable = {"dataType": "ip", "data": alert_data['old_ip_second'], "tags": [domain_tag, f"type:{alert_type}", "details:old_ip_second"]}
333+
observables.append(observable)
319334
if alert_data.get('new_MX_records'):
320-
observables.append({"dataType": "other", "data": alert_data['new_MX_records']})
335+
observable = {"dataType": "other", "data": alert_data['new_MX_records'], "tags": [domain_tag, f"type:{alert_type}", "details:new_MX_records"]}
336+
observables.append(observable)
321337
if alert_data.get('old_MX_records'):
322-
observables.append({"dataType": "other", "data": alert_data['old_MX_records']})
338+
observable = {"dataType": "other", "data": alert_data['old_MX_records'], "tags": [domain_tag, f"type:{alert_type}", "details:old_MX_records"]}
339+
observables.append(observable)
323340
if alert_data.get('new_mail_A_record_ip'):
324-
observables.append({"dataType": "ip", "data": alert_data['new_mail_A_record_ip']})
341+
observable = {"dataType": "ip", "data": alert_data['new_mail_A_record_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:new_mail_A_record_ip"]}
342+
observables.append(observable)
325343
if alert_data.get('old_mail_A_record_ip'):
326-
observables.append({"dataType": "ip", "data": alert_data['old_mail_A_record_ip']})
344+
observable = {"dataType": "ip", "data": alert_data['old_mail_A_record_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:old_mail_A_record_ip"]}
345+
observables.append(observable)
327346

328347
elif app_name == 'data_leak':
329348
alert = context_data.get('alert')
330349
if alert:
331-
observables.append({"dataType": "url", "data": alert.url})
332-
observables.append({"dataType": "other", "data": alert.keyword.name})
350+
observable = {"dataType": "url", "data": alert.url, "tags": []}
351+
if alert.keyword:
352+
observable["tags"].append(f"keyword:{alert.keyword.name}")
353+
observables.append(observable)
333354

334355
elif app_name == 'dns_finder':
335356
alert = context_data.get('alert')
336357
if alert:
337-
observables.append({"dataType": "domain", "data": alert.dns_twisted.domain_name})
338-
if alert.dns_twisted.keyword_monitored:
339-
observables.append({"dataType": "other", "data": alert.dns_twisted.keyword_monitored.name})
340-
if alert.dns_twisted.dns_monitored:
341-
observables.append({"dataType": "domain", "data": alert.dns_twisted.dns_monitored.domain_name})
358+
observable = {"dataType": "domain", "data": alert.dns_twisted.domain_name, "tags": []}
342359
if alert.dns_twisted.fuzzer:
343-
observables.append({"dataType": "other", "data": alert.dns_twisted.fuzzer})
360+
observable["tags"].append(f"fuzzer:{alert.dns_twisted.fuzzer}")
361+
if alert.dns_twisted.dns_monitored:
362+
observable["tags"].append(f"corporate_dns:{alert.dns_twisted.dns_monitored.domain_name}")
363+
if alert.dns_twisted.keyword_monitored:
364+
observable["tags"].append(f"corporate_keyword:{alert.dns_twisted.keyword_monitored.name}")
365+
366+
observables.append(observable)
344367

345368
observables = [observable for observable in observables if observable['data'] is not None and observable['data'] != 'None']
346369

@@ -513,6 +536,8 @@ def send_notification(channel, content_template, subscribers_filter, send_func,
513536
title=formatted_title,
514537
description=content,
515538
severity=app_config_thehive['severity'],
539+
tlp=app_config_thehive['tlp'],
540+
pap=app_config_thehive['pap'],
516541
tags=app_config_thehive['tags'],
517542
customFields=app_config_thehive.get('customFields'),
518543
app_name=app_name,
@@ -527,6 +552,8 @@ def send_notification(channel, content_template, subscribers_filter, send_func,
527552
title=formatted_title,
528553
description=app_config_thehive['description_template'].format(**common_data),
529554
severity=app_config_thehive['severity'],
555+
tlp=app_config_thehive['tlp'],
556+
pap=app_config_thehive['pap'],
530557
tags=app_config_thehive['tags'],
531558
app_name=app_name,
532559
domain_name=None,

Watcher/Watcher/common/utils/send_thehive_alerts.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,18 @@ def post_to_thehive(url, data, headers, proxies):
4343
return None
4444

4545

46-
def send_thehive_alert(title, description, severity, tags, app_name, domain_name, observables=None, customFields=None, thehive_url=None, api_key=None):
46+
def send_thehive_alert(title, description, severity, tlp, pap, tags, app_name, domain_name, observables=None, customFields=None, thehive_url=None, api_key=None):
4747
from common.core import generate_ref
4848
"""
4949
Send or update an alert in TheHive based on the application and ticket_id.
5050
5151
:param title: The title of the alert.
5252
:param description: The description of the alert.
5353
:param severity: The severity level of the alert (integer).
54+
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
55+
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
5456
:param tags: A list of tags associated with the alert.
55-
:param app_name: The application triggering the alert (e.g., 'website_monitoring').
57+
:param app_name: The application triggering the alert.
5658
:param domain_name: The domain name related to the alert (used for ticket_id lookup).
5759
:param observables: Any observables (default is None).
5860
:param customFields: Custom fields for the alert (default is None).
@@ -95,6 +97,8 @@ def send_thehive_alert(title, description, severity, tags, app_name, domain_name
9597
title=title,
9698
description=description,
9799
severity=severity,
100+
tlp=tlp,
101+
pap=pap,
98102
tags=tags,
99103
app_name=app_name,
100104
observables=observables,
@@ -114,6 +118,8 @@ def send_thehive_alert(title, description, severity, tags, app_name, domain_name
114118
title=title,
115119
description=description,
116120
severity=severity,
121+
tlp=tlp,
122+
pap=pap,
117123
tags=tags,
118124
app_name=app_name,
119125
observables=observables,

Watcher/Watcher/common/utils/update_thehive.py

+27-5
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,31 @@ def create_observables(observables):
106106

107107
observables_data = []
108108
for obs in observables:
109+
tag_info = []
110+
if 'tags' in obs:
111+
for tag in obs['tags']:
112+
tag_parts = tag.split(':')
113+
if len(tag_parts) == 2:
114+
tag_info.append(f"*{tag_parts[0]}:* {tag_parts[1]}")
115+
116+
tags_message = "\n".join(tag_info) if tag_info else "No tags"
117+
message = f"**More information(s)**:\n{tags_message}"
118+
109119
observable_data = {
110120
"dataType": obs['dataType'],
111121
"data": obs['data'],
112-
"message": f"An observable was added on {current_date} at {current_time}.",
122+
"message": message,
113123
"ioc": True,
114124
"sighted": True,
115-
"tlp": 2
125+
"tlp": 1,
126+
"pap": 1
116127
}
128+
129+
if 'tags' in obs:
130+
observable_data['tags'] = obs['tags']
131+
117132
observables_data.append(observable_data)
133+
118134
return observables_data
119135

120136

@@ -140,7 +156,7 @@ def update_existing_alert_case(item_type, existing_item, observables, comment, t
140156
add_comment_to_item(item_type, item_id, comment, thehive_url, api_key)
141157

142158

143-
def create_new_alert(ticket_id, title, description, severity, tags, app_name, observables, customFields, comment, thehive_url, api_key):
159+
def create_new_alert(ticket_id, title, description, severity, tlp, pap, tags, app_name, observables, customFields, comment, thehive_url, api_key):
144160
from common.core import generate_ref
145161
"""
146162
Create a new alert in TheHive with the provided details.
@@ -149,6 +165,8 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
149165
:param title: The title of the alert.
150166
:param description: The description of the alert.
151167
:param severity: The severity level of the alert (integer).
168+
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
169+
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
152170
:param tags: A list of tags associated with the alert.
153171
:param app_name: The application triggering the alert.
154172
:param observables: A list of observables to associate with the alert.
@@ -166,6 +184,8 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
166184
"title": title,
167185
"description": description,
168186
"severity": severity,
187+
"tlp": tlp,
188+
"pap": pap,
169189
"tags": tags,
170190
"type": app_name,
171191
"source": "watcher",
@@ -201,7 +221,7 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
201221
return None
202222

203223

204-
def handle_alert_or_case(ticket_id, observables, comment, title, description, severity, tags, app_name, customFields, thehive_url, api_key):
224+
def handle_alert_or_case(ticket_id, observables, comment, title, description, severity, tlp, pap, tags, app_name, customFields, thehive_url, api_key):
205225
"""
206226
Handle the creation or updating of alerts and cases in TheHive.
207227
@@ -211,6 +231,8 @@ def handle_alert_or_case(ticket_id, observables, comment, title, description, se
211231
:param title: The title for the alert.
212232
:param description: The description for the alert.
213233
:param severity: The severity of the alert (integer).
234+
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
235+
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
214236
:param tags: A list of tags for the alert.
215237
:param app_name: The name of the application triggering the alert.
216238
:param customFields: Custom fields to be included in the alert.
@@ -235,6 +257,6 @@ def handle_alert_or_case(ticket_id, observables, comment, title, description, se
235257
else:
236258
create_new_alert(
237259
ticket_id=ticket_id, title=title, description=description, severity=severity,
238-
tags=tags, app_name=app_name, observables=observables,
260+
tlp=tlp, pap=pap, tags=tags, app_name=app_name, observables=observables,
239261
customFields=customFields, comment=comment, thehive_url=thehive_url, api_key=api_key
240262
)

Watcher/Watcher/watcher/settings.py

+28-13
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
THE_HIVE_KEY = os.environ.get('THE_HIVE_KEY', '')
127127
THE_HIVE_CUSTOM_FIELD = os.environ.get('THE_HIVE_CUSTOM_FIELD', 'watcher-id')
128128
THE_HIVE_EMAIL_SENDER = os.environ.get('THE_HIVE_EMAIL_SENDER', '[email protected]')
129+
THE_HIVE_TAGS = os.environ.get('THE_HIVE_TAGS', "Watcher,Impersonation,Malicious Domain,Typosquatting").split(",")
129130

130131
# MISP Setup
131132
MISP_URL = os.environ.get('MISP_URL', 'https://127.0.0.1')
@@ -214,20 +215,34 @@
214215
# Database
215216
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
216217
# SECURITY WARNING: In production please set DB_USER and DB_PASSWORD environment variables in the .env file.
218+
# DATABASES = {
219+
# 'default': {
220+
# 'ENGINE': 'django.db.backends.mysql',
221+
# 'CONN_MAX_AGE': 3600,
222+
# 'NAME': 'db_watcher',
223+
# 'USER': os.environ.get('DB_USER', 'watcher'),
224+
# 'PASSWORD': os.environ.get('DB_PASSWORD', 'Ee5kZm4fWWAmE9hs'),
225+
# 'HOST': 'db_watcher',
226+
# 'PORT': '3306',
227+
# 'OPTIONS': {
228+
# 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
229+
# "charset": "utf8mb4",
230+
# },
231+
# }
232+
# }
217233
DATABASES = {
218-
'default': {
219-
'ENGINE': 'django.db.backends.mysql',
220-
'CONN_MAX_AGE': 3600,
221-
'NAME': 'db_watcher',
222-
'USER': os.environ.get('DB_USER', 'watcher'),
223-
'PASSWORD': os.environ.get('DB_PASSWORD', 'Ee5kZm4fWWAmE9hs'),
224-
'HOST': 'db_watcher',
225-
'PORT': '3306',
226-
'OPTIONS': {
227-
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
228-
"charset": "utf8mb4",
229-
},
230-
}
234+
'default': {
235+
'ENGINE': 'django.db.backends.mysql',
236+
'CONN_MAX_AGE': 3600,
237+
'NAME': 'db_watcher3',
238+
'USER': 'watcher',
239+
'PASSWORD': 'Ee5kZm4fWWAmE9hs!',
240+
'HOST': 'localhost',
241+
'PORT': '3306',
242+
'OPTIONS': {
243+
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
244+
},
245+
}
231246
}
232247

233248
# Password validation

0 commit comments

Comments
 (0)