main
py 140 lines 4.28 KB
Raw
1 #!/usr/bin/python3
2 # Copyright (C) 2025, SOCFortress LLC.
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 ipaddress
12 import json
13 import os
14 import subprocess
15 import sys
16
17 LOG_FILE = (
18 "C:\\Program Files (x86)\\ossec-agent\\active-response\\active-responses.log"
19 if os.name == "nt"
20 else "/var/ossec/logs/active-responses.log"
21 )
22
23 COMMANDS = {"add": 0, "delete": 1, "continue": 2, "abort": 3}
24
25 OS_SUCCESS = 0
26 OS_INVALID = -1
27
28
29 class Message:
30 def __init__(self, alert="", command=0):
31 self.alert = alert
32 self.command = command
33
34
35 def write_debug_file(ar_name, msg):
36 """Writes a debug message to the log file."""
37 with open(LOG_FILE, mode="a") as log_file:
38 log_msg = {
39 "timestamp": datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
40 "active_response": "windows_firewall",
41 "message": json.loads(msg) if isinstance(msg, str) and msg.strip().startswith("{") else msg,
42 }
43 log_file.write(json.dumps(log_msg) + "\n")
44
45
46 def setup_and_check_message(argv):
47 """Reads and validates the input message."""
48 input_str = next(sys.stdin, "")
49 write_debug_file(argv[0], input_str)
50 try:
51 data = json.loads(input_str)
52 except ValueError:
53 write_debug_file(argv[0], "Decoding JSON has failed, invalid input format")
54 return Message(command=OS_INVALID)
55 command = COMMANDS.get(data.get("command"), OS_INVALID)
56 if command == OS_INVALID:
57 write_debug_file(argv[0], "Not valid command: " + data.get("command"))
58 return Message(alert=data, command=command)
59
60
61 def is_valid_ipv4(ip):
62 """Checks if an IP address is valid and not private."""
63 try:
64 ip_obj = ipaddress.IPv4Address(ip)
65 return ip_obj.is_global
66 except ipaddress.AddressValueError:
67 return False
68
69
70 def block_ip(ip):
71 """Blocks an IP address on the Windows Firewall."""
72 try:
73 subprocess.run(
74 [
75 r"C:\Windows\System32\netsh",
76 "advfirewall",
77 "firewall",
78 "add",
79 "rule",
80 f"name=SOCFortress Block Outbound {ip}",
81 "dir=out",
82 "action=block",
83 f"remoteip={ip}",
84 ],
85 check=True,
86 )
87 return f"Blocked IP {ip} on Windows Firewall"
88 except subprocess.CalledProcessError as e:
89 return f"Failed to block IP {ip} on Windows Firewall: {e}"
90
91
92 def remove_ip(ip):
93 """Removes a blocked IP address from the Windows Firewall."""
94 try:
95 subprocess.run(
96 [r"C:\Windows\System32\netsh", "advfirewall", "firewall", "delete", "rule", f"name=SOCFortress Block Outbound {ip}"],
97 check=True,
98 )
99 return f"Removed blocked IP {ip} from Windows Firewall"
100 except subprocess.CalledProcessError as e:
101 return f"Failed to remove blocked IP {ip} from Windows Firewall: {e}"
102
103
104 def extract_alert_info(msg, argv):
105 """Extracts the action and IP from the alert message."""
106 try:
107 alert = msg.alert["parameters"]["alert"]
108 action = alert["action"]
109 ip = alert["ip"]
110 except KeyError as e:
111 write_debug_file(argv[0], f"Missing key in alert message: {str(e)}")
112 sys.exit(OS_INVALID)
113 return action, ip
114
115
116 def main(argv):
117 write_debug_file(argv[0], {"status": "Started"})
118 msg = setup_and_check_message(argv)
119 if msg.command < 0:
120 sys.exit(OS_INVALID)
121 if msg.command == COMMANDS["add"]:
122 action, ip = extract_alert_info(msg, argv)
123 if not is_valid_ipv4(ip):
124 write_debug_file(argv[0], {"status": "failed", "message": f"Invalid IP address {ip}"})
125 sys.exit(OS_INVALID)
126 if action == "block":
127 write_debug_file(argv[0], block_ip(ip))
128 if action == "unblock":
129 write_debug_file(argv[0], remove_ip(ip))
130 elif msg.command == COMMANDS["delete"]:
131 # Optionally, include logic here to remove the firewall rule if necessary
132 pass
133 else:
134 write_debug_file(argv[0], "Invalid command")
135 write_debug_file(argv[0], "Ended")
136 sys.exit(OS_SUCCESS)
137
138
139 if __name__ == "__main__":
140 main(sys.argv)