| 1 | #!/usr/bin/python3 |
| 2 | # Copyright (C) 2015-2022, Wazuh Inc. |
| 3 | # All rights reserved. |
| 4 | |
| 5 | # This program is free software; you can redistribute it |
| 6 | # and/or modify it under the terms of the GNU General Public |
| 7 | # License (version 2) as published by the FSF - Free Software |
| 8 | # Foundation. |
| 9 | |
| 10 | import datetime |
| 11 | import json |
| 12 | import os |
| 13 | import sys |
| 14 | from pathlib import PurePosixPath |
| 15 | from pathlib import PureWindowsPath |
| 16 | |
| 17 | if os.name == "nt": |
| 18 | LOG_FILE = "C:\\Program Files (x86)\\ossec-agent\\active-response\\active-responses.log" |
| 19 | else: |
| 20 | LOG_FILE = "/var/ossec/logs/active-responses.log" |
| 21 | |
| 22 | ADD_COMMAND = 0 |
| 23 | DELETE_COMMAND = 1 |
| 24 | CONTINUE_COMMAND = 2 |
| 25 | ABORT_COMMAND = 3 |
| 26 | |
| 27 | OS_SUCCESS = 0 |
| 28 | OS_INVALID = -1 |
| 29 | |
| 30 | |
| 31 | class message: |
| 32 | def __init__(self): |
| 33 | self.alert = "" |
| 34 | self.command = 0 |
| 35 | |
| 36 | |
| 37 | def write_debug_file(ar_name, msg): |
| 38 | with open(LOG_FILE, mode="a") as log_file: |
| 39 | ar_name_posix = str(PurePosixPath(PureWindowsPath(ar_name[ar_name.find("active-response") :]))) |
| 40 | log_file.write(str(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")) + " " + ar_name_posix + ": " + msg + "\n") |
| 41 | |
| 42 | |
| 43 | def setup_and_check_message(argv): |
| 44 | # get alert from stdin |
| 45 | input_str = "" |
| 46 | for line in sys.stdin: |
| 47 | input_str = line |
| 48 | break |
| 49 | |
| 50 | write_debug_file(argv[0], input_str) |
| 51 | |
| 52 | try: |
| 53 | data = json.loads(input_str) |
| 54 | except ValueError: |
| 55 | write_debug_file(argv[0], "Decoding JSON has failed, invalid input format") |
| 56 | message.command = OS_INVALID |
| 57 | return message |
| 58 | |
| 59 | message.alert = data |
| 60 | |
| 61 | command = data.get("command") |
| 62 | |
| 63 | if command == "add": |
| 64 | message.command = ADD_COMMAND |
| 65 | elif command == "delete": |
| 66 | message.command = DELETE_COMMAND |
| 67 | else: |
| 68 | message.command = OS_INVALID |
| 69 | write_debug_file(argv[0], "Not valid command: " + command) |
| 70 | |
| 71 | return message |
| 72 | |
| 73 | |
| 74 | def send_keys_and_check_message(argv, keys): |
| 75 | # build and send message with keys |
| 76 | keys_msg = json.dumps( |
| 77 | {"version": 1, "origin": {"name": argv[0], "module": "active-response"}, "command": "check_keys", "parameters": {"keys": keys}}, |
| 78 | ) |
| 79 | |
| 80 | write_debug_file(argv[0], keys_msg) |
| 81 | |
| 82 | print(keys_msg) |
| 83 | sys.stdout.flush() |
| 84 | |
| 85 | # read the response of previous message |
| 86 | input_str = "" |
| 87 | while True: |
| 88 | line = sys.stdin.readline() |
| 89 | if line: |
| 90 | input_str = line |
| 91 | break |
| 92 | |
| 93 | write_debug_file(argv[0], input_str) |
| 94 | |
| 95 | try: |
| 96 | data = json.loads(input_str) |
| 97 | except ValueError: |
| 98 | write_debug_file(argv[0], "Decoding JSON has failed, invalid input format") |
| 99 | return message |
| 100 | |
| 101 | action = data.get("command") |
| 102 | |
| 103 | if "continue" == action: |
| 104 | ret = CONTINUE_COMMAND |
| 105 | elif "abort" == action: |
| 106 | ret = ABORT_COMMAND |
| 107 | else: |
| 108 | ret = OS_INVALID |
| 109 | write_debug_file(argv[0], "Invalid value of 'command'") |
| 110 | |
| 111 | return ret |
| 112 | |
| 113 | |
| 114 | def main(argv): |
| 115 | write_debug_file(argv[0], "Started") |
| 116 | |
| 117 | # validate json and get command |
| 118 | msg = setup_and_check_message(argv) |
| 119 | |
| 120 | if msg.command < 0: |
| 121 | sys.exit(OS_INVALID) |
| 122 | |
| 123 | if msg.command == ADD_COMMAND: |
| 124 | """Start Custom Key |
| 125 | At this point, it is necessary to select the keys from the alert and add them into the keys array. |
| 126 | """ |
| 127 | |
| 128 | alert = msg.alert["parameters"]["alert"] |
| 129 | keys = [alert["rule"]["id"]] |
| 130 | |
| 131 | """ End Custom Key """ |
| 132 | |
| 133 | action = send_keys_and_check_message(argv, keys) |
| 134 | |
| 135 | # if necessary, abort execution |
| 136 | if action != CONTINUE_COMMAND: |
| 137 | if action == ABORT_COMMAND: |
| 138 | write_debug_file(argv[0], "Aborted") |
| 139 | sys.exit(OS_SUCCESS) |
| 140 | else: |
| 141 | write_debug_file(argv[0], "Invalid command") |
| 142 | sys.exit(OS_INVALID) |
| 143 | |
| 144 | """ Start Custom Action Add """ |
| 145 | |
| 146 | with open("ar-test-result.txt", mode="a") as test_file: |
| 147 | test_file.write("Active response triggered by rule ID: <" + str(keys) + ">\n") |
| 148 | |
| 149 | """ End Custom Action Add """ |
| 150 | |
| 151 | elif msg.command == DELETE_COMMAND: |
| 152 | """Start Custom Action Delete""" |
| 153 | |
| 154 | os.remove("ar-test-result.txt") |
| 155 | |
| 156 | """ End Custom Action Delete """ |
| 157 | |
| 158 | else: |
| 159 | write_debug_file(argv[0], "Invalid command") |
| 160 | |
| 161 | write_debug_file(argv[0], "Ended") |
| 162 | |
| 163 | sys.exit(OS_SUCCESS) |
| 164 | |
| 165 | |
| 166 | if __name__ == "__main__": |
| 167 | main(sys.argv) |