Skip to content

Commit

Permalink
Adjustment of TLP, PAP, Severity, and Observable Tags in TheHive
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
ygalnezri committed Jan 16, 2025
1 parent 0aae703 commit 59b0505
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 45 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ THE_HIVE_KEY=
# Ensure the custom field referenced here is CREATED IN THEHIVE. Otherwise, Alert exports to TheHive will be impacted
THE_HIVE_CUSTOM_FIELD=watcher-id
THE_HIVE_EMAIL_SENDER=watcher@watcher.com
THE_HIVE_TAGS=Watcher,Impersonation,Malicious Domain,Typosquatting

# MISP Setup
MISP_URL=
Expand Down
77 changes: 52 additions & 25 deletions Watcher/Watcher/common/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,10 @@ def generate_ref():
"**The following trendy words were detected:**\n"
"{words_list}"
),
'severity': 2,
'tags': ["Threats Watcher", "Watcher", "Buzzword", "Trendy Words", "Threat Detection"]
'severity': 1,
'tlp': 1,
'pap': 1,
'tags': settings.THE_HIVE_TAGS
},
'data_leak': {
'title': "New Data Leakage - Alert #{alert_pk} for {keyword_name} keyword",
Expand All @@ -216,8 +218,10 @@ def generate_ref():
"*Keyword:* {keyword_name}\n"
"*Source:* {url}\n"
),
'severity': 3,
'tags': ["Data Leak", "Watcher", "Sensitive Data", "Leak Detection"]
'severity': 1,
'tlp': 1,
'pap': 1,
'tags': settings.THE_HIVE_TAGS
},
'website_monitoring': {
'title': "Website Monitoring Detected - {alert_type} on {domain_name_sanitized}",
Expand All @@ -236,8 +240,10 @@ def generate_ref():
"*• New Mail Server:* {new_mail_A_record_ip}\n"
"*• Old Mail Server:* {old_mail_A_record_ip}\n"
),
'severity': 2,
'tags': ["Website Monitoring", "Watcher", "Incident", "Website", "Domain Name", "Impersonation" , "Malicious Domain", "Typosquatting"]
'severity': 1,
'tlp': 1,
'pap': 1,
'tags': settings.THE_HIVE_TAGS
},
'dns_finder': {
'title': "New Twisted DNS found - {dns_domain_name_sanitized}",
Expand All @@ -249,8 +255,10 @@ def generate_ref():
"*Corporate DNS:* {alert.dns_twisted.dns_monitored}\n"
"*Fuzzer:* {alert.dns_twisted.fuzzer}\n"
),
'severity': 3,
'tags': ["DNS Finder", "Watcher", "Twisted DNS", "Corporate Keywords", "Corporate DNS Assets", "Impersonation" , "Malicious Domain", "Typosquatting"]
'severity': 1,
'tlp': 1,
'pap': 1,
'tags': settings.THE_HIVE_TAGS
},
}

Expand Down Expand Up @@ -306,41 +314,56 @@ def collect_observables(app_name, context_data):
elif app_name == 'website_monitoring':
site = context_data.get('site')
alert_data = context_data.get('alert_data', {})
alert_type = alert_data.get('type')
if site:
observables.append({"dataType": "domain", "data": site.domain_name})
domain_tag = f"domain_name:{site.domain_name}"
observable = {"dataType": "domain", "data": site.domain_name, "tags": [domain_tag]}
observables.append(observable)
if alert_data.get('new_ip'):
observables.append({"dataType": "ip", "data": alert_data['new_ip']})
observable = {"dataType": "ip", "data": alert_data['new_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:new_ip"]}
observables.append(observable)
if alert_data.get('old_ip'):
observables.append({"dataType": "ip", "data": alert_data['old_ip']})
observable = {"dataType": "ip", "data": alert_data['old_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:old_ip"]}
observables.append(observable)
if alert_data.get('new_ip_second'):
observables.append({"dataType": "ip", "data": alert_data['new_ip_second']})
observable = {"dataType": "ip", "data": alert_data['new_ip_second'], "tags": [domain_tag, f"type:{alert_type}", "details:new_ip_second"]}
observables.append(observable)
if alert_data.get('old_ip_second'):
observables.append({"dataType": "ip", "data": alert_data['old_ip_second']})
observable = {"dataType": "ip", "data": alert_data['old_ip_second'], "tags": [domain_tag, f"type:{alert_type}", "details:old_ip_second"]}
observables.append(observable)
if alert_data.get('new_MX_records'):
observables.append({"dataType": "other", "data": alert_data['new_MX_records']})
observable = {"dataType": "other", "data": alert_data['new_MX_records'], "tags": [domain_tag, f"type:{alert_type}", "details:new_MX_records"]}
observables.append(observable)
if alert_data.get('old_MX_records'):
observables.append({"dataType": "other", "data": alert_data['old_MX_records']})
observable = {"dataType": "other", "data": alert_data['old_MX_records'], "tags": [domain_tag, f"type:{alert_type}", "details:old_MX_records"]}
observables.append(observable)
if alert_data.get('new_mail_A_record_ip'):
observables.append({"dataType": "ip", "data": alert_data['new_mail_A_record_ip']})
observable = {"dataType": "ip", "data": alert_data['new_mail_A_record_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:new_mail_A_record_ip"]}
observables.append(observable)
if alert_data.get('old_mail_A_record_ip'):
observables.append({"dataType": "ip", "data": alert_data['old_mail_A_record_ip']})
observable = {"dataType": "ip", "data": alert_data['old_mail_A_record_ip'], "tags": [domain_tag, f"type:{alert_type}", "details:old_mail_A_record_ip"]}
observables.append(observable)

elif app_name == 'data_leak':
alert = context_data.get('alert')
if alert:
observables.append({"dataType": "url", "data": alert.url})
observables.append({"dataType": "other", "data": alert.keyword.name})
observable = {"dataType": "url", "data": alert.url, "tags": []}
if alert.keyword:
observable["tags"].append(f"keyword:{alert.keyword.name}")
observables.append(observable)

elif app_name == 'dns_finder':
alert = context_data.get('alert')
if alert:
observables.append({"dataType": "domain", "data": alert.dns_twisted.domain_name})
if alert.dns_twisted.keyword_monitored:
observables.append({"dataType": "other", "data": alert.dns_twisted.keyword_monitored.name})
if alert.dns_twisted.dns_monitored:
observables.append({"dataType": "domain", "data": alert.dns_twisted.dns_monitored.domain_name})
observable = {"dataType": "domain", "data": alert.dns_twisted.domain_name, "tags": []}
if alert.dns_twisted.fuzzer:
observables.append({"dataType": "other", "data": alert.dns_twisted.fuzzer})
observable["tags"].append(f"fuzzer:{alert.dns_twisted.fuzzer}")
if alert.dns_twisted.dns_monitored:
observable["tags"].append(f"corporate_dns:{alert.dns_twisted.dns_monitored.domain_name}")
if alert.dns_twisted.keyword_monitored:
observable["tags"].append(f"corporate_keyword:{alert.dns_twisted.keyword_monitored.name}")

observables.append(observable)

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

Expand Down Expand Up @@ -513,6 +536,8 @@ def send_notification(channel, content_template, subscribers_filter, send_func,
title=formatted_title,
description=content,
severity=app_config_thehive['severity'],
tlp=app_config_thehive['tlp'],
pap=app_config_thehive['pap'],
tags=app_config_thehive['tags'],
customFields=app_config_thehive.get('customFields'),
app_name=app_name,
Expand All @@ -527,6 +552,8 @@ def send_notification(channel, content_template, subscribers_filter, send_func,
title=formatted_title,
description=app_config_thehive['description_template'].format(**common_data),
severity=app_config_thehive['severity'],
tlp=app_config_thehive['tlp'],
pap=app_config_thehive['pap'],
tags=app_config_thehive['tags'],
app_name=app_name,
domain_name=None,
Expand Down
10 changes: 8 additions & 2 deletions Watcher/Watcher/common/utils/send_thehive_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,18 @@ def post_to_thehive(url, data, headers, proxies):
return None


def send_thehive_alert(title, description, severity, tags, app_name, domain_name, observables=None, customFields=None, thehive_url=None, api_key=None):
def send_thehive_alert(title, description, severity, tlp, pap, tags, app_name, domain_name, observables=None, customFields=None, thehive_url=None, api_key=None):
from common.core import generate_ref
"""
Send or update an alert in TheHive based on the application and ticket_id.
:param title: The title of the alert.
:param description: The description of the alert.
:param severity: The severity level of the alert (integer).
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
:param tags: A list of tags associated with the alert.
:param app_name: The application triggering the alert (e.g., 'website_monitoring').
:param app_name: The application triggering the alert.
:param domain_name: The domain name related to the alert (used for ticket_id lookup).
:param observables: Any observables (default is None).
:param customFields: Custom fields for the alert (default is None).
Expand Down Expand Up @@ -95,6 +97,8 @@ def send_thehive_alert(title, description, severity, tags, app_name, domain_name
title=title,
description=description,
severity=severity,
tlp=tlp,
pap=pap,
tags=tags,
app_name=app_name,
observables=observables,
Expand All @@ -114,6 +118,8 @@ def send_thehive_alert(title, description, severity, tags, app_name, domain_name
title=title,
description=description,
severity=severity,
tlp=tlp,
pap=pap,
tags=tags,
app_name=app_name,
observables=observables,
Expand Down
32 changes: 27 additions & 5 deletions Watcher/Watcher/common/utils/update_thehive.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,31 @@ def create_observables(observables):

observables_data = []
for obs in observables:
tag_info = []
if 'tags' in obs:
for tag in obs['tags']:
tag_parts = tag.split(':')
if len(tag_parts) == 2:
tag_info.append(f"*{tag_parts[0]}:* {tag_parts[1]}")

tags_message = "\n".join(tag_info) if tag_info else "No tags"
message = f"**More information(s)**:\n{tags_message}"

observable_data = {
"dataType": obs['dataType'],
"data": obs['data'],
"message": f"An observable was added on {current_date} at {current_time}.",
"message": message,
"ioc": True,
"sighted": True,
"tlp": 2
"tlp": 1,
"pap": 1
}

if 'tags' in obs:
observable_data['tags'] = obs['tags']

observables_data.append(observable_data)

return observables_data


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


def create_new_alert(ticket_id, title, description, severity, tags, app_name, observables, customFields, comment, thehive_url, api_key):
def create_new_alert(ticket_id, title, description, severity, tlp, pap, tags, app_name, observables, customFields, comment, thehive_url, api_key):
from common.core import generate_ref
"""
Create a new alert in TheHive with the provided details.
Expand All @@ -149,6 +165,8 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
:param title: The title of the alert.
:param description: The description of the alert.
:param severity: The severity level of the alert (integer).
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
:param tags: A list of tags associated with the alert.
:param app_name: The application triggering the alert.
:param observables: A list of observables to associate with the alert.
Expand All @@ -166,6 +184,8 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
"title": title,
"description": description,
"severity": severity,
"tlp": tlp,
"pap": pap,
"tags": tags,
"type": app_name,
"source": "watcher",
Expand Down Expand Up @@ -201,7 +221,7 @@ def create_new_alert(ticket_id, title, description, severity, tags, app_name, ob
return None


def handle_alert_or_case(ticket_id, observables, comment, title, description, severity, tags, app_name, customFields, thehive_url, api_key):
def handle_alert_or_case(ticket_id, observables, comment, title, description, severity, tlp, pap, tags, app_name, customFields, thehive_url, api_key):
"""
Handle the creation or updating of alerts and cases in TheHive.
Expand All @@ -211,6 +231,8 @@ def handle_alert_or_case(ticket_id, observables, comment, title, description, se
:param title: The title for the alert.
:param description: The description for the alert.
:param severity: The severity of the alert (integer).
:param tlp: The Traffic Light Protocol (TLP) level of the alert (integer).
:param pap: The Permissible Action Protocol (PAP) level of the alert (integer).
:param tags: A list of tags for the alert.
:param app_name: The name of the application triggering the alert.
:param customFields: Custom fields to be included in the alert.
Expand All @@ -235,6 +257,6 @@ def handle_alert_or_case(ticket_id, observables, comment, title, description, se
else:
create_new_alert(
ticket_id=ticket_id, title=title, description=description, severity=severity,
tags=tags, app_name=app_name, observables=observables,
tlp=tlp, pap=pap, tags=tags, app_name=app_name, observables=observables,
customFields=customFields, comment=comment, thehive_url=thehive_url, api_key=api_key
)
41 changes: 28 additions & 13 deletions Watcher/Watcher/watcher/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
THE_HIVE_KEY = os.environ.get('THE_HIVE_KEY', '')
THE_HIVE_CUSTOM_FIELD = os.environ.get('THE_HIVE_CUSTOM_FIELD', 'watcher-id')
THE_HIVE_EMAIL_SENDER = os.environ.get('THE_HIVE_EMAIL_SENDER', 'watcher@watcher.com')
THE_HIVE_TAGS = os.environ.get('THE_HIVE_TAGS', "Watcher,Impersonation,Malicious Domain,Typosquatting").split(",")

# MISP Setup
MISP_URL = os.environ.get('MISP_URL', 'https://127.0.0.1')
Expand Down Expand Up @@ -214,20 +215,34 @@
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
# SECURITY WARNING: In production please set DB_USER and DB_PASSWORD environment variables in the .env file.
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'CONN_MAX_AGE': 3600,
# 'NAME': 'db_watcher',
# 'USER': os.environ.get('DB_USER', 'watcher'),
# 'PASSWORD': os.environ.get('DB_PASSWORD', 'Ee5kZm4fWWAmE9hs'),
# 'HOST': 'db_watcher',
# 'PORT': '3306',
# 'OPTIONS': {
# 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
# "charset": "utf8mb4",
# },
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 3600,
'NAME': 'db_watcher',
'USER': os.environ.get('DB_USER', 'watcher'),
'PASSWORD': os.environ.get('DB_PASSWORD', 'Ee5kZm4fWWAmE9hs'),
'HOST': 'db_watcher',
'PORT': '3306',
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
"charset": "utf8mb4",
},
}
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 3600,
'NAME': 'db_watcher3',
'USER': 'watcher',
'PASSWORD': 'Ee5kZm4fWWAmE9hs!',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
},
}
}

# Password validation
Expand Down

0 comments on commit 59b0505

Please sign in to comment.