wazuh-notify/custom-active-response.py

228 lines
6.9 KiB
Python
Raw Normal View History

2024-04-29 19:28:48 +02:00
#!/usr/bin/env python3
# This script is adapted version of the Python active response script sample, provided by Wazuh, in the documentation:
2024-04-30 21:50:03 +02:00
# https://documentation.wazuh.com/current/user-manual/capabilities/active-response/custom-active-response-scripts.html
2024-04-29 19:28:48 +02:00
# It is provided under the below copyright statement:
#
# Copyright (C) 2015-2022, Wazuh Inc.
# All rights reserved.
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License (version 2) as published by the FSF - Free Software
# Foundation.
#
# This version has changes in
# 1) the first lines of code with the assignments, and
# 2) the Start Custom Action Add section
2024-04-30 21:50:03 +02:00
# This adapted version is free software. Rudi Klein, april 2024
2024-04-28 20:27:23 +02:00
2024-04-30 21:50:03 +02:00
import datetime
import json
2024-04-28 20:27:23 +02:00
import os
import sys
from pathlib import PureWindowsPath, PurePosixPath
2024-04-30 21:50:03 +02:00
2024-04-29 19:28:48 +02:00
from wazuh_notifier_lib import import_config as ic
2024-04-30 21:50:03 +02:00
from wazuh_notifier_lib import set_env as se
2024-04-28 20:27:23 +02:00
2024-04-29 19:28:48 +02:00
wazuh_path, ar_path, config_path = se()
2024-04-28 20:27:23 +02:00
ADD_COMMAND = 0
DELETE_COMMAND = 1
CONTINUE_COMMAND = 2
ABORT_COMMAND = 3
OS_SUCCESS = 0
OS_INVALID = -1
2024-04-30 21:50:03 +02:00
class Message:
2024-04-28 20:27:23 +02:00
def __init__(self):
self.alert = ""
self.command = 0
def write_debug_file(ar_name, msg):
2024-04-29 19:28:48 +02:00
with open(ar_path, mode="a") as log_file:
2024-04-28 20:27:23 +02:00
ar_name_posix = str(PurePosixPath(PureWindowsPath(ar_name[ar_name.find("active-response"):])))
2024-04-30 21:50:03 +02:00
log_file.write(
str(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + " " + ar_name_posix + ": " + msg + "\n")
2024-04-28 20:27:23 +02:00
def setup_and_check_message(argv):
# get alert from stdin
input_str = ""
for line in sys.stdin:
input_str = line
break
write_debug_file(argv[0], input_str)
try:
data = json.loads(input_str)
except ValueError:
write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format')
2024-04-30 21:50:03 +02:00
Message.command = OS_INVALID
return Message
2024-04-28 20:27:23 +02:00
2024-04-30 21:50:03 +02:00
Message.alert = data
2024-04-28 20:27:23 +02:00
command = data.get("command")
if command == "add":
2024-04-30 21:50:03 +02:00
Message.command = ADD_COMMAND
2024-04-28 20:27:23 +02:00
elif command == "delete":
2024-04-30 21:50:03 +02:00
Message.command = DELETE_COMMAND
2024-04-28 20:27:23 +02:00
else:
2024-04-30 21:50:03 +02:00
Message.command = OS_INVALID
2024-04-28 20:27:23 +02:00
write_debug_file(argv[0], 'Not valid command: ' + command)
2024-04-30 21:50:03 +02:00
return Message
2024-04-28 20:27:23 +02:00
def send_keys_and_check_message(argv, keys):
# build and send message with keys
2024-04-30 21:50:03 +02:00
keys_msg = json.dumps(
{"version": 1, "origin": {"name": argv[0], "module": "active-response"}, "command": "check_keys",
"parameters": {"keys": keys}})
2024-04-28 20:27:23 +02:00
write_debug_file(argv[0], keys_msg)
print(keys_msg)
sys.stdout.flush()
# read the response of previous message
input_str = ""
while True:
line = sys.stdin.readline()
if line:
input_str = line
break
write_debug_file(argv[0], input_str)
try:
data = json.loads(input_str)
except ValueError:
write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format')
2024-04-30 21:50:03 +02:00
return Message
2024-04-28 20:27:23 +02:00
action = data.get("command")
if "continue" == action:
ret = CONTINUE_COMMAND
elif "abort" == action:
ret = ABORT_COMMAND
else:
ret = OS_INVALID
write_debug_file(argv[0], "Invalid value of 'command'")
return ret
2024-04-30 21:50:03 +02:00
def parameters_deconstruct(event_keys):
a_id: str = str(event_keys["agent"]["id"])
a_name: str = str(event_keys["agent"]["name"])
e_level: str = str(event_keys["rule"]["level"])
e_description: str = str(event_keys["rule"]["description"])
e_id: str = str(event_keys["rule"]["id"])
e_fired_times: str = str(event_keys["rule"]["firedtimes"])
2024-04-28 20:27:23 +02:00
2024-04-30 21:50:03 +02:00
return a_id, a_name, e_id, e_description, e_level, e_fired_times
def construct_message(caller: str, a_id: str, a_name: str, e_id: str, e_description: str, e_level: str,
e_fired_times: str):
discord_accent = ""
if caller == "discord":
discord_accent = "**"
message_params: str = ("--message " + '"' +
discord_accent + "Agent: " + discord_accent + a_name + " (" + a_id + ")" + "\n" +
discord_accent + "Event id: " + discord_accent + e_id + "\n" +
discord_accent + "Description: " + discord_accent + e_description + "\n" +
discord_accent + "Threat level: " + discord_accent + e_level + "\n" +
discord_accent + "Times fired: " + discord_accent + e_fired_times + "\n" + '"')
return message_params
def main(argv):
2024-04-28 20:27:23 +02:00
write_debug_file(argv[0], "Started")
# validate json and get command
msg = setup_and_check_message(argv)
if msg.command < 0:
sys.exit(OS_INVALID)
if msg.command == ADD_COMMAND:
""" Start Custom Key
At this point, it is necessary to select the keys from the alert and add them into the keys array.
"""
alert = msg.alert["parameters"]["alert"]
2024-04-30 21:50:03 +02:00
keys = [alert["rule"]]
2024-04-28 20:27:23 +02:00
2024-04-30 21:50:03 +02:00
agent_id, agent_name, event_level, event_description, event_id, event_fired_times = parameters_deconstruct(
alert)
2024-04-28 20:27:23 +02:00
action = send_keys_and_check_message(argv, keys)
# if necessary, abort execution
if action != CONTINUE_COMMAND:
if action == ABORT_COMMAND:
write_debug_file(argv[0], "Aborted")
sys.exit(OS_SUCCESS)
else:
write_debug_file(argv[0], "Invalid command")
sys.exit(OS_INVALID)
""" Start Custom Action Add """
2024-04-30 21:50:03 +02:00
if str(ic("discord_enabled")) == "1":
caller = "discord"
2024-04-29 19:28:48 +02:00
discord_notifier = '{0}/active-response/bin/wazuh-discord-notifier.py'.format(wazuh_path)
discord_exec = "python3 " + discord_notifier + " "
write_debug_file(argv[0], "Start Discord notifier")
2024-04-30 21:50:03 +02:00
discord_params = construct_message(caller, agent_id, agent_name, event_level, event_description, event_id,
event_fired_times)
2024-04-29 19:28:48 +02:00
discord_command = discord_exec + discord_params
os.system(discord_command)
2024-04-30 21:50:03 +02:00
if str(ic("ntfy_enabled")) == "1":
caller = "ntfy"
2024-04-29 19:28:48 +02:00
ntfy_notifier = '{0}/active-response/bin/wazuh-ntfy-notifier.py'.format(wazuh_path)
ntfy_exec = "python3 " + ntfy_notifier + " "
write_debug_file(argv[0], "Start NTFY notifier")
2024-04-30 21:50:03 +02:00
ntfy_params = construct_message(caller, agent_id, agent_name, event_level, event_description, event_id,
event_fired_times)
2024-04-29 19:28:48 +02:00
ntfier_command = ntfy_exec + ntfy_params
os.system(ntfier_command)
2024-04-28 20:27:23 +02:00
""" End Custom Action Add """
elif msg.command == DELETE_COMMAND:
""" Start Custom Action Delete """
2024-04-30 21:50:03 +02:00
pass
2024-04-28 20:27:23 +02:00
""" End Custom Action Delete """
else:
write_debug_file(argv[0], "Invalid command")
write_debug_file(argv[0], "Ended")
sys.exit(OS_SUCCESS)
if __name__ == "__main__":
2024-04-30 21:50:03 +02:00
main(sys.argv)