Wazuh exclusion (#212)
* Refactor wazuh_rule_exclude endpoint to accept RuleExcludeRequest * modify png file * chore: Update branch name in Docker workflow from network-connectors to main * updated wazuhManagerRuleExclude api FE * chore: Update Docker workflow to include wazuh-exclusion branch * chore: Update Docker workflow to use 'main' branch instead of 'wazuh-exclusion' * chore: Update Docker workflow to use 'main' branch instead of 'wazuh-exclusion' * crowdstrike * chore: Update Docker workflow to use 'main' branch instead of 'wazuh-exclusion' * chore: Update customer integrations table for customer {customer_code} * precommit fixes --------- Co-authored-by: Davide Di Modica <webmaster.ddm@gmail.com>
taylor_socfortress committed
May 13, 2024 at 13:07 UTC
75faafa310a8dcb650c89e759e786fcabadb3ec0
7 files changed
+113
-101
backend/app/connectors/wazuh_manager/routes/rules.py
+4
-4
@@ -16,12 +16,14 @@ from app.connectors.wazuh_manager.schema.rules import RuleDisable
16
from app.connectors.wazuh_manager.schema.rules import RuleDisableResponse
17
from app.connectors.wazuh_manager.schema.rules import RuleEnable
18
from app.connectors.wazuh_manager.schema.rules import RuleEnableResponse
19
+from app.connectors.wazuh_manager.schema.rules import RuleExcludeRequest
20
from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
21
22
# from app.connectors.wazuh_manager.schema.rules import RuleExclude
23
# from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
24
from app.connectors.wazuh_manager.services.rules import disable_rule
25
from app.connectors.wazuh_manager.services.rules import enable_rule
26
+from app.connectors.wazuh_manager.services.rules import post_to_copilot_ai_module
27
28
# from app.connectors.wazuh_manager.services.rules import exclude_rule
29
from app.db.db_session import get_db
@@ -155,12 +157,10 @@ async def enable_wazuh_rule(
157
raise HTTPException(status_code=404, detail="Was not able to enable rule")
158
159
158
-# ! TODO: Implement this endpoint - Maybe use OpenAI?
160
@wazuh_manager_rules_router.post(
161
"/rule/exclude",
162
response_model=RuleExcludeResponse,
163
description="Retrieve recommended exclusion for a Wazuh Rule",
164
)
164
-async def exclude_wazuh_rule() -> RuleExcludeResponse:
165
- raise HTTPException(status_code=501, detail="Feature not yet ready")
166
- return RuleExcludeResponse(success=False, message="Feature not yet ready")
165
+async def exclude_wazuh_rule(request: RuleExcludeRequest) -> RuleExcludeResponse:
166
+ return await post_to_copilot_ai_module(data=request)
backend/app/connectors/wazuh_manager/schema/rules.py
+85
-6
@@ -1,8 +1,10 @@
1
from typing import List
2
from typing import Optional
3
4
+from fastapi import HTTPException
5
from pydantic import BaseModel
6
from pydantic import Field
7
+from pydantic import validator
8
9
10
class RuleDisable(BaseModel):
@@ -43,12 +45,89 @@ class AllDisabledRuleResponse(BaseModel):
45
message: str
46
47
46
-class RuleExclude(BaseModel):
47
- input_value: str = Field(
48
- ...,
49
- description="The proposed value of the field trying to be exclude that would result in an exclusiong",
50
- example="C:\\Windows\\ServiceState\\EventLog\\Data\\lastalive1.dat",
51
- )
48
+payload = {
49
+ "data_win_system_eventRecordID": "521098",
50
+ "data_win_eventdata_user": "WIN-HFOU106TD7K\\Administrator",
51
+ "agent_id": "111",
52
+ "agent_name": "WIN-HFO106TD7K",
53
+ "gl2_remote_ip": "10.255.255.13",
54
+ "data_win_system_eventID": "22",
55
+ "agent_labels_customer": "00002",
56
+ "source": "10.255.255.13",
57
+ "gl2_source_input": "660320f176ca320e8393f030",
58
+ "rule_level": 3,
59
+ "data_win_system_task": "22",
60
+ "timestamp_utc": "2024-04-17T15:06:54.742Z",
61
+ "syslog_type": "wazuh",
62
+ "data_win_system_threadID": "2888",
63
+ "rule_description": "Sysmon - Event 22: DNS Request by C:\\Windows\\system32\\PING.EXE",
64
+ "gl2_source_node": "3b68efa4-3319-4885-a38f-c944f0fcf191",
65
+ "id": "1713366415.56188571",
66
+ "rule_mitre_tactic": "Command and Control",
67
+ "process_image": "C:\\Windows\\system32\\PING.EXE",
68
+ "data_win_eventdata_utcTime": "2024-04-17 15:06:28.457",
69
+ "streams": ["661555f676ca320e837b14cc", "660320f176ca320e8393f057"],
70
+ "rule_mitre_id": "T1071",
71
+ "gl2_message_id": "01HVP9HG8YE31EQH1878V50H89",
72
+ "data_win_system_computer": "WIN-HFOU106TD7K",
73
+ "agent_ip": "192.168.200.3",
74
+ "data_win_eventdata_image": "C:\\Windows\\system32\\PING.EXE",
75
+ "threat_intel_value": "evil.socfortress.co",
76
+ "data_win_eventdata_queryName": "evil.socfortress.co",
77
+ "rule_groups": "windows, sysmon, sysmon_event_22",
78
+ "data_win_system_keywords": "0x8000000000000000",
79
+ "data_win_system_level": "4",
80
+ "process_id": "6072",
81
+ "data_win_eventdata_queryStatus": "0",
82
+ "data_win_system_severityValue": "INFORMATION",
83
+ "dns_response_code": "0",
84
+ "dns_query": "evil.socfortress.co",
85
+ "data_win_eventdata_processGuid": "{691ff406-e58c-661f-b401-000000002300}",
86
+ "rule_mitre_technique": "Application Layer Protocol",
87
+ "rule_firedtimes": 2,
88
+ "data_win_system_systemTime": "2024-04-17T15:06:54.742696000Z",
89
+ "decoder_name": "windows_eventchannel",
90
+ "data_win_system_processID": "2180",
91
+ "data_win_system_channel": "Microsoft-Windows-Sysmon/Operational",
92
+ "syslog_level": "ALERT",
93
+ "threat_intel_comment": "This is a test IoC",
94
+ "data_win_system_providerName": "Microsoft-Windows-Sysmon",
95
+ "data_win_eventdata_processId": "6072",
96
+ "data_win_system_version": "5",
97
+ "data_win_system_providerGuid": "{5770385f-c22a-43e0-bf4c-06f5698ffbd9}",
98
+ "timestamp": "2024-04-17 15:06:57.694",
99
+ "threat_intel_ioc_source": "test",
100
+ "rule_group1": "windows",
101
+ "data_win_system_opcode": "0",
102
+}
103
+
104
+
105
+class RuleExcludeRequest(BaseModel):
106
+ integration: str = Field(..., example="wazuh-rule-exclusion")
107
+ prompt: dict = Field(..., example=payload)
108
+
109
+ @validator("integration")
110
+ def check_integration(cls, v):
111
+ if v != "wazuh-rule-exclusion":
112
+ raise HTTPException(
113
+ status_code=400,
114
+ detail="Invalid integration. Only 'wazuh-rule-exclusion' is supported.",
115
+ )
116
+ return v
117
+
118
+ @validator("prompt")
119
+ def check_rule_group1(cls, v):
120
+ if "rule_group1" not in v:
121
+ raise HTTPException(
122
+ status_code=400,
123
+ detail="Missing 'rule_group1' in prompt.",
124
+ )
125
+ if v["rule_group1"] != "windows":
126
+ raise HTTPException(
127
+ status_code=400,
128
+ detail="Invalid 'rule_group1'. Only 'windows' is supported.",
129
+ )
130
+ return v
131
132
133
class RuleExcludeResponse(BaseModel):
backend/app/connectors/wazuh_manager/services/rules.py
+15
-88
@@ -1,11 +1,11 @@
1
-import re
2
-from enum import Enum
1
from typing import Any
2
from typing import Dict
3
from typing import List
4
from typing import Tuple
5
from typing import Union
6
7
+import httpx
8
+
9
# import pcre2
10
import xmltodict
11
from fastapi import HTTPException
@@ -15,7 +15,7 @@ from app.connectors.wazuh_manager.schema.rules import RuleDisable
15
from app.connectors.wazuh_manager.schema.rules import RuleDisableResponse
16
from app.connectors.wazuh_manager.schema.rules import RuleEnable
17
from app.connectors.wazuh_manager.schema.rules import RuleEnableResponse
18
-from app.connectors.wazuh_manager.schema.rules import RuleExclude
18
+from app.connectors.wazuh_manager.schema.rules import RuleExcludeRequest
19
from app.connectors.wazuh_manager.schema.rules import RuleExcludeResponse
20
from app.connectors.wazuh_manager.utils.universal import restart_service
21
from app.connectors.wazuh_manager.utils.universal import send_get_request
@@ -243,92 +243,19 @@ async def enable_rule(rule: RuleEnable, previous_level: str) -> RuleEnableRespon
243
244
245
################# ! EXCLUDE RULE ! #################
246
-
247
-
248
-def make_pcre2_compatible(input_string: str) -> str:
246
+async def post_to_copilot_ai_module(data: RuleExcludeRequest) -> RuleExcludeResponse:
247
"""
250
- Convert the input string to a PCRE2 compatible regex pattern.
251
-
252
- Parameters:
253
- - input_string (str): The input string to convert.
254
-
255
- Returns:
256
- - str: The PCRE2 compatible regex pattern.
257
- """
258
- # PCRE2 uses \\ to escape a backslash
259
- return input_string.replace("\\", "\\\\")
260
-
261
-
262
-class RegexSpecialCharacters(Enum):
263
- DOT = (".", "\.")
264
- CARET = ("^", "\^")
265
- DOLLAR = ("$", "\$")
266
- STAR = ("*", "\*")
267
- PLUS = ("+", "\+")
268
- QUESTION = ("?", "\?")
269
- CURLY_OPEN = ("{", "\{")
270
- CURLY_CLOSE = ("}", "\}")
271
- SQUARE_OPEN = ("[", "\[")
272
- SQUARE_CLOSE = ("]", "\]")
273
- SINGLE_BACKSLASH = ("\\", "\\\\")
274
- DOUBLE_BACKSLASH = ("\\\\", "\\\\\\\\")
275
- PIPE = ("|", "\|")
276
- PAREN_OPEN = ("(", "\(")
277
- PAREN_CLOSE = (")", "\)")
278
- COLON = (":", "\:")
279
- DASH = ("-", "\-")
280
-
281
-
282
-# Create a dictionary for easy lookup
283
-REGEX_REPLACE_DICT = {char.value[0]: char.value[1] for char in RegexSpecialCharacters}
284
-
285
-
286
-async def replace_special_chars(rule: RuleExclude):
287
- for char, replacement in REGEX_REPLACE_DICT.items():
288
- # Use Python's raw string notation for regular expressions
289
- pattern = re.compile(re.escape(char))
290
- input_string = pattern.sub(replacement, rule.input_value)
291
- logger.info(f"Input String: {input_string}")
292
- return input_string
293
-
294
-
295
-async def exclude_rule(rule: RuleExclude) -> RuleExcludeResponse:
296
- """
297
- Exclude a rule based on the provided input value and rule value.
248
+ Send a POST request to the copilot-ai-module Docker container.
249
250
Args:
300
- rule (RuleExclude): The rule to be excluded, containing the input value and rule value.
301
-
302
- Returns:
303
- RuleExcludeResponse: The response indicating the success or failure of excluding the rule, along with a recommended exclusion.
304
-
305
- Raises:
306
- Exception: If an error occurs while excluding the rule.
251
+ data (CollectHuntress): The data to send to the copilot-ai-module Docker container.
252
"""
308
- # Function to replace special characters
309
- # repr of the input string
310
- input_string = repr(rule.input_value)
311
- logger.info(f"Input String: {input_string}")
312
- excluded_string = await replace_special_chars(rule)
313
- logger.info(f"Excluded String: {excluded_string}")
314
- return None
315
- # try:
316
- # # Convert rule_value to a PCRE2 compatible regex pattern
317
- # pcre2_pattern = make_pcre2_compatible(rule.rule_value)
318
-
319
- # compiled_pattern = pcre2.compile(pcre2_pattern)
320
- # print(f"Compiled Pattern: {compiled_pattern}") # Debugging line
321
-
322
- # print(f"Input Value: {rule.input_value}") # Debugging line
323
-
324
- # match_data = compiled_pattern.match(rule.input_value)
325
-
326
- # if match_data:
327
- # return RuleExcludeResponse(success=True, message="Successfully excluded rule", recommended_exclusion=rule.input_value)
328
- # else:
329
- # return RuleExcludeResponse(success=False, message="Failed to exclude rule", recommended_exclusion="")
330
-
331
- # except Exception as e:
332
- # print(f"Exception: {e}") # Debugging line
333
- # logger.error(f"Failed to exclude rule: {e}")
334
- # return RuleExcludeResponse(success=False, message=f"Failed to exclude rule: {e}", recommended_exclusion="")
253
+ logger.info(f"Sending POST request to http://copilot-ai-module/wazuh-rule-exclusion with data: {data.dict()}")
254
+ raise HTTPException(status_code=501, detail="Not Implemented Yet")
255
+ async with httpx.AsyncClient() as client:
256
+ data = await client.post(
257
+ "http://127.0.0.1:5001/wazuh-rule-exclusion",
258
+ json=data.dict(),
259
+ timeout=120,
260
+ )
261
+ return RuleExcludeResponse(**data.json())
backend/app/integrations/crowdstrike/routes/provision.py
+2
@@ -17,6 +17,7 @@ from app.integrations.routes import find_customer_integration
17
from app.integrations.routes import get_customer_integrations_by_customer_code
18
from app.integrations.schema import CustomerIntegrations
19
from app.integrations.schema import CustomerIntegrationsResponse
20
+from app.middleware.license import is_feature_enabled
21
22
integration_crowdstrike_router = APIRouter()
23
@@ -98,6 +99,7 @@ async def provision_crowdstrike_route(
99
Returns:
100
ProvisionCrowdstrikeResponse: The response object containing the result of the provisioning.
101
"""
102
+ await is_feature_enabled(feature_name="CROWDSTRIKE", session=session)
103
customer_integration_response = await get_customer_integration_response(
104
provision_crowdstrike_request.customer_code,
105
session,
backend/app/integrations/crowdstrike/services/provision.py
+2
-1
@@ -37,7 +37,7 @@ from app.customers.routes.customers import get_customer_meta
37
from app.integrations.crowdstrike.schema.provision import CrowdstrikeCustomerDetails
38
from app.integrations.crowdstrike.schema.provision import ProvisionCrowdstrikeAuthKeys
39
from app.integrations.crowdstrike.schema.provision import ProvisionCrowdstrikeResponse
40
-from app.integrations.schema import CustomerIntegrations
40
+from app.integrations.models.customer_integration_settings import CustomerIntegrations
41
from app.network_connectors.models.network_connectors import (
42
CustomerNetworkConnectorsMeta,
43
)
@@ -535,6 +535,7 @@ async def update_customer_integration_table(
535
customer_code (str): The customer code.
536
session (AsyncSession): The async session object for making HTTP requests.
537
"""
538
+ logger.info(f"Updating customer integrations table for customer {customer_code}")
539
await session.execute(
540
update(CustomerIntegrations)
541
.where(
backend/app/integrations/markdown/crowdstrike.md
+1
-1
@@ -104,4 +104,4 @@ docker compose -f /opt/CoPilot/data/data/CUSTOMER_NAME/CUSTOMER_NAME_docker-comp
104
```
105
106
You should now see the container running:
107
-
107
+
frontend/src/api/alerts.ts
+4
-1
@@ -102,6 +102,9 @@ export default {
102
)
103
},
104
wazuhManagerRuleExclude(source: AlertSourceContent) {
105
- return HttpClient.post<FlaskBaseResponse & WazuhRuleExclude>(`/wazuh_manager/rule/exclude`, source)
105
+ return HttpClient.post<FlaskBaseResponse & WazuhRuleExclude>(`/wazuh_manager/rule/exclude`, {
106
+ integration: "wazuh-rule-exclusion",
107
+ prompt: source
108
+ })
109
}
110
}